nlib
HandleMaker.h
Go to the documentation of this file.
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, NULL}}}
86 
87 template<class HBODY> class HandleMaker;
88 
89 template<class HBODY>
90 class HandleAccess {
91  public:
92  HandleAccess() NLIB_NOEXCEPT : ptr_(NULL), handle_table_(NULL) {}
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_ = NULL;
102  handle_table_ = NULL;
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 {
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  int salt = ptr_->base_count;
164  salt |= ((salt + 1) << 10) | ((salt + 2) << 20);
165  int handle = static_cast<int>(idx + (salt * N));
166  return (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>;
184 };
185 
186 template<class HBODY>
188  : ptr_(ptr) {}
189 
190 template<class HBODY>
191 errno_t HandleMaker<HBODY>::AttachHandleBody(int* handle, HBODY* body) NLIB_NOEXCEPT NLIB_NO_TSAN {
192  // HandleMaker is the internal class, and you can avoid nullptr args
193  // if (!handle || !body) return EINVAL;
194  if (!ptr_->mask) {
195  int32_t expected = 0;
196  int32_t mask = detail::GenerateHandleMakerMask();
197  nlib_atomic_compare_exchange32(&ptr_->mask, &expected, mask, 0,
199  }
200 
201  size_t idx;
202  detail::HandleData<HBODY>* data;
203 
204  // ptr_->cur is not guraded because the value of ptr_->cur can be random.
205  idx = (ptr_->cur) & (N - 1);
206  ptr_->cur += 7; // to avoid false sharing
207  data = &ptr_->table[idx];
208  int32_t expected = 0;
209 
210  if (!nlib_atomic_compare_exchange32(&data->transaction_count, &expected, -1, 1,
212  size_t from = idx + 1;
213  for (; idx < N; ++idx) {
214  data = &ptr_->table[idx];
215  expected = 0;
216  if (nlib_atomic_compare_exchange32(&data->transaction_count, &expected, -1, 1,
218  goto CREATE_HANDLE_SUCCESS;
219  }
220  }
221  for (idx = 0; idx < from; ++idx) {
222  data = &ptr_->table[idx];
223  expected = 0;
224  if (nlib_atomic_compare_exchange32(&data->transaction_count, &expected, -1, 1,
226  goto CREATE_HANDLE_SUCCESS;
227  }
228  }
229  for (; idx < N; ++idx) {
230  data = &ptr_->table[idx];
231  expected = 0;
232  if (nlib_atomic_compare_exchange32(&data->transaction_count, &expected, -1, 0,
234  goto CREATE_HANDLE_SUCCESS;
235  }
236  }
237  for (idx = 0; idx < from; ++idx) {
238  data = &ptr_->table[idx];
239  expected = 0;
240  if (nlib_atomic_compare_exchange32(&data->transaction_count, &expected, -1, 0,
242  goto CREATE_HANDLE_SUCCESS;
243  }
244  }
245  // may return ENFILE if there is an available slot
246  return ENFILE;
247  }
248 
249  CREATE_HANDLE_SUCCESS:
250  data->item = body;
251  data->handle = this->GetHandleFromIdx(idx);
252  nlib_atomic_store32(&data->transaction_count, 1, NLIB_ATOMIC_RELEASE);
253  *handle = data->handle;
254  return 0;
255 }
256 
257 template<class HBODY>
259  NLIB_NOEXCEPT NLIB_NO_THREAD_SAFETY_ANALYSIS NLIB_NO_TSAN {
260  // HandleMaker is the internal class, and you can avoid nullptr args
261  // if (!access) return EINVAL;
262 
263  detail::HandleData<HBODY>* data;
264  size_t idx;
265  if (!nlib_atomic_load32(&ptr_->mask, NLIB_ATOMIC_RELAXED)) {
266  // no handles created yet
267  return EBADF;
268  }
269  idx = this->GetIdxFromHandle(handle);
270  if (idx >= N) {
271  return EBADF;
272  }
273 
274  data = &ptr_->table[idx];
275  int32_t old_cnt = nlib_atomic_load32(&data->transaction_count, NLIB_ATOMIC_RELAXED);
276  if (old_cnt <= 0) return EBADF;
277  for (;;) {
278  if (nlib_atomic_compare_exchange32(&data->transaction_count, &old_cnt, old_cnt + 1, 1,
280  break;
281  if (old_cnt <= 0) return EBADF;
282  }
283 
284  access->Init(data, ptr_); // implicit acquire
285  if (data->handle != handle || !handle_maker::IsHandleBodyEnabled(data->item)) {
286  // invalid handle or HBODY already closed
287  access->Reset();
288  return EBADF;
289  }
290  return 0;
291 }
292 
293 template<class HBODY>
295  if (handle_table_) {
296  HandleMaker<HBODY> maker(handle_table_);
297  maker.Remove(ptr_);
298  }
299 }
300 
301 NLIB_NAMESPACE_END
302 
303 #endif // INCLUDE_NN_NLIB_HANDLEMAKER_H_
A total number of handles that access the HBODY type can be obtained through the data member size of ...
Definition: HandleMaker.h:50
int32_t nlib_atomic_load32(const int32_t *ptr, int memorder)
Loads a value in an atomic operation. Its behavior is similar to the one for __atomic_load_n() of gcc...
void LockHandleBody(HBODY *body) noexcept
Called when the HandleAccess<HBODY> object is given by HandleMaker<HBODY>::GetHandleAccess().
Definition: HandleMaker.h:31
#define NLIB_ALWAYS_INLINE
Indicates that the compiler is forced to perform inline expansion of functions.
Definition: Platform_unix.h:97
#define NLIB_DISALLOW_COPY_AND_ASSIGN(TypeName)
Prohibits use of the copy constructor and assignment operator for the class specified by TypeName...
Definition: Config.h:163
void UnlockHandleBody(HBODY *body) noexcept
Called when the HandleAccess object is destroyed.
Definition: HandleMaker.h:37
#define NLIB_ATOMIC_RELEASE
Similar to __ATOMIC_RELEASE of gcc or std::memory_order_release of C++11.
#define NLIB_CHECK_RESULT
Indicates that the caller of the function must check the returned value.
A class supporting the implementation of handles with a 32-bit integer value.
Definition: HandleMaker.h:87
void DestroyHandleBody(HBODY *body) noexcept
Called from the HandleMaker object to destroy a handle instance.
Definition: HandleMaker.h:25
NLIB_CHECK_RESULT errno_t nlib_gen_random(void *buf, size_t size)
Generates a random value of size bytes and stores it in buf.
int32_t nlib_atomic_sub_fetch32(int32_t *ptr, int32_t val, int memorder)
Subtracts atomic values. Its behavior is similar to the one for __atomic_sub_fetch() of gcc...
bool IsHandleBodyEnabled(HBODY *body) noexcept
Returns if the handle instance is valid or not.
Definition: HandleMaker.h:43
#define NLIB_NOEXCEPT
Defines noexcept geared to the environment, or the equivalent.
Definition: Config.h:99
A table referencing handle instances used by the HandlerMaker class.
Definition: HandleMaker.h:78
A file that contains the configuration information for each development environment.
TimeSpan operator*(int i, const TimeSpan &rhs) noexcept
Increases rhs by a factor of i.
Definition: DateTime.h:225
#define NLIB_ATOMIC_RELAXED
Similar to __ATOMIC_RELAXED of gcc or std::memory_order_relaxed of C++11.
An accessor class that can access the handle instance as if it is a pointer. It becomes available aft...
Definition: HandleMaker.h:90
int nlib_atomic_compare_exchange32(int32_t *ptr, int32_t *expected, int32_t desired, int weak, int success_memorder, int failure_memorder)
Compares and swaps atomic values. Its behavior is similar to the one for __atomic_compare_exchange_n(...
HandleAccess() noexcept
Instantiates the object with default parameters (default constructor).
Definition: HandleMaker.h:92
#define NLIB_STATIC_ASSERT(exp)
Defines a static assertion. Uses static_assert if it is available for use.
Definition: Config.h:154
#define NLIB_NONNULL
Indicates that you cannot specify NULL for all arguments.
void nlib_atomic_store32(int32_t *ptr, int32_t val, int memorder)
Stores a value in an atomic operation. Its behavior is similar to the one for __atomic_store_n() of g...
int errno_t
Indicates with an int-type typedef that a POSIX error value is returned as the return value...
Definition: NMalloc.h:37