OSDN Git Service

intrusive_ptr_hookに最低限のマルチスレッド対応を実現
authorSubaruG <subaru_g@users.sourceforge.jp>
Thu, 8 Apr 2010 15:17:37 +0000 (00:17 +0900)
committerSubaruG <subaru_g@users.sourceforge.jp>
Thu, 8 Apr 2010 15:17:37 +0000 (00:17 +0900)
gintenlib/intrusive_ptr_hook.hpp

index 2b70227..4def617 100644 (file)
   仕様:
     ・ADL
         intrusive_ptr_add_ref, intrusive_ptr_release, intrusive_ptr_use_count は
-        ADLによってのみ呼び出されます。gintenlib::intrusive_ptr_xxx とは呼べません。
-        intrusive_ptr_hook を継承することが原因で、継承先のクラスのADL対象に
+        ADLによってのみ呼び出されます。明示的に名前空間修飾することは出来ません。
+        また、 intrusive_ptr_hook を継承することが原因で、継承先のクラスのADL対象に
         gintenlib 名前空間が加わることはありません。
     ・例外仕様
         Deleter のコピーコンストラクタにおいて例外が投げられない限り、例外安全です。
     ・マルチスレッド対応
-        カウンタによって対処します(現在検討中)。
+        参照カウントによって対処します。
+        ・increment:インクリメント操作
+        ・decrement_and_compare_to_zero:デクリメントしてゼロ比較する操作
+        この二つの操作がスレッドセーフならば、全体としてスレッドセーフに動作します。
     ・コピー関連の動作
         コピー、代入操作は、一貫して「何も行わない」方針を採っています。
         コピー初期化される場合は、カウンタ、削除子、共にデフォルト値で初期化されます。
@@ -82,6 +85,22 @@ namespace gintenlib
 {
  namespace intrusive_ptr_hook_  // ADL 回避用
  {
+  // インクリメント/デクリメント処理(これがアトミックならスレッドセーフ)
+  // デフォルトの処理(ADLで書き換えられる。通常は書き換える必要はない)
+  template<typename T>
+  void increment( T& x )
+  {
+    // これは T が atomic<U> ならアトミック
+    ++x;
+  }
+  template<typename T>
+  bool decrement_and_compare_to_zero( T& x )
+  {
+    // これが問題。ロックするなりして対処する必要がある
+    return --x == 0;
+  }
+
+  // 本体
   template<typename Derived, typename Deleter = deleter, typename Counter = int>
   class intrusive_ptr_hook
   {
@@ -150,8 +169,8 @@ namespace gintenlib
       
       counter_type& count = get_count(*p);
       assert( count >= 0 );
-      
-      ++count;
+
+      increment( count );
       
     }
     friend void intrusive_ptr_release( Derived const* p )
@@ -162,9 +181,7 @@ namespace gintenlib
       counter_type& count = get_count(*p);
       assert( count > 0 );
       
-      --count;
-      
-      if( count == 0 )
+      if( decrement_and_compare_to_zero(count) )
       {
         // p の破棄処理の途中で deleter が削除されると困るのでコピーする
         deleter d = get_deleter(*p);