xrcu.cpp 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. #include "xrcu.hpp"
  2. #include "xatomic.hpp"
  3. #include "version.hpp"
  4. #include <thread>
  5. #include <mutex>
  6. #include <atomic>
  7. #include <chrono>
  8. #include <cstdint>
  9. #include <ctime>
  10. #include <functional>
  11. namespace xrcu
  12. {
  13. struct td_link
  14. {
  15. td_link *next;
  16. td_link *prev;
  17. void init_head ()
  18. {
  19. this->next = this->prev = this;
  20. }
  21. void add (td_link *headp)
  22. { // Add self to HEADP.
  23. this->next = headp->next;
  24. this->prev = headp;
  25. headp->next->prev = this;
  26. headp->next = this;
  27. }
  28. void del ()
  29. { // Unlink self.
  30. this->next->prev = this->prev;
  31. this->prev->next = this->next;
  32. }
  33. bool empty_p () const
  34. {
  35. return (this == this->next);
  36. }
  37. bool linked_p () const
  38. {
  39. return (this->next != nullptr);
  40. }
  41. void splice (td_link *dst)
  42. {
  43. if (this->empty_p ())
  44. return;
  45. this->next->prev = dst;
  46. this->prev->next = dst->next;
  47. dst->next->prev = this->prev;
  48. dst->next = this->next;
  49. }
  50. };
  51. static const uintptr_t GP_PHASE_BIT =
  52. (uintptr_t)1 << (sizeof (uintptr_t) * 8 - 1);
  53. static const uintptr_t GP_NEST_MASK = GP_PHASE_BIT - 1;
  54. // Possible states for a reader thread.
  55. enum
  56. {
  57. rd_active,
  58. rd_inactive,
  59. rd_old
  60. };
  61. struct registry
  62. {
  63. std::atomic_uintptr_t counter;
  64. td_link root;
  65. std::mutex td_mtx;
  66. std::mutex gp_mtx;
  67. static const unsigned int QS_ATTEMPTS = 1000;
  68. registry () : counter (1)
  69. {
  70. this->root.init_head ();
  71. }
  72. void add_tdata (td_link *lp)
  73. {
  74. this->td_mtx.lock ();
  75. lp->add (&this->root);
  76. this->td_mtx.unlock ();
  77. }
  78. uintptr_t get_ctr () const
  79. {
  80. return (this->counter.load (std::memory_order_relaxed));
  81. }
  82. void poll_readers (td_link *, td_link *, td_link *);
  83. void sync ();
  84. };
  85. static registry global_reg;
  86. // Maximum number of pending finalizers before flushing.
  87. #ifndef XRCU_MAX_FINS
  88. # define XRCU_MAX_FINS 1000
  89. #endif
  90. static const unsigned int MAX_FINS = XRCU_MAX_FINS;
  91. struct tl_data : public td_link
  92. {
  93. bool must_flush;
  94. unsigned int n_fins;
  95. std::atomic_uintptr_t counter;
  96. size_t xrand_val;
  97. finalizable *fin_objs;
  98. uintptr_t get_ctr () const
  99. {
  100. return (this->counter.load (std::memory_order_relaxed));
  101. }
  102. int state () const
  103. {
  104. auto val = this->counter.load (std::memory_order_acquire);
  105. if (!(val & GP_NEST_MASK))
  106. return (rd_inactive);
  107. else if (!((val ^ global_reg.get_ctr ()) & GP_PHASE_BIT))
  108. return (rd_active);
  109. else
  110. return (rd_old);
  111. }
  112. bool in_cs () const
  113. {
  114. return ((this->get_ctr () & GP_NEST_MASK) != 0);
  115. }
  116. bool flush_all ()
  117. {
  118. if (!sync ())
  119. return (false);
  120. for (auto f = this->fin_objs; f != nullptr; )
  121. {
  122. auto next = f->_Fin_next;
  123. f->safe_destroy ();
  124. f = next;
  125. }
  126. this->fin_objs = nullptr;
  127. this->n_fins = 0;
  128. this->must_flush = false;
  129. return (true);
  130. }
  131. void finalize (finalizable *finp)
  132. {
  133. finp->_Fin_next = this->fin_objs;
  134. this->fin_objs = finp;
  135. if (++this->n_fins < MAX_FINS)
  136. ;
  137. else if (!this->flush_all ())
  138. /* Couldn't reclaim memory since we are in a critical section.
  139. * Set the flag to do it ASAP. */
  140. this->must_flush = true;
  141. }
  142. ~tl_data ()
  143. {
  144. if (!this->linked_p ())
  145. return;
  146. this->counter.store (0, std::memory_order_release);
  147. if (this->n_fins > 0)
  148. this->flush_all ();
  149. global_reg.td_mtx.lock ();
  150. this->del ();
  151. global_reg.td_mtx.unlock ();
  152. }
  153. };
  154. static thread_local tl_data tldata {};
  155. static inline tl_data*
  156. local_data ()
  157. {
  158. auto self = &tldata;
  159. if (!self->linked_p ())
  160. global_reg.add_tdata (self);
  161. return (self);
  162. }
  163. void enter_cs ()
  164. {
  165. auto self = local_data ();
  166. auto val = self->get_ctr ();
  167. val = (val & GP_NEST_MASK) == 0 ? global_reg.get_ctr () : val + 1;
  168. self->counter.store (val, std::memory_order_release);
  169. }
  170. void exit_cs ()
  171. {
  172. auto self = local_data ();
  173. auto val = self->get_ctr ();
  174. self->counter.store (val - 1, std::memory_order_release);
  175. if (self->must_flush && !self->in_cs ())
  176. self->flush_all ();
  177. }
  178. bool in_cs ()
  179. {
  180. auto self = &tldata;
  181. return (self->linked_p () && self->in_cs ());
  182. }
  183. void registry::poll_readers (td_link *readers, td_link *outp, td_link *qsp)
  184. {
  185. for (unsigned int loops = 0 ; ; ++loops)
  186. {
  187. td_link *next, *runp = readers->next;
  188. for (; runp != readers; runp = next)
  189. {
  190. next = runp->next;
  191. switch (((tl_data *)runp)->state ())
  192. {
  193. case rd_active:
  194. if (outp != nullptr)
  195. {
  196. runp->del ();
  197. runp->add (outp);
  198. break;
  199. }
  200. // Fallthrough.
  201. case rd_inactive:
  202. runp->del ();
  203. runp->add (qsp);
  204. break;
  205. default:
  206. break;
  207. }
  208. }
  209. if (readers->empty_p ())
  210. break;
  211. this->td_mtx.unlock ();
  212. if (loops < QS_ATTEMPTS)
  213. xatomic_spin_nop ();
  214. else
  215. std::this_thread::sleep_for (std::chrono::milliseconds (1));
  216. this->td_mtx.lock ();
  217. }
  218. }
  219. void registry::sync ()
  220. {
  221. this->gp_mtx.lock ();
  222. this->td_mtx.lock ();
  223. if (this->root.empty_p ())
  224. {
  225. this->td_mtx.unlock ();
  226. this->gp_mtx.unlock ();
  227. return;
  228. }
  229. td_link out, qs;
  230. qs.init_head ();
  231. out.init_head ();
  232. std::atomic_thread_fence (std::memory_order_acq_rel);
  233. poll_readers (&this->root, &out, &qs);
  234. this->counter.store (this->get_ctr () ^ GP_PHASE_BIT,
  235. std::memory_order_relaxed);
  236. poll_readers (&out, nullptr, &qs);
  237. qs.splice (&this->root);
  238. this->td_mtx.unlock ();
  239. this->gp_mtx.unlock ();
  240. }
  241. bool sync ()
  242. {
  243. if (in_cs ())
  244. return (false);
  245. global_reg.sync ();
  246. return (true);
  247. }
  248. void finalize (finalizable *finp)
  249. {
  250. if (finp)
  251. local_data()->finalize (finp);
  252. }
  253. bool flush_finalizers ()
  254. {
  255. auto tld = local_data ();
  256. bool ret = tld->flush_all ();
  257. if (!ret)
  258. tld->must_flush = true;
  259. return (ret);
  260. }
  261. unsigned int xrand ()
  262. {
  263. auto self = &tldata; // Avoid local_data ()
  264. if (!self->xrand_val)
  265. self->xrand_val = (unsigned int)(time (nullptr) ^
  266. std::hash<std::thread::id>() (std::this_thread::get_id ()));
  267. self->xrand_val = self->xrand_val * 1103515245 + 12345;
  268. return (self->xrand_val >> 16);
  269. }
  270. static void
  271. atfork_prepare ()
  272. {
  273. global_reg.td_mtx.lock ();
  274. }
  275. static void
  276. atfork_parent ()
  277. {
  278. global_reg.td_mtx.unlock ();
  279. }
  280. static void
  281. atfork_child ()
  282. {
  283. atfork_parent ();
  284. // Reset the registry
  285. global_reg.root.init_head ();
  286. auto self = &tldata;
  287. if (!self->linked_p ())
  288. return;
  289. // Manually add ourselves to the registry without locking.
  290. self->add (&global_reg.root);
  291. }
  292. atfork atfork_data ()
  293. {
  294. atfork ret;
  295. ret.prepare = atfork_prepare;
  296. ret.parent = atfork_parent;
  297. ret.child = atfork_child;
  298. return (ret);
  299. }
  300. void library_version (int& major, int& minor)
  301. {
  302. major = MAJOR, minor = MINOR;
  303. }
  304. } // namespace rcu