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 NLIB_VIS_PUBLIC int32_t GenerateHandleMakerMask() NLIB_NOEXCEPT;
52 
53 } // namespace detail
54 
55 template<class HBODY>
56 struct HandleTable {
57  // must be statically initialized to 0
58  int32_t mask;
59  int base_count;
60  uint32_t cur;
61  detail::HandleData<HBODY> table[handle_maker::NumHandle<HBODY>::size];
62 };
63 #define NLIB_HANDLETABLE_INITIALIZER {0, 0, 0, {{0, 0, NULL}}}
64 
65 template<class HBODY> class HandleMaker;
66 
67 template<class HBODY>
68 class HandleAccess {
69  public:
70  HandleAccess() NLIB_NOEXCEPT : ptr_(NULL), handle_table_(NULL) {}
72  HBODY* operator->() NLIB_NOEXCEPT { return ptr_->item; }
73  HBODY& operator*() NLIB_NOEXCEPT { return *ptr_->item; }
74  // Please call this just before you disable HBODY
75  void DetachHandleBody() NLIB_NOEXCEPT;
76  NLIB_ALWAYS_INLINE void Reset() NLIB_NOEXCEPT {
77  if (ptr_) {
78  HandleAccess<HBODY> tmp(ptr_, handle_table_);
79  ptr_ = NULL;
80  handle_table_ = NULL;
81  }
82  }
83 
84  private:
85  HandleAccess(detail::HandleData<HBODY>* ptr, HandleTable<HBODY>* handle_table) NLIB_NOEXCEPT
86  : ptr_(ptr), handle_table_(handle_table) {}
87  NLIB_ALWAYS_INLINE void Init(detail::HandleData<HBODY>* ptr,
88  HandleTable<HBODY>* handle_table) NLIB_NOEXCEPT {
89  this->Reset();
90  ptr_ = ptr;
91  handle_table_ = handle_table;
92  handle_maker::LockHandleBody<HBODY>(ptr->item);
93  }
94 
95  detail::HandleData<HBODY>* ptr_;
96  HandleTable<HBODY>* handle_table_;
97 
98  friend class HandleMaker<HBODY>;
99  NLIB_DISALLOW_COPY_AND_ASSIGN(HandleAccess);
100 };
101 
102 template<class HBODY>
104  int32_t old_handle = ptr_->handle;
105  if (old_handle != 0) {
106  if (nlib_atomic_compare_exchange32(&ptr_->handle, &old_handle, 0, 0,
108  nlib_atomic_sub_fetch32(&ptr_->transaction_count, 1, NLIB_ATOMIC_RELAXED);
109  // ptr_->transaction_count cannot be zero
110  }
111  }
112 }
113 
114 template<class HBODY>
115 class HandleMaker {
116 #if (defined(_MSC_VER) && _MSC_VER >= 1700) || \
117  (defined(__GLIBCXX__) && __GLIBCXX__ >= 20140911) || \
118  defined(_LIBCPP_VERSION)
119  NLIB_STATIC_ASSERT(std::is_nothrow_destructible<HBODY>::value);
120 #endif
121  static const size_t N = handle_maker::NumHandle<HBODY>::size;
122  NLIB_STATIC_ASSERT((N & (N - 1)) == 0);
123  NLIB_STATIC_ASSERT(N <= 4096);
124  NLIB_STATIC_ASSERT(N >= 64);
125 
126  public:
128  NLIB_CHECK_RESULT errno_t AttachHandleBody(int* handle,
129  HBODY* body) NLIB_NOEXCEPT NLIB_NONNULL;
130  NLIB_CHECK_RESULT errno_t GetHandleAccess(
131  int handle, HandleAccess<HBODY>* access) NLIB_NOEXCEPT NLIB_NONNULL;
132 
133  private:
134  NLIB_ALWAYS_INLINE size_t GetIdxFromHandle(int handle) NLIB_NOEXCEPT {
135  handle = (handle ^ ptr_->mask) >> 2; // demangle
136  return static_cast<size_t>(handle) & (N - 1);
137  }
138  NLIB_ALWAYS_INLINE int GetHandleFromIdx(size_t idx) NLIB_NOEXCEPT {
139  if (++ptr_->base_count == 1024 * 1024) ptr_->base_count = 0;
140  int salt = ptr_->base_count;
141  salt |= ((salt + 1) << 10) | ((salt + 2) << 20);
142  int handle = static_cast<int>(idx + (salt * N));
143  return (handle << 2) ^ ptr_->mask; // mangle
144  }
145  NLIB_ALWAYS_INLINE void Remove(detail::HandleData<HBODY>* data) NLIB_NOEXCEPT {
146  HBODY* x = data->item;
147  handle_maker::UnlockHandleBody<HBODY>(x); // implicit acquire
148  if (nlib_atomic_sub_fetch32(&data->transaction_count, 1, NLIB_ATOMIC_RELAXED) == 0) {
149  // NOTE:
150  // DO NOT set NULL on data->item, and data->handle is already 0
151  NLIB_ASSERT(!handle_maker::IsHandleBodyEnabled(x));
152  handle_maker::DestroyHandleBody<HBODY>(x);
153  }
154  }
155 
156  private:
157  HandleTable<HBODY>* ptr_;
158  friend class HandleAccess<HBODY>;
159 };
160 
161 template<class HBODY>
163  : ptr_(ptr) {}
164 
165 template<class HBODY>
167  // HandleMaker is the internal class, and you can avoid nullptr args
168  // if (!handle || !body) return EINVAL;
169  if (!ptr_->mask) {
170  int32_t expected = 0;
171  int32_t mask = detail::GenerateHandleMakerMask();
172  nlib_atomic_compare_exchange32(&ptr_->mask, &expected, mask, 0,
174  }
175 
176  size_t idx;
177  detail::HandleData<HBODY>* data;
178 
179  // ptr_->cur is not guraded because the value of ptr_->cur can be random.
180  idx = (ptr_->cur) & (N - 1);
181  ptr_->cur += 7; // to avoid false sharing
182  data = &ptr_->table[idx];
183  int32_t expected = 0;
184 
185  if (!nlib_atomic_compare_exchange32(&data->transaction_count, &expected, -1, 1,
187  size_t from = idx + 1;
188  for (; idx < N; ++idx) {
189  data = &ptr_->table[idx];
190  expected = 0;
191  if (nlib_atomic_compare_exchange32(&data->transaction_count, &expected, -1, 1,
193  goto CREATE_HANDLE_SUCCESS;
194  }
195  }
196  for (idx = 0; idx < from; ++idx) {
197  data = &ptr_->table[idx];
198  expected = 0;
199  if (nlib_atomic_compare_exchange32(&data->transaction_count, &expected, -1, 1,
201  goto CREATE_HANDLE_SUCCESS;
202  }
203  }
204  for (; idx < N; ++idx) {
205  data = &ptr_->table[idx];
206  expected = 0;
207  if (nlib_atomic_compare_exchange32(&data->transaction_count, &expected, -1, 0,
209  goto CREATE_HANDLE_SUCCESS;
210  }
211  }
212  for (idx = 0; idx < from; ++idx) {
213  data = &ptr_->table[idx];
214  expected = 0;
215  if (nlib_atomic_compare_exchange32(&data->transaction_count, &expected, -1, 0,
217  goto CREATE_HANDLE_SUCCESS;
218  }
219  }
220  // may return ENFILE if there is an available slot
221  return ENFILE;
222  }
223 
224  CREATE_HANDLE_SUCCESS:
225  data->item = body;
226  data->handle = this->GetHandleFromIdx(idx);
227  nlib_atomic_store32(&data->transaction_count, 1, NLIB_ATOMIC_RELEASE);
228  *handle = data->handle;
229  return 0;
230 }
231 
232 template<class HBODY>
234  // HandleMaker is the internal class, and you can avoid nullptr args
235  // if (!access) return EINVAL;
236 
237  detail::HandleData<HBODY>* data;
238  size_t idx;
239  if (!nlib_atomic_load32(&ptr_->mask, NLIB_ATOMIC_RELAXED)) {
240  // no handles created yet
241  return EBADF;
242  }
243  idx = this->GetIdxFromHandle(handle);
244  if (idx >= N) {
245  return EBADF;
246  }
247 
248  data = &ptr_->table[idx];
249  int32_t old_cnt = nlib_atomic_load32(&data->transaction_count, NLIB_ATOMIC_RELAXED);
250  if (old_cnt <= 0) return EBADF;
251  for (;;) {
252  if (nlib_atomic_compare_exchange32(&data->transaction_count, &old_cnt, old_cnt + 1, 1,
254  break;
255  if (old_cnt <= 0) return EBADF;
256  }
257 
258  access->Init(data, ptr_); // implicit acquire
259  if (data->handle != handle || !handle_maker::IsHandleBodyEnabled(data->item)) {
260  // invalid handle or HBODY already closed
261  access->Reset();
262  return EBADF;
263  }
264  return 0;
265 }
266 
267 template<class HBODY>
269  if (handle_table_) {
270  HandleMaker<HBODY> maker(handle_table_);
271  maker.Remove(ptr_);
272  }
273 }
274 
275 NLIB_NAMESPACE_END
276 
277 #endif // INCLUDE_NN_NLIB_HANDLEMAKER_H_
#define NLIB_NOEXCEPT
環境に合わせてnoexcept 又は同等の定義がされます。
Definition: Platform.h:2151
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オブジェクトがHandleMaker::GetHandleAccess()により与えられる際に呼び出され...
Definition: HandleMaker.h:18
#define NLIB_NONNULL
全ての引数にNULLを指定することができないことを示します。
Definition: Platform_unix.h:66
#define NLIB_DISALLOW_COPY_AND_ASSIGN(TypeName)
TypeName で指定されたクラスのコピーコンストラクタと代入演算子を禁止します。
Definition: Config.h:126
void UnlockHandleBody(HBODY *body) noexcept
HandleAccessオブジェクトのデストラクト時に実行されます。
Definition: HandleMaker.h:24
#define NLIB_ATOMIC_RELEASE
gccの__ATOMIC_RELEASEやC++11のstd::memory_order_releaseに準じます。
32bit整数値を持つハンドルの実装を支援するクラスです。
Definition: HandleMaker.h:65
void DestroyHandleBody(HBODY *body) noexcept
HandleMakerオブジェクトからハンドルの実体を解体する際に呼び出されます。
Definition: HandleMaker.h:12
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
HandlerMakerクラスが利用するハンドルの実体への参照テーブルです。
Definition: HandleMaker.h:56
開発環境別の設定が書かれるファイルです。
TimeSpan operator*(int i, const TimeSpan &rhs) noexcept
rhs を i 倍します。
Definition: DateTime.h:213
#define NLIB_ATOMIC_RELAXED
gccの__ATOMIC_RELAXEDやC++11のstd::memory_order_relaxedに準じます。
#define NLIB_ALWAYS_INLINE
コンパイラに関数をインライン展開するように強く示します。
Definition: Platform_unix.h:59
#define NLIB_VIS_PUBLIC
関数やクラス等のシンボルをライブラリの外部に公開します。
Definition: Platform_unix.h:51
ハンドル実体に対してポインタのようにアクセスできるアクセサクラスです。HandlerMakerクラスにより設定さ...
Definition: HandleMaker.h:68
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:70
#define NLIB_STATIC_ASSERT(exp)
静的アサートが定義されます。利用可能であればstatic_assertを利用します。
Definition: Config.h:117
void nlib_atomic_store32(int32_t *ptr, int32_t val, int memorder)
アトミックに値をストアします。動作はgccの__atomic_store_n()に準じます。
#define NLIB_CHECK_RESULT
関数の呼び出し元が戻り値をチェックする必要があることを示します。
Definition: Platform_unix.h:64
int errno_t
intのtypedefで、戻り値としてPOSIXのエラー値を返すことを示します。
Definition: NMalloc.h:24