Instantly share code, notes, and snippets.
Last active
January 18, 2016 03:36
-
Star
(0)
0
You must be signed in to star a gist -
Fork
(0)
0
You must be signed in to fork a gist
-
Save 43x2/27cac4d6281d0843ebb2 to your computer and use it in GitHub Desktop.
非同期生成リソースを保持するコンテナ
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SharedResourceContainer.h is placed in PUBLIC DOMAIN. | |
// | |
#if !defined( SHARED_RESOURCE_CONTAINER_H ) | |
#define SHARED_RESOURCE_CONTAINER_H | |
#pragma once | |
#include <unordered_map> | |
#include <mutex> | |
#include <future> | |
#include <stdexcept> | |
#include <utility> | |
//! 共有リソースのコンテナ | |
/*! | |
* キーに一意のリソースを非同期で生成するタスクを管理します。 | |
* shared_future で管理しますので、都度 request() しても、request() で得られた shared_future をコピーしても同じです。 | |
* Key は std::unordered_map のキーとして有効なものである (std::hash< Key >, std::equal_to< Key > が定義されている) 必要があります。 | |
* また、Resource には request() から渡す引数 ( key, args... ) を受けるコンストラクタのほか、 | |
* | |
* - 引数なしコンストラクタ (DefaultConstructible) | |
* - コピーコンストラクタ (CopyConstructible, ただし MoveConstructible である必要はない) | |
* - デストラクタ (Destructible) | |
* - ムーブ代入演算子 (MoveAssignable) | |
* | |
* が定義されている必要があります。 | |
*/ | |
template< class Key, class Resource > | |
class SharedResourceContainer | |
{ | |
// | |
// メンバ関数 | |
// | |
public: | |
//! コンストラクタ | |
SharedResourceContainer() noexcept; | |
//! ムーブコンストラクタ | |
SharedResourceContainer( SharedResourceContainer && source ); | |
//! デストラクタ | |
~SharedResourceContainer() = default; | |
//! ムーブ代入演算子 | |
SharedResourceContainer & operator=( SharedResourceContainer && source ); | |
private: | |
//! コピーコンストラクタ (禁止) | |
SharedResourceContainer( const SharedResourceContainer & ) = delete; | |
//! 代入演算子 (禁止) | |
SharedResourceContainer & operator=( const SharedResourceContainer & ) = delete; | |
public: | |
//! リソースの非同期生成をリクエストする | |
template< class ...Args > | |
std::shared_future< Resource > request( const Key & key, Args... args ); | |
private: | |
//! すべてのリソース生成タスクを移動する (ムーブセマンティクス用) | |
void move_all_tasks( SharedResourceContainer && source ); | |
// | |
// メンバ変数 | |
// | |
private: | |
//! 排他制御用ミューテックス | |
std::mutex mutex_; | |
//! リソース生成タスクコンテナ | |
std::unordered_map< Key, std::shared_future< Resource > > tasks_; | |
//! 有効なオブジェクトであるか | |
bool is_valid_; | |
}; | |
// コンストラクタ | |
template< class Key, class Resource > | |
SharedResourceContainer< Key, Resource >::SharedResourceContainer() noexcept | |
: mutex_() | |
, tasks_() | |
, is_valid_( true ) | |
{ | |
// | |
} | |
// ムーブコンストラクタ | |
template< class Key, class Resource > | |
SharedResourceContainer< Key, Resource >::SharedResourceContainer( SharedResourceContainer && source ) | |
: mutex_() | |
, tasks_() | |
, is_valid_( true ) | |
{ | |
move_all_tasks( std::move( source ) ); | |
} | |
// ムーブ代入演算子 | |
template< class Key, class Resource > | |
SharedResourceContainer< Key, Resource > & SharedResourceContainer< Key, Resource >::operator=( SharedResourceContainer && source ) | |
{ | |
if ( &source != this ) // 自己参照対策 | |
{ | |
move_all_tasks( std::move( source ) ); | |
} | |
return *this; | |
} | |
// リソースの非同期生成をリクエストする | |
template< class Key, class Resource > | |
template< class ...Args > | |
std::shared_future< Resource > SharedResourceContainer< Key, Resource >::request( const Key & key, Args ...args ) | |
{ | |
std::lock_guard< std::mutex > guardian( mutex_ ); | |
if ( !is_valid_ ) | |
{ | |
throw std::runtime_error( "Requesting to the invalid SharedResourceContainer object." ); | |
} | |
// 既にリソースを作るタスクがあれば... | |
auto it = tasks_.find( key ); | |
if ( it != tasks_.end() ) | |
{ | |
// ...それを (コピーして) 返す | |
return it->second; | |
} | |
// ない場合はここで作る | |
auto task = std::async( std::launch::async, []( const Key key, Args... args ) { | |
return Resource( key, args... ); | |
}, key, args... ).share(); | |
// タスクを内部で保持 | |
auto result = tasks_.emplace( key, std::move( task ) ); | |
if ( !result.second ) | |
{ | |
throw std::runtime_error( "Failed to the unordered_map insertion." ); | |
} | |
return result.first->second; | |
} | |
// すべてのリソース生成タスクを移動する (ムーブセマンティクス用) | |
template< class Key, class Resource > | |
void SharedResourceContainer< Key, Resource >::move_all_tasks( SharedResourceContainer && source ) | |
{ | |
std::lock( mutex_, source.mutex_ ); | |
std::lock_guard< std::mutex > guardian_self( mutex_, std::adopt_lock ); | |
std::lock_guard< std::mutex > guardian_source( source.mutex_, std::adopt_lock ); | |
if ( !source.is_valid_ ) | |
{ | |
throw std::runtime_error( "Moving tasks from the invalid SharedResourceContainer object." ); | |
} | |
tasks_ = std::move( source.tasks_ ); | |
source.is_valid_ = false; | |
is_valid_ = true; | |
} | |
#endif // !defined( SHARED_RESOURCE_CONTAINER_H ) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment