12c4fd39ffa4586e0b75da864d1751abff8ca77e
[folly.git] / folly / experimental / hazptr / hazptr-impl.h
1 /*
2  * Copyright 2017 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /* override-include-guard */
18 #ifndef HAZPTR_H
19 #error "This should only be included by hazptr.h"
20 #endif
21
22 /* quality of implementation switches */
23
24 // NOTE: The #ifndef pattern is prone to ODR violation. Its use for
25 // quality of implementation options is temporary. Eventually these
26 // options should be added to the API in future API extensions.
27
28 #ifndef HAZPTR_AMB
29 #define HAZPTR_AMB true
30 #endif
31
32 #ifndef HAZPTR_TC
33 #define HAZPTR_TC true
34 #endif
35
36 #ifndef HAZPTR_TC_SIZE
37 #define HAZPTR_TC_SIZE 10
38 #endif
39
40 #ifndef HAZPTR_PRIV
41 #define HAZPTR_PRIV true
42 #endif
43
44 #ifndef HAZPTR_ONE_DOMAIN
45 #define HAZPTR_ONE_DOMAIN false
46 #endif
47
48 #ifndef HAZPTR_SCAN_MULT
49 #define HAZPTR_SCAN_MULT 2
50 #endif
51
52 #ifndef HAZPTR_SCAN_THRESHOLD
53 #define HAZPTR_SCAN_THRESHOLD 1000
54 #endif
55
56 /* stats switch */
57 #ifndef HAZPTR_STATS
58 #define HAZPTR_STATS false
59 #endif
60
61 #include <folly/concurrency/CacheLocality.h>
62 #include <folly/experimental/AsymmetricMemoryBarrier.h>
63 #include <folly/experimental/hazptr/debug.h>
64
65 #include <mutex> // for thread caching
66 #include <unordered_set> // for hash set in bulk reclamation
67
68 namespace folly {
69 namespace hazptr {
70
71 /**
72  *  Helper classes and functions
73  */
74
75 /** hazptr_stats */
76
77 class hazptr_stats;
78
79 #if HAZPTR_STATS
80 #define INC_HAZPTR_STATS(x) hazptr_stats_.x()
81 #else
82 #define INC_HAZPTR_STATS(x)
83 #endif
84
85 /** hazptr_mb */
86
87 class hazptr_mb {
88  public:
89   static void light();
90   static void heavy();
91 };
92
93 /**
94  *  TLS structures
95  */
96
97 /** TLS life state */
98
99 enum hazptr_tls_state { TLS_ALIVE, TLS_UNINITIALIZED, TLS_DESTROYED };
100
101 /** hazptr_tc structures
102  *  Thread caching of hazptr_rec-s that belong to the default domain.
103  */
104
105 struct hazptr_tc_entry {
106   hazptr_rec* hprec_;
107
108   void fill(hazptr_rec* hprec);
109   hazptr_rec* get();
110   void evict();
111 };
112
113 static_assert(
114     std::is_trivial<hazptr_tc_entry>::value,
115     "hazptr_tc_entry must be trivial"
116     " to avoid a branch to check initialization");
117
118 struct hazptr_tc {
119   hazptr_tc_entry entry_[HAZPTR_TC_SIZE];
120   int count_;
121
122  public:
123   hazptr_rec* get();
124   bool put(hazptr_rec* hprec);
125 };
126
127 static_assert(
128     std::is_trivial<hazptr_tc>::value,
129     "hazptr_tc must be trivial to avoid a branch to check initialization");
130
131 void hazptr_tc_init();
132 void hazptr_tc_shutdown();
133 hazptr_rec* hazptr_tc_try_get();
134 bool hazptr_tc_try_put(hazptr_rec* hprec);
135
136 /** hazptr_priv structures
137  *  Thread private lists of retired objects that belong to the default domain.
138  */
139
140 struct hazptr_priv {
141   hazptr_obj* head_;
142   hazptr_obj* tail_;
143   int rcount_;
144   bool active_;
145
146   void push(hazptr_obj* obj);
147   void pushAllToDomain();
148 };
149
150 static_assert(
151     std::is_trivial<hazptr_priv>::value,
152     "hazptr_priv must be trivial to avoid a branch to check initialization");
153
154 void hazptr_priv_init();
155 void hazptr_priv_shutdown();
156 bool hazptr_priv_try_retire(hazptr_obj* obj);
157
158 /** hazptr_tls_life */
159
160 struct hazptr_tls_life {
161   hazptr_tls_life();
162   ~hazptr_tls_life();
163 };
164
165 void tls_life_odr_use();
166
167 /** tls globals */
168
169 extern thread_local hazptr_tls_state tls_state_;
170 extern thread_local hazptr_tc tls_tc_data_;
171 extern thread_local hazptr_priv tls_priv_data_;
172 extern thread_local hazptr_tls_life tls_life_; // last
173
174 /**
175  *  hazptr_domain
176  */
177
178 inline constexpr hazptr_domain::hazptr_domain(memory_resource* mr) noexcept
179     : mr_(mr) {}
180
181 /**
182  *  hazptr_obj_base
183  */
184
185 template <typename T, typename D>
186 inline void hazptr_obj_base<T, D>::retire(hazptr_domain& domain, D deleter) {
187   DEBUG_PRINT(this << " " << &domain);
188   deleter_ = std::move(deleter);
189   reclaim_ = [](hazptr_obj* p) {
190     auto hobp = static_cast<hazptr_obj_base*>(p);
191     auto obj = static_cast<T*>(hobp);
192     hobp->deleter_(obj);
193   };
194   if (HAZPTR_PRIV &&
195       (HAZPTR_ONE_DOMAIN || (&domain == &default_hazptr_domain()))) {
196     if (hazptr_priv_try_retire(this)) {
197       return;
198     }
199   }
200   domain.objRetire(this);
201 }
202
203 /**
204  *  hazptr_rec
205  */
206
207 class hazptr_rec {
208   friend class hazptr_domain;
209   friend class hazptr_holder;
210   friend struct hazptr_tc_entry;
211
212   FOLLY_ALIGN_TO_AVOID_FALSE_SHARING
213   std::atomic<const void*> hazptr_{nullptr};
214   hazptr_rec* next_{nullptr};
215   std::atomic<bool> active_{false};
216
217   void set(const void* p) noexcept;
218   const void* get() const noexcept;
219   void clear() noexcept;
220   bool isActive() noexcept;
221   bool tryAcquire() noexcept;
222   void release() noexcept;
223 };
224
225 /**
226  *  hazptr_holder
227  */
228
229 FOLLY_ALWAYS_INLINE hazptr_holder::hazptr_holder(hazptr_domain& domain) {
230   domain_ = &domain;
231   if (LIKELY(
232           HAZPTR_TC &&
233           (HAZPTR_ONE_DOMAIN || &domain == &default_hazptr_domain()))) {
234     auto hprec = hazptr_tc_try_get();
235     if (LIKELY(hprec != nullptr)) {
236       hazptr_ = hprec;
237       DEBUG_PRINT(this << " " << domain_ << " " << hazptr_);
238       return;
239     }
240   }
241   hazptr_ = domain_->hazptrAcquire();
242   DEBUG_PRINT(this << " " << domain_ << " " << hazptr_);
243   if (hazptr_ == nullptr) { std::bad_alloc e; throw e; }
244 }
245
246 FOLLY_ALWAYS_INLINE hazptr_holder::hazptr_holder(std::nullptr_t) {
247   domain_ = nullptr;
248   hazptr_ = nullptr;
249   DEBUG_PRINT(this << " " << domain_ << " " << hazptr_);
250 }
251
252 FOLLY_ALWAYS_INLINE hazptr_holder::~hazptr_holder() {
253   DEBUG_PRINT(this);
254   if (LIKELY(hazptr_ != nullptr)) {
255     hazptr_->clear();
256     if (LIKELY(
257             HAZPTR_TC &&
258             (HAZPTR_ONE_DOMAIN || domain_ == &default_hazptr_domain()))) {
259       if (LIKELY(hazptr_tc_try_put(hazptr_))) {
260         return;
261       }
262     }
263     domain_->hazptrRelease(hazptr_);
264   }
265 }
266
267 FOLLY_ALWAYS_INLINE hazptr_holder::hazptr_holder(hazptr_holder&& rhs) noexcept {
268   domain_ = rhs.domain_;
269   hazptr_ = rhs.hazptr_;
270   rhs.domain_ = nullptr;
271   rhs.hazptr_ = nullptr;
272 }
273
274 FOLLY_ALWAYS_INLINE
275 hazptr_holder& hazptr_holder::operator=(hazptr_holder&& rhs) noexcept {
276   /* Self-move is a no-op.  */
277   if (LIKELY(this != &rhs)) {
278     this->~hazptr_holder();
279     new (this) hazptr_holder(std::move(rhs));
280   }
281   return *this;
282 }
283
284 template <typename T>
285 FOLLY_ALWAYS_INLINE bool hazptr_holder::try_protect(
286     T*& ptr,
287     const std::atomic<T*>& src) noexcept {
288   return try_protect(ptr, src, [](T* t) { return t; });
289 }
290
291 template <typename T, typename Func>
292 FOLLY_ALWAYS_INLINE bool hazptr_holder::try_protect(
293     T*& ptr,
294     const std::atomic<T*>& src,
295     Func f) noexcept {
296   DEBUG_PRINT(this << " " << ptr << " " << &src);
297   reset(f(ptr));
298   /*** Full fence ***/ hazptr_mb::light();
299   T* p = src.load(std::memory_order_acquire);
300   if (UNLIKELY(p != ptr)) {
301     ptr = p;
302     reset();
303     return false;
304   }
305   return true;
306 }
307
308 template <typename T>
309 FOLLY_ALWAYS_INLINE T* hazptr_holder::get_protected(
310     const std::atomic<T*>& src) noexcept {
311   return get_protected(src, [](T* t) { return t; });
312 }
313
314 template <typename T, typename Func>
315 FOLLY_ALWAYS_INLINE T* hazptr_holder::get_protected(
316     const std::atomic<T*>& src,
317     Func f) noexcept {
318   T* p = src.load(std::memory_order_relaxed);
319   while (!try_protect(p, src, f)) {
320   }
321   DEBUG_PRINT(this << " " << p << " " << &src);
322   return p;
323 }
324
325 template <typename T>
326 FOLLY_ALWAYS_INLINE void hazptr_holder::reset(const T* ptr) noexcept {
327   auto p = static_cast<hazptr_obj*>(const_cast<T*>(ptr));
328   DEBUG_PRINT(this << " " << ptr << " p:" << p);
329   DCHECK(hazptr_); // UB if *this is empty
330   hazptr_->set(p);
331 }
332
333 FOLLY_ALWAYS_INLINE void hazptr_holder::reset(std::nullptr_t) noexcept {
334   DEBUG_PRINT(this);
335   DCHECK(hazptr_); // UB if *this is empty
336   hazptr_->clear();
337 }
338
339 FOLLY_ALWAYS_INLINE void hazptr_holder::swap(hazptr_holder& rhs) noexcept {
340   DEBUG_PRINT(
341     this << " " <<  this->hazptr_ << " " << this->domain_ << " -- "
342     << &rhs << " " << rhs.hazptr_ << " " << rhs.domain_);
343   if (!HAZPTR_ONE_DOMAIN) {
344     std::swap(this->domain_, rhs.domain_);
345   }
346   std::swap(this->hazptr_, rhs.hazptr_);
347 }
348
349 FOLLY_ALWAYS_INLINE void swap(hazptr_holder& lhs, hazptr_holder& rhs) noexcept {
350   lhs.swap(rhs);
351 }
352
353 ////////////////////////////////////////////////////////////////////////////////
354 // [TODO]:
355 // - Control of reclamation (when and by whom)
356 // - End-to-end lock-free implementation
357
358 /** Definition of default_hazptr_domain() */
359
360 FOLLY_ALWAYS_INLINE hazptr_domain& default_hazptr_domain() {
361   DEBUG_PRINT(&default_domain_);
362   return default_domain_;
363 }
364
365 /** hazptr_rec */
366
367 FOLLY_ALWAYS_INLINE void hazptr_rec::set(const void* p) noexcept {
368   DEBUG_PRINT(this << " " << p);
369   hazptr_.store(p, std::memory_order_release);
370 }
371
372 inline const void* hazptr_rec::get() const noexcept {
373   auto p = hazptr_.load(std::memory_order_acquire);
374   DEBUG_PRINT(this << " " << p);
375   return p;
376 }
377
378 FOLLY_ALWAYS_INLINE void hazptr_rec::clear() noexcept {
379   DEBUG_PRINT(this);
380   hazptr_.store(nullptr, std::memory_order_release);
381 }
382
383 inline bool hazptr_rec::isActive() noexcept {
384   return active_.load(std::memory_order_acquire);
385 }
386
387 inline bool hazptr_rec::tryAcquire() noexcept {
388   bool active = isActive();
389   if (!active &&
390       active_.compare_exchange_strong(
391           active, true, std::memory_order_release, std::memory_order_relaxed)) {
392     DEBUG_PRINT(this);
393     return true;
394   }
395   return false;
396 }
397
398 inline void hazptr_rec::release() noexcept {
399   DEBUG_PRINT(this);
400   active_.store(false, std::memory_order_release);
401 }
402
403 /** hazptr_obj */
404
405 inline const void* hazptr_obj::getObjPtr() const {
406   DEBUG_PRINT(this);
407   return this;
408 }
409
410 /** hazptr_domain */
411
412 inline hazptr_domain::~hazptr_domain() {
413   DEBUG_PRINT(this);
414   { /* reclaim all remaining retired objects */
415     hazptr_obj* next;
416     auto retired = retired_.exchange(nullptr);
417     while (retired) {
418       for (auto p = retired; p; p = next) {
419         next = p->next_;
420         (*(p->reclaim_))(p);
421       }
422       retired = retired_.exchange(nullptr);
423     }
424   }
425   /* Leak the data for the default domain to avoid destruction order
426    * issues with thread caches.
427    */
428   if (this != &default_hazptr_domain()) {
429     /* free all hazptr_rec-s */
430     hazptr_rec* next;
431     for (auto p = hazptrs_.load(std::memory_order_acquire); p; p = next) {
432       next = p->next_;
433       DCHECK(!p->isActive());
434       mr_->deallocate(static_cast<void*>(p), sizeof(hazptr_rec));
435     }
436   }
437 }
438
439 inline hazptr_rec* hazptr_domain::hazptrAcquire() {
440   hazptr_rec* p;
441   hazptr_rec* next;
442   for (p = hazptrs_.load(std::memory_order_acquire); p; p = next) {
443     next = p->next_;
444     if (p->tryAcquire()) {
445       return p;
446     }
447   }
448   p = static_cast<hazptr_rec*>(mr_->allocate(sizeof(hazptr_rec)));
449   DEBUG_PRINT(this << " " << p << " " << sizeof(hazptr_rec));
450   if (p == nullptr) {
451     return nullptr;
452   }
453   p->active_.store(true, std::memory_order_relaxed);
454   p->next_ = hazptrs_.load(std::memory_order_acquire);
455   while (!hazptrs_.compare_exchange_weak(
456       p->next_, p, std::memory_order_release, std::memory_order_acquire))
457     /* keep trying */;
458   auto hcount = hcount_.fetch_add(1);
459   DEBUG_PRINT(this << " " << p << " " << sizeof(hazptr_rec) << " " << hcount);
460   return p;
461 }
462
463 inline void hazptr_domain::hazptrRelease(hazptr_rec* p) noexcept {
464   DEBUG_PRINT(this << " " << p);
465   p->release();
466 }
467
468 inline int
469 hazptr_domain::pushRetired(hazptr_obj* head, hazptr_obj* tail, int count) {
470   /*** Full fence ***/ hazptr_mb::light();
471   tail->next_ = retired_.load(std::memory_order_acquire);
472   while (!retired_.compare_exchange_weak(
473       tail->next_,
474       head,
475       std::memory_order_release,
476       std::memory_order_acquire)) {
477   }
478   return rcount_.fetch_add(count) + count;
479 }
480
481 inline bool hazptr_domain::reachedThreshold(int rcount) {
482   return (
483       rcount >= HAZPTR_SCAN_THRESHOLD &&
484       rcount >= HAZPTR_SCAN_MULT * hcount_.load(std::memory_order_acquire));
485 }
486
487 inline void hazptr_domain::objRetire(hazptr_obj* p) {
488   auto rcount = pushRetired(p, p, 1);
489   if (reachedThreshold(rcount)) {
490     tryBulkReclaim();
491   }
492 }
493
494 inline void hazptr_domain::tryBulkReclaim() {
495   DEBUG_PRINT(this);
496   do {
497     auto hcount = hcount_.load(std::memory_order_acquire);
498     auto rcount = rcount_.load(std::memory_order_acquire);
499     if (rcount < HAZPTR_SCAN_THRESHOLD || rcount < HAZPTR_SCAN_MULT * hcount) {
500       return;
501     }
502     if (rcount_.compare_exchange_weak(
503             rcount, 0, std::memory_order_release, std::memory_order_relaxed)) {
504       break;
505     }
506   } while (true);
507   bulkReclaim();
508 }
509
510 inline void hazptr_domain::bulkReclaim() {
511   DEBUG_PRINT(this);
512   /*** Full fence ***/ hazptr_mb::heavy();
513   auto p = retired_.exchange(nullptr, std::memory_order_acquire);
514   auto h = hazptrs_.load(std::memory_order_acquire);
515   std::unordered_set<const void*> hs; // TODO lock-free alternative
516   for (; h; h = h->next_) {
517     hs.insert(h->get());
518   }
519   int rcount = 0;
520   hazptr_obj* retired = nullptr;
521   hazptr_obj* tail = nullptr;
522   hazptr_obj* next;
523   for (; p; p = next) {
524     next = p->next_;
525     if (hs.count(p->getObjPtr()) == 0) {
526       DEBUG_PRINT(this << " " << p << " " << p->reclaim_);
527       (*(p->reclaim_))(p);
528     } else {
529       p->next_ = retired;
530       retired = p;
531       if (tail == nullptr) {
532         tail = p;
533       }
534       ++rcount;
535     }
536   }
537   if (tail) {
538     pushRetired(retired, tail, rcount);
539   }
540 }
541
542 /** hazptr_stats */
543
544 class hazptr_stats {
545  public:
546   ~hazptr_stats();
547   void light();
548   void heavy();
549   void seq_cst();
550
551  private:
552   std::atomic<uint64_t> light_{0};
553   std::atomic<uint64_t> heavy_{0};
554   std::atomic<uint64_t> seq_cst_{0};
555 };
556
557 extern hazptr_stats hazptr_stats_;
558
559 inline hazptr_stats::~hazptr_stats() {
560   DEBUG_PRINT(this << " light " << light_.load());
561   DEBUG_PRINT(this << " heavy " << heavy_.load());
562   DEBUG_PRINT(this << " seq_cst " << seq_cst_.load());
563 }
564
565 FOLLY_ALWAYS_INLINE void hazptr_stats::light() {
566   if (HAZPTR_STATS) {
567     /* atomic */ ++light_;
568   }
569 }
570
571 inline void hazptr_stats::heavy() {
572   if (HAZPTR_STATS) {
573     /* atomic */ ++heavy_;
574   }
575 }
576
577 inline void hazptr_stats::seq_cst() {
578   if (HAZPTR_STATS) {
579     /* atomic */ ++seq_cst_;
580   }
581 }
582
583 /** hazptr_mb */
584
585 FOLLY_ALWAYS_INLINE void hazptr_mb::light() {
586   DEBUG_PRINT("");
587   if (HAZPTR_AMB) {
588     folly::asymmetricLightBarrier();
589     INC_HAZPTR_STATS(light);
590   } else {
591     atomic_thread_fence(std::memory_order_seq_cst);
592     INC_HAZPTR_STATS(seq_cst);
593   }
594 }
595
596 inline void hazptr_mb::heavy() {
597   DEBUG_PRINT("");
598   if (HAZPTR_AMB) {
599     folly::asymmetricHeavyBarrier(AMBFlags::EXPEDITED);
600     INC_HAZPTR_STATS(heavy);
601   } else {
602     atomic_thread_fence(std::memory_order_seq_cst);
603     INC_HAZPTR_STATS(seq_cst);
604   }
605 }
606
607 /**
608  *  TLS structures
609  */
610
611 /**
612  *  hazptr_tc structures
613  */
614
615 /** hazptr_tc_entry */
616
617 FOLLY_ALWAYS_INLINE void hazptr_tc_entry::fill(hazptr_rec* hprec) {
618   hprec_ = hprec;
619   DEBUG_PRINT(this << " " << hprec);
620 }
621
622 FOLLY_ALWAYS_INLINE hazptr_rec* hazptr_tc_entry::get() {
623   auto hprec = hprec_;
624   DEBUG_PRINT(this << " " << hprec);
625   return hprec;
626 }
627
628 inline void hazptr_tc_entry::evict() {
629   auto hprec = hprec_;
630   hprec->release();
631   DEBUG_PRINT(this << " " << hprec);
632 }
633
634 /** hazptr_tc */
635
636 FOLLY_ALWAYS_INLINE hazptr_rec* hazptr_tc::get() {
637   if (LIKELY(count_ != 0)) {
638     auto hprec = entry_[--count_].get();
639     DEBUG_PRINT(this << " " << hprec);
640     return hprec;
641   }
642   DEBUG_PRINT(this << " nullptr");
643   return nullptr;
644 }
645
646 FOLLY_ALWAYS_INLINE bool hazptr_tc::put(hazptr_rec* hprec) {
647   if (LIKELY(count_ < HAZPTR_TC_SIZE)) {
648     entry_[count_++].fill(hprec);
649     DEBUG_PRINT(this << " " << count_ - 1);
650     return true;
651   }
652   return false;
653 }
654
655 /** hazptr_tc free functions */
656
657 FOLLY_ALWAYS_INLINE hazptr_rec* hazptr_tc_try_get() {
658   DEBUG_PRINT(TLS_UNINITIALIZED << TLS_ALIVE << TLS_DESTROYED);
659   DEBUG_PRINT(tls_state_);
660   if (LIKELY(tls_state_ == TLS_ALIVE)) {
661     DEBUG_PRINT(tls_state_);
662     return tls_tc_data_.get();
663   } else if (tls_state_ == TLS_UNINITIALIZED) {
664     tls_life_odr_use();
665     return tls_tc_data_.get();
666   }
667   return nullptr;
668 }
669
670 FOLLY_ALWAYS_INLINE bool hazptr_tc_try_put(hazptr_rec* hprec) {
671   DEBUG_PRINT(tls_state_);
672   if (LIKELY(tls_state_ == TLS_ALIVE)) {
673     DEBUG_PRINT(tls_state_);
674     return tls_tc_data_.put(hprec);
675   }
676   return false;
677 }
678
679 inline void hazptr_tc_init() {
680   DEBUG_PRINT("");
681   auto& tc = tls_tc_data_;
682   DEBUG_PRINT(&tc);
683   tc.count_ = 0;
684   for (int i = 0; i < HAZPTR_TC_SIZE; ++i) {
685     tc.entry_[i].hprec_ = nullptr;
686   }
687 }
688
689 inline void hazptr_tc_shutdown() {
690   auto& tc = tls_tc_data_;
691   DEBUG_PRINT(&tc);
692   for (int i = 0; i < tc.count_; ++i) {
693     tc.entry_[i].evict();
694   }
695 }
696
697 /**
698  *  hazptr_priv
699  */
700
701 inline void hazptr_priv::push(hazptr_obj* obj) {
702   auto& domain = default_hazptr_domain();
703   obj->next_ = nullptr;
704   if (tail_) {
705     tail_->next_ = obj;
706   } else {
707     if (!active_) {
708       domain.objRetire(obj);
709       return;
710     }
711     head_ = obj;
712   }
713   tail_ = obj;
714   ++rcount_;
715   if (domain.reachedThreshold(rcount_)) {
716     pushAllToDomain();
717   }
718 }
719
720 inline void hazptr_priv::pushAllToDomain() {
721   auto& domain = default_hazptr_domain();
722   domain.pushRetired(head_, tail_, rcount_);
723   head_ = nullptr;
724   tail_ = nullptr;
725   rcount_ = 0;
726   domain.tryBulkReclaim();
727 }
728
729 inline void hazptr_priv_init() {
730   auto& priv = tls_priv_data_;
731   DEBUG_PRINT(&priv);
732   priv.head_ = nullptr;
733   priv.tail_ = nullptr;
734   priv.rcount_ = 0;
735   priv.active_ = true;
736 }
737
738 inline void hazptr_priv_shutdown() {
739   auto& priv = tls_priv_data_;
740   DEBUG_PRINT(&priv);
741   DCHECK(priv.active_);
742   priv.active_ = false;
743   if (priv.tail_) {
744     priv.pushAllToDomain();
745   }
746 }
747
748 inline bool hazptr_priv_try_retire(hazptr_obj* obj) {
749   DEBUG_PRINT(tls_state_);
750   if (tls_state_ == TLS_ALIVE) {
751     DEBUG_PRINT(tls_state_);
752     tls_priv_data_.push(obj);
753     return true;
754   } else if (tls_state_ == TLS_UNINITIALIZED) {
755     DEBUG_PRINT(tls_state_);
756     tls_life_odr_use();
757     tls_priv_data_.push(obj);
758     return true;
759   }
760   return false;
761 }
762
763 /** hazptr_tls_life */
764
765 inline void tls_life_odr_use() {
766   DEBUG_PRINT(tls_state_);
767   CHECK(tls_state_ == TLS_UNINITIALIZED);
768   auto volatile tlsOdrUse = &tls_life_;
769   CHECK(tlsOdrUse != nullptr);
770   DEBUG_PRINT(tlsOdrUse);
771 }
772
773 inline hazptr_tls_life::hazptr_tls_life() {
774   DEBUG_PRINT(this);
775   CHECK(tls_state_ == TLS_UNINITIALIZED);
776   hazptr_tc_init();
777   hazptr_priv_init();
778   tls_state_ = TLS_ALIVE;
779 }
780
781 inline hazptr_tls_life::~hazptr_tls_life() {
782   DEBUG_PRINT(this);
783   CHECK(tls_state_ == TLS_ALIVE);
784   hazptr_tc_shutdown();
785   hazptr_priv_shutdown();
786   tls_state_ = TLS_DESTROYED;
787 }
788
789 } // namespace folly
790 } // namespace hazptr