nlib
HandleMaker.h
[詳解]
1 
2 #pragma once
3 #ifndef INCLUDE_NN_NLIB_HANDLEMAKER_H_
4 #define INCLUDE_NN_NLIB_HANDLEMAKER_H_
5 
6 #include "nn/nlib/Config.h"
7 
8 NLIB_NAMESPACE_BEGIN
9 
10 namespace handle_maker {
11 template<class HBODY>
13  // HBODY must be unlocked
14  delete body;
15 }
16 
17 template<class HBODY>
19  // at least, nlib_atomic_thread_fence(NLIB_ATOMIC_ACQUIRE) needed
20  body->Lock();
21 }
22 
23 template<class HBODY>
25  // at least, read body or nlib_atomic_thread_fence(NLIB_ATOMIC_ACQUIRE) needed
26  body->Unlock();
27 }
28 
29 template<class HBODY>
31  // HBODY is locked(by LockHandleBody())
32  // body->IsHandleBodyEnabled() must return false if HBODY has been already closed.
33  return body->IsHandleBodyEnabled();
34 }
35 
36 template<class HBODY>
37 struct NumHandle {
38  static const size_t size = HBODY::N;
39 };
40 
41 } // namespace handle_maker
42 
43 namespace detail {
44 template<class HBODY>
45 struct HandleData {
46  int32_t transaction_count;
47  int handle;
48  HBODY* item;
49 };
50 
51 inline int32_t GenerateHandleMakerMask() NLIB_NOEXCEPT {
52  int32_t mask;
53  if (0 != nlib_gen_random(&mask, sizeof(mask))) {
54  mask = static_cast<int32_t>(reinterpret_cast<uintptr_t>(&mask));
55  }
56  // -1, 0, 1, 2, 3 become invalid as handle values
57  mask |= (1L << (20 + 2));
58  mask &= ~3;
59  return mask;
60 }
61 
62 } // namespace detail
63 
64 template<class HBODY>
65 struct HandleTable {
66  // must be statically initialized to 0
67  int32_t mask;
68  int base_count;
69  uint32_t cur;
70  detail::HandleData<HBODY> table[handle_maker::NumHandle<HBODY>::size];
71 };
72 #define NLIB_HANDLETABLE_INITIALIZER {0, 0, 0, {{0, 0, NULL}}}
73 
74 template<class HBODY> class HandleMaker;
75 
76 template<class HBODY>
77 class HandleAccess {
78  public:
79  HandleAccess() NLIB_NOEXCEPT : ptr_(NULL), handle_table_(NULL) {}
81  HBODY* operator->() NLIB_NOEXCEPT { return ptr_->item; }
82  HBODY& operator*() NLIB_NOEXCEPT { return *ptr_->item; }
83  // Please call this just before you disable HBODY
84  void DetachHandleBody() NLIB_NOEXCEPT;
85  NLIB_ALWAYS_INLINE void Reset() NLIB_NOEXCEPT {
86  if (ptr_) {
87  HandleAccess<HBODY> tmp(ptr_, handle_table_);
88  ptr_ = NULL;
89  handle_table_ = NULL;
90  }
91  }
92 
93  private:
94  HandleAccess(detail::HandleData<HBODY>* ptr, HandleTable<HBODY>* handle_table) NLIB_NOEXCEPT
95  : ptr_(ptr), handle_table_(handle_table) {}
96  NLIB_ALWAYS_INLINE void Init(detail::HandleData<HBODY>* ptr,
97  HandleTable<HBODY>* handle_table)
98  NLIB_NOEXCEPT NLIB_EXCLUDES(*ptr->item) NLIB_ACQUIRE(*ptr->item)
99  NLIB_NO_THREAD_SAFETY_ANALYSIS {
100  this->Reset();
101  ptr_ = ptr;
102  handle_table_ = handle_table;
103  handle_maker::LockHandleBody<HBODY>(ptr->item);
104  }
105 
106  detail::HandleData<HBODY>* ptr_;
107  HandleTable<HBODY>* handle_table_;
108 
109  friend class HandleMaker<HBODY>;
111 };
112 
113 template<class HBODY>
115  int32_t old_handle = ptr_->handle;
116  if (old_handle != 0) {
117  if (nlib_atomic_compare_exchange32(&ptr_->handle, &old_handle, 0, 0,
119  nlib_atomic_sub_fetch32(&ptr_->transaction_count, 1, NLIB_ATOMIC_RELAXED);
120  // ptr_->transaction_count cannot be zero
121  }
122  }
123 }
124 
125 template<class HBODY>
126 class HandleMaker {
127 #if (defined(__GLIBCXX__) && __GLIBCXX__ >= 20140911) || \
128  defined(_LIBCPP_VERSION)
129  NLIB_STATIC_ASSERT(std::is_nothrow_destructible<HBODY>::value);
130 #endif
131  static const size_t N = handle_maker::NumHandle<HBODY>::size;
132  NLIB_STATIC_ASSERT((N & (N - 1)) == 0);
133  NLIB_STATIC_ASSERT(N <= 4096);
134  NLIB_STATIC_ASSERT(N >= 64);
135 
136  public:
138  NLIB_CHECK_RESULT errno_t AttachHandleBody(int* handle,
139  HBODY* body) NLIB_NOEXCEPT NLIB_NONNULL;
140  NLIB_CHECK_RESULT errno_t GetHandleAccess(
141  int handle, HandleAccess<HBODY>* access) NLIB_NOEXCEPT NLIB_NONNULL;
142 
143  private:
144  NLIB_ALWAYS_INLINE size_t GetIdxFromHandle(int handle) NLIB_NOEXCEPT {
145  handle = (handle ^ ptr_->mask) >> 2; // demangle
146  return static_cast<size_t>(handle) & (N - 1);
147  }
148  NLIB_ALWAYS_INLINE int GetHandleFromIdx(size_t idx) NLIB_NOEXCEPT {
149  if (++ptr_->base_count == 1024 * 1024) ptr_->base_count = 0;
150  int salt = ptr_->base_count;
151  salt |= ((salt + 1) << 10) | ((salt + 2) << 20);
152  int handle = static_cast<int>(idx + (salt * N));
153  return (handle << 2) ^ ptr_->mask; // mangle
154  }
155  NLIB_ALWAYS_INLINE void Remove(detail::HandleData<HBODY>* data)
156  NLIB_NOEXCEPT NLIB_REQUIRES(*data->item) NLIB_RELEASE(*data->item)
157  NLIB_NO_THREAD_SAFETY_ANALYSIS {
158  HBODY* x = data->item;
159  handle_maker::UnlockHandleBody<HBODY>(data->item); // implicit acquire
160  if (nlib_atomic_sub_fetch32(&data->transaction_count, 1, NLIB_ATOMIC_RELAXED) == 0) {
161  // NOTE:
162  // DO NOT set NULL on data->item, and data->handle is already 0
163  NLIB_ASSERT(!handle_maker::IsHandleBodyEnabled(x));
164  handle_maker::DestroyHandleBody<HBODY>(x);
165  }
166  }
167 
168  private:
169  HandleTable<HBODY>* ptr_;
170  friend class HandleAccess<HBODY>;
171 };
172 
173 template<class HBODY>
175  : ptr_(ptr) {}
176 
177 template<class HBODY>
178 errno_t HandleMaker<HBODY>::AttachHandleBody(int* handle, HBODY* body) NLIB_NOEXCEPT NLIB_NO_TSAN {
179  // HandleMaker is the internal class, and you can avoid nullptr args
180  // if (!handle || !body) return EINVAL;
181  if (!ptr_->mask) {
182  int32_t expected = 0;
183  int32_t mask = detail::GenerateHandleMakerMask();
184  nlib_atomic_compare_exchange32(&ptr_->mask, &expected, mask, 0,
186  }
187 
188  size_t idx;
189  detail::HandleData<HBODY>* data;
190 
191  // ptr_->cur is not guraded because the value of ptr_->cur can be random.
192  idx = (ptr_->cur) & (N - 1);
193  ptr_->cur += 7; // to avoid false sharing
194  data = &ptr_->table[idx];
195  int32_t expected = 0;
196 
197  if (!nlib_atomic_compare_exchange32(&data->transaction_count, &expected, -1, 1,
199  size_t from = idx + 1;
200  for (; idx < N; ++idx) {
201  data = &ptr_->table[idx];
202  expected = 0;
203  if (nlib_atomic_compare_exchange32(&data->transaction_count, &expected, -1, 1,
205  goto CREATE_HANDLE_SUCCESS;
206  }
207  }
208  for (idx = 0; idx < from; ++idx) {
209  data = &ptr_->table[idx];
210  expected = 0;
211  if (nlib_atomic_compare_exchange32(&data->transaction_count, &expected, -1, 1,
213  goto CREATE_HANDLE_SUCCESS;
214  }
215  }
216  for (; idx < N; ++idx) {
217  data = &ptr_->table[idx];
218  expected = 0;
219  if (nlib_atomic_compare_exchange32(&data->transaction_count, &expected, -1, 0,
221  goto CREATE_HANDLE_SUCCESS;
222  }
223  }
224  for (idx = 0; idx < from; ++idx) {
225  data = &ptr_->table[idx];
226  expected = 0;
227  if (nlib_atomic_compare_exchange32(&data->transaction_count, &expected, -1, 0,
229  goto CREATE_HANDLE_SUCCESS;
230  }
231  }
232  // may return ENFILE if there is an available slot
233  return ENFILE;
234  }
235 
236  CREATE_HANDLE_SUCCESS:
237  data->item = body;
238  data->handle = this->GetHandleFromIdx(idx);
239  nlib_atomic_store32(&data->transaction_count, 1, NLIB_ATOMIC_RELEASE);
240  *handle = data->handle;
241  return 0;
242 }
243 
244 template<class HBODY>
246  NLIB_NOEXCEPT NLIB_NO_THREAD_SAFETY_ANALYSIS NLIB_NO_TSAN {
247  // HandleMaker is the internal class, and you can avoid nullptr args
248  // if (!access) return EINVAL;
249 
250  detail::HandleData<HBODY>* data;
251  size_t idx;
252  if (!nlib_atomic_load32(&ptr_->mask, NLIB_ATOMIC_RELAXED)) {
253  // no handles created yet
254  return EBADF;
255  }
256  idx = this->GetIdxFromHandle(handle);
257  if (idx >= N) {
258  return EBADF;
259  }
260 
261  data = &ptr_->table[idx];
262  int32_t old_cnt = nlib_atomic_load32(&data->transaction_count, NLIB_ATOMIC_RELAXED);
263  if (old_cnt <= 0) return EBADF;
264  for (;;) {
265  if (nlib_atomic_compare_exchange32(&data->transaction_count, &old_cnt, old_cnt + 1, 1,
267  break;
268  if (old_cnt <= 0) return EBADF;
269  }
270 
271  access->Init(data, ptr_); // implicit acquire
272  if (data->handle != handle || !handle_maker::IsHandleBodyEnabled(data->item)) {
273  // invalid handle or HBODY already closed
274  access->Reset();
275  return EBADF;
276  }
277  return 0;
278 }
279 
280 template<class HBODY>
282  if (handle_table_) {
283  HandleMaker<HBODY> maker(handle_table_);
284  maker.Remove(ptr_);
285  }
286 }
287 
288 NLIB_NAMESPACE_END
289 
290 #endif // INCLUDE_NN_NLIB_HANDLEMAKER_H_
size_t型のstatic constのデータメンバsizeを通してHBODY型へアクセスするハンドルの総数を得ることができま...
Definition: HandleMaker.h:37
int32_t nlib_atomic_load32(const int32_t *ptr, int memorder)
アトミックに値をロードします。動作はgccの__atomic_load_n()に準じます。
void LockHandleBody(HBODY *body) noexcept
HandleAccess<HBODY>オブジェクトがHandleMaker<HBODY>::GetHandleAccess()により与えられる際に呼び出され...
Definition: HandleMaker.h:18
#define NLIB_ALWAYS_INLINE
コンパイラに関数をインライン展開するように強く示します。
Definition: Platform_unix.h:69
#define NLIB_DISALLOW_COPY_AND_ASSIGN(TypeName)
TypeName で指定されたクラスのコピーコンストラクタと代入演算子を禁止します。
Definition: Config.h:145
void UnlockHandleBody(HBODY *body) noexcept
HandleAccess<HBODY>オブジェクトのデストラクト時に実行されます。
Definition: HandleMaker.h:24
#define NLIB_ATOMIC_RELEASE
gccの__ATOMIC_RELEASEやC++11のstd::memory_order_releaseに準じます。
#define NLIB_CHECK_RESULT
関数の呼び出し元が戻り値をチェックする必要があることを示します。
Definition: Platform_unix.h:74
32bit整数値を持つハンドルの実装を支援するクラスです。
Definition: HandleMaker.h:74
void DestroyHandleBody(HBODY *body) noexcept
HandleMakerオブジェクトからハンドルの実体を解体する際に呼び出されます。
Definition: HandleMaker.h:12
NLIB_CHECK_RESULT errno_t nlib_gen_random(void *buf, size_t size)
ランダムな値をsize バイト生成してbuf に格納します。
int32_t nlib_atomic_sub_fetch32(int32_t *ptr, int32_t val, int memorder)
アトミックな値の減算を行います。動作はgccの__atomic_sub_fetch()に準じます。
bool IsHandleBodyEnabled(HBODY *body) noexcept
ハンドルの実体が有効かどうかを返します。
Definition: HandleMaker.h:30
#define NLIB_NOEXCEPT
環境に合わせてnoexcept 又は同等の定義がされます。
Definition: Config.h:86
HandlerMakerクラスが利用するハンドルの実体への参照テーブルです。
Definition: HandleMaker.h:65
開発環境別の設定が書かれるファイルです。
TimeSpan operator*(int i, const TimeSpan &rhs) noexcept
rhs を i 倍します。
Definition: DateTime.h:212
#define NLIB_ATOMIC_RELAXED
gccの__ATOMIC_RELAXEDやC++11のstd::memory_order_relaxedに準じます。
ハンドル実体に対してポインタのようにアクセスできるアクセサクラスです。HandlerMakerクラスにより設定さ...
Definition: HandleMaker.h:77
int nlib_atomic_compare_exchange32(int32_t *ptr, int32_t *expected, int32_t desired, int weak, int success_memorder, int failure_memorder)
アトミックな値の比較と入れ替えを行います。動作はgccの__atomic_compare_exchange_n()に準じます。 ...
HandleAccess() noexcept
デフォルトコンストラクタです。
Definition: HandleMaker.h:79
#define NLIB_STATIC_ASSERT(exp)
静的アサートが定義されます。利用可能であればstatic_assertを利用します。
Definition: Config.h:136
#define NLIB_NONNULL
全ての引数にNULLを指定することができないことを示します。
Definition: Platform_unix.h:76
void nlib_atomic_store32(int32_t *ptr, int32_t val, int memorder)
アトミックに値をストアします。動作はgccの__atomic_store_n()に準じます。
int errno_t
intのtypedefで、戻り値としてPOSIXのエラー値を返すことを示します。
Definition: NMalloc.h:24