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