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 \
86  { \
87  0, 0, 0, { \
88  { 0, 0, nullptr } \
89  } \
90  }
91 
92 template<class HBODY>
94 
95 template<class HBODY>
97  public:
98  NLIB_CEXPR HandleAccess() NLIB_NOEXCEPT : ptr_(nullptr), handle_table_(nullptr) {}
100  HBODY* operator->() NLIB_NOEXCEPT { return ptr_->item; }
101  HBODY& operator*() NLIB_NOEXCEPT { return *ptr_->item; }
102  // Please call this just before you disable HBODY
103  void DetachHandleBody() NLIB_NOEXCEPT;
104  NLIB_ALWAYS_INLINE void Reset() NLIB_NOEXCEPT {
105  if (ptr_) {
106  HandleAccess<HBODY> tmp(ptr_, handle_table_);
107  ptr_ = nullptr;
108  handle_table_ = nullptr;
109  }
110  }
111 
112  private:
113  HandleAccess(detail::HandleData<HBODY>* ptr, HandleTable<HBODY>* handle_table) NLIB_NOEXCEPT
114  : ptr_(ptr),
115  handle_table_(handle_table) {}
116  NLIB_ALWAYS_INLINE void
117  Init(detail::HandleData<HBODY>* ptr, HandleTable<HBODY>* handle_table) NLIB_NOEXCEPT
118  NLIB_EXCLUDES(*ptr->item) NLIB_ACQUIRE(*ptr->item) NLIB_NO_THREAD_SAFETY_ANALYSIS {
119  this->Reset();
120  ptr_ = ptr;
121  handle_table_ = handle_table;
122  handle_maker::LockHandleBody<HBODY>(ptr->item);
123  }
124 
125  detail::HandleData<HBODY>* ptr_;
126  HandleTable<HBODY>* handle_table_;
127 
128  friend class HandleMaker<HBODY>;
129  NLIB_DISALLOW_COPY_AND_ASSIGN(HandleAccess);
130 };
131 
132 template<class HBODY>
134  int32_t old_handle = ptr_->handle;
135  if (old_handle != 0) {
136  if (nlib_atomic_compare_exchange32(&ptr_->handle, &old_handle, 0, 0, NLIB_ATOMIC_RELAXED,
138  nlib_atomic_sub_fetch32(&ptr_->transaction_count, 1, NLIB_ATOMIC_RELAXED);
139  // ptr_->transaction_count cannot be zero
140  }
141  }
142 }
143 
144 template<class HBODY>
145 class HandleMaker NLIB_FINAL {
146 #if (defined(__GLIBCXX__) && __GLIBCXX__ >= 20140911) || defined(_LIBCPP_VERSION)
147  NLIB_STATIC_ASSERT(std::is_nothrow_destructible<HBODY>::value);
148 #endif
149  static const size_t N = handle_maker::NumHandle<HBODY>::size;
150  NLIB_STATIC_ASSERT((N & (N - 1)) == 0);
151  NLIB_STATIC_ASSERT(N <= 4096);
152  NLIB_STATIC_ASSERT(N >= 64);
153 
154  public:
156  NLIB_CHECK_RESULT errno_t AttachHandleBody(int* handle, HBODY* body) NLIB_NOEXCEPT NLIB_NONNULL;
157  NLIB_CHECK_RESULT errno_t GetHandleAccess(int handle, HandleAccess<HBODY>* access)
159 
160  private:
161  NLIB_ALWAYS_INLINE size_t GetIdxFromHandle(int handle) NLIB_NOEXCEPT {
162  handle = (handle ^ ptr_->mask) >> 2; // demangle
163  return static_cast<size_t>(handle) & (N - 1);
164  }
165  NLIB_ALWAYS_INLINE int GetHandleFromIdx(size_t idx) NLIB_NOEXCEPT {
166  if (++ptr_->base_count == 1024 * 1024) ptr_->base_count = 0;
167  uint32_t salt = static_cast<uint32_t>(ptr_->base_count);
168  salt |= ((salt + 1) << 10) | ((salt + 2) << 20);
169  uint32_t handle = static_cast<uint32_t>(idx + (salt * N));
170  return static_cast<int>((handle << 2) ^ ptr_->mask); // mangle
171  }
172  NLIB_ALWAYS_INLINE void Remove(detail::HandleData<HBODY>* data) NLIB_NOEXCEPT
173  NLIB_REQUIRES(*data->item) NLIB_RELEASE(*data->item) NLIB_NO_THREAD_SAFETY_ANALYSIS {
174  HBODY* x = data->item;
175  handle_maker::UnlockHandleBody<HBODY>(data->item); // implicit acquire
176  if (nlib_atomic_sub_fetch32(&data->transaction_count, 1, NLIB_ATOMIC_RELAXED) == 0) {
177  // NOTE:
178  // DO NOT set NULL on data->item, and data->handle is already 0
179  NLIB_ASSERT(!handle_maker::IsHandleBodyEnabled(x));
180  handle_maker::DestroyHandleBody<HBODY>(x);
181  }
182  }
183 
184  private:
185  HandleTable<HBODY>* ptr_;
186  friend class HandleAccess<HBODY>;
187  NLIB_DISALLOW_COPY_AND_ASSIGN(HandleMaker);
188 };
189 
190 template<class HBODY>
192  : ptr_(ptr) {}
193 
194 template<class HBODY>
195 errno_t HandleMaker<HBODY>::AttachHandleBody(int* handle, HBODY* body) NLIB_NOEXCEPT NLIB_NO_TSAN {
196  // HandleMaker is the internal class, and you can avoid nullptr args
197  // if (!handle || !body) return EINVAL;
198  if (!ptr_->mask) {
199  int32_t expected = 0;
200  int32_t mask = detail::GenerateHandleMakerMask();
201  nlib_atomic_compare_exchange32(&ptr_->mask, &expected, mask, 0, NLIB_ATOMIC_RELAXED,
203  }
204 
205  size_t idx;
206  detail::HandleData<HBODY>* data;
207 
208  // ptr_->cur is not guraded because the value of ptr_->cur can be random.
209  idx = (ptr_->cur) & (N - 1);
210  ptr_->cur += 7; // to avoid false sharing
211  data = &ptr_->table[idx];
212  int32_t expected = 0;
213 
214  if (!nlib_atomic_compare_exchange32(&data->transaction_count, &expected, -1, 1,
216  size_t from = idx + 1;
217  for (; idx < N; ++idx) {
218  data = &ptr_->table[idx];
219  expected = 0;
220  if (nlib_atomic_compare_exchange32(&data->transaction_count, &expected, -1, 1,
222  goto CREATE_HANDLE_SUCCESS;
223  }
224  }
225  for (idx = 0; idx < from; ++idx) {
226  data = &ptr_->table[idx];
227  expected = 0;
228  if (nlib_atomic_compare_exchange32(&data->transaction_count, &expected, -1, 1,
230  goto CREATE_HANDLE_SUCCESS;
231  }
232  }
233  for (; idx < N; ++idx) {
234  data = &ptr_->table[idx];
235  expected = 0;
236  if (nlib_atomic_compare_exchange32(&data->transaction_count, &expected, -1, 0,
238  goto CREATE_HANDLE_SUCCESS;
239  }
240  }
241  for (idx = 0; idx < from; ++idx) {
242  data = &ptr_->table[idx];
243  expected = 0;
244  if (nlib_atomic_compare_exchange32(&data->transaction_count, &expected, -1, 0,
246  goto CREATE_HANDLE_SUCCESS;
247  }
248  }
249  // may return ENFILE if there is an available slot
250  return ENFILE;
251  }
252 
253 CREATE_HANDLE_SUCCESS:
254  data->item = body;
255  data->handle = this->GetHandleFromIdx(idx);
256  nlib_atomic_store32(&data->transaction_count, 1, NLIB_ATOMIC_RELEASE);
257  *handle = data->handle;
258  return 0;
259 }
260 
261 template<class HBODY>
263  NLIB_NOEXCEPT NLIB_NO_THREAD_SAFETY_ANALYSIS NLIB_NO_TSAN {
264  // HandleMaker is the internal class, and you can avoid nullptr args
265  // if (!access) return EINVAL;
266 
267  detail::HandleData<HBODY>* data;
268  size_t idx;
269  if (!nlib_atomic_load32(&ptr_->mask, NLIB_ATOMIC_RELAXED)) {
270  // no handles created yet
271  return EBADF;
272  }
273  idx = this->GetIdxFromHandle(handle);
274  if (idx >= N) {
275  return EBADF;
276  }
277 
278  data = &ptr_->table[idx];
279  int32_t old_cnt = nlib_atomic_load32(&data->transaction_count, NLIB_ATOMIC_RELAXED);
280  if (old_cnt <= 0) return EBADF;
281  for (;;) {
282  if (nlib_atomic_compare_exchange32(&data->transaction_count, &old_cnt, old_cnt + 1, 1,
284  break;
285  if (old_cnt <= 0) return EBADF;
286  }
287 
288  access->Init(data, ptr_); // implicit acquire
289  if (data->handle != handle || !handle_maker::IsHandleBodyEnabled(data->item)) {
290  // invalid handle or HBODY already closed
291  access->Reset();
292  return EBADF;
293  }
294  return 0;
295 }
296 
297 template<class HBODY>
299  if (handle_table_) {
300  HandleMaker<HBODY> maker(handle_table_);
301  maker.Remove(ptr_);
302  }
303 }
304 
305 NLIB_NAMESPACE_END
306 
307 #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:95
#define NLIB_DISALLOW_COPY_AND_ASSIGN(TypeName)
TypeName で指定されたクラスのコピーコンストラクタと代入演算子を禁止します。
Definition: Config.h:183
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:93
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:109
HandlerMakerクラスが利用するハンドルの実体への参照テーブルです。
Definition: HandleMaker.h:78
#define NLIB_CEXPR
利用可能であればconstexprが定義されます。そうでない場合は空文字列です。
Definition: Config.h:111
開発環境別の設定が書かれるファイルです。
TimeSpan operator*(int i, const TimeSpan &rhs) noexcept
rhs を i 倍します。
Definition: DateTime.h:195
#define NLIB_ATOMIC_RELAXED
gccの__ATOMIC_RELAXEDやC++11のstd::memory_order_relaxedに準じます。
ハンドル実体に対してポインタのようにアクセスできるアクセサクラスです。HandlerMakerクラスにより設定さ...
Definition: HandleMaker.h:96
#define NLIB_FINAL
利用可能であればfinalが定義されます。そうでない場合は空文字列です。
Definition: Config.h:250
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:98
#define NLIB_STATIC_ASSERT(exp)
静的アサートが定義されます。利用可能であればstatic_assertを利用します。
Definition: Config.h:174
#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