Skip to content

Instantly share code, notes, and snippets.

@43x2
Last active January 18, 2016 03:36
Show Gist options
  • Save 43x2/27cac4d6281d0843ebb2 to your computer and use it in GitHub Desktop.
Save 43x2/27cac4d6281d0843ebb2 to your computer and use it in GitHub Desktop.
非同期生成リソースを保持するコンテナ
// 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