SharingPtr.h revision 355940
1//===---------------------SharingPtr.h --------------------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#ifndef utility_SharingPtr_h_
10#define utility_SharingPtr_h_
11
12#include <memory>
13
14// Microsoft Visual C++ currently does not enable std::atomic to work in CLR
15// mode - as such we need to "hack around it" for MSVC++ builds only using
16// Windows specific intrinsics instead of the C++11 atomic support
17#ifdef _MSC_VER
18#include <intrin.h>
19#else
20#include <atomic>
21#endif
22
23#include <stddef.h>
24
25
26//#define ENABLE_SP_LOGGING 1 // DON'T CHECK THIS LINE IN UNLESS COMMENTED OUT
27#if defined(ENABLE_SP_LOGGING)
28
29extern "C" void track_sp(void *sp_this, void *ptr, long count);
30
31#endif
32
33namespace lldb_private {
34
35namespace imp {
36
37class shared_count {
38  shared_count(const shared_count &) = delete;
39  shared_count &operator=(const shared_count &) = delete;
40
41public:
42  explicit shared_count(long refs = 0) : shared_owners_(refs) {}
43
44  void add_shared();
45  void release_shared();
46  long use_count() const { return shared_owners_ + 1; }
47
48protected:
49#ifdef _MSC_VER
50  long shared_owners_;
51#else
52  std::atomic<long> shared_owners_;
53#endif
54  virtual ~shared_count();
55
56private:
57  virtual void on_zero_shared() = 0;
58};
59
60template <class T> class shared_ptr_pointer : public shared_count {
61  T data_;
62
63public:
64  shared_ptr_pointer(T p) : data_(p) {}
65
66private:
67  void on_zero_shared() override;
68
69  shared_ptr_pointer(const shared_ptr_pointer &) = delete;
70  shared_ptr_pointer &operator=(const shared_ptr_pointer &) = delete;
71};
72
73template <class T> void shared_ptr_pointer<T>::on_zero_shared() {
74  delete data_;
75}
76
77template <class T> class shared_ptr_emplace : public shared_count {
78  T data_;
79
80public:
81  shared_ptr_emplace() : data_() {}
82
83  template <class A0> shared_ptr_emplace(A0 &a0) : data_(a0) {}
84
85  template <class A0, class A1>
86  shared_ptr_emplace(A0 &a0, A1 &a1) : data_(a0, a1) {}
87
88  template <class A0, class A1, class A2>
89  shared_ptr_emplace(A0 &a0, A1 &a1, A2 &a2) : data_(a0, a1, a2) {}
90
91  template <class A0, class A1, class A2, class A3>
92  shared_ptr_emplace(A0 &a0, A1 &a1, A2 &a2, A3 &a3) : data_(a0, a1, a2, a3) {}
93
94  template <class A0, class A1, class A2, class A3, class A4>
95  shared_ptr_emplace(A0 &a0, A1 &a1, A2 &a2, A3 &a3, A4 &a4)
96      : data_(a0, a1, a2, a3, a4) {}
97
98private:
99  void on_zero_shared() override;
100
101public:
102  T *get() { return &data_; }
103};
104
105template <class T> void shared_ptr_emplace<T>::on_zero_shared() {}
106
107} // namespace imp
108
109template <class T> class SharingPtr {
110public:
111  typedef T element_type;
112
113private:
114  element_type *ptr_;
115  imp::shared_count *cntrl_;
116
117  struct nat {
118    int for_bool_;
119  };
120
121public:
122  SharingPtr();
123  SharingPtr(std::nullptr_t);
124  template <class Y> explicit SharingPtr(Y *p);
125  template <class Y> explicit SharingPtr(Y *p, imp::shared_count *ctrl_block);
126  template <class Y> SharingPtr(const SharingPtr<Y> &r, element_type *p);
127  SharingPtr(const SharingPtr &r);
128  template <class Y> SharingPtr(const SharingPtr<Y> &r);
129
130  ~SharingPtr();
131
132  SharingPtr &operator=(const SharingPtr &r);
133  template <class Y> SharingPtr &operator=(const SharingPtr<Y> &r);
134
135  void swap(SharingPtr &r);
136  void reset();
137  template <class Y> void reset(Y *p);
138  void reset(std::nullptr_t);
139
140  element_type *get() const { return ptr_; }
141  element_type &operator*() const { return *ptr_; }
142  element_type *operator->() const { return ptr_; }
143  long use_count() const { return cntrl_ ? cntrl_->use_count() : 0; }
144  bool unique() const { return use_count() == 1; }
145  bool empty() const { return cntrl_ == nullptr; }
146  operator nat *() const { return (nat *)get(); }
147
148  static SharingPtr<T> make_shared();
149
150  template <class A0> static SharingPtr<T> make_shared(A0 &);
151
152  template <class A0, class A1> static SharingPtr<T> make_shared(A0 &, A1 &);
153
154  template <class A0, class A1, class A2>
155  static SharingPtr<T> make_shared(A0 &, A1 &, A2 &);
156
157  template <class A0, class A1, class A2, class A3>
158  static SharingPtr<T> make_shared(A0 &, A1 &, A2 &, A3 &);
159
160  template <class A0, class A1, class A2, class A3, class A4>
161  static SharingPtr<T> make_shared(A0 &, A1 &, A2 &, A3 &, A4 &);
162
163private:
164  template <class U> friend class SharingPtr;
165};
166
167template <class T>
168inline SharingPtr<T>::SharingPtr() : ptr_(nullptr), cntrl_(nullptr) {}
169
170template <class T>
171inline SharingPtr<T>::SharingPtr(std::nullptr_t)
172    : ptr_(nullptr), cntrl_(nullptr) {}
173
174template <class T>
175template <class Y>
176SharingPtr<T>::SharingPtr(Y *p) : ptr_(p), cntrl_(nullptr) {
177  std::unique_ptr<Y> hold(p);
178  typedef imp::shared_ptr_pointer<Y *> _CntrlBlk;
179  cntrl_ = new _CntrlBlk(p);
180  hold.release();
181}
182
183template <class T>
184template <class Y>
185SharingPtr<T>::SharingPtr(Y *p, imp::shared_count *cntrl_block)
186    : ptr_(p), cntrl_(cntrl_block) {}
187
188template <class T>
189template <class Y>
190inline SharingPtr<T>::SharingPtr(const SharingPtr<Y> &r, element_type *p)
191    : ptr_(p), cntrl_(r.cntrl_) {
192  if (cntrl_)
193    cntrl_->add_shared();
194}
195
196template <class T>
197inline SharingPtr<T>::SharingPtr(const SharingPtr &r)
198    : ptr_(r.ptr_), cntrl_(r.cntrl_) {
199  if (cntrl_)
200    cntrl_->add_shared();
201}
202
203template <class T>
204template <class Y>
205inline SharingPtr<T>::SharingPtr(const SharingPtr<Y> &r)
206    : ptr_(r.ptr_), cntrl_(r.cntrl_) {
207  if (cntrl_)
208    cntrl_->add_shared();
209}
210
211template <class T> SharingPtr<T>::~SharingPtr() {
212  if (cntrl_)
213    cntrl_->release_shared();
214}
215
216template <class T>
217inline SharingPtr<T> &SharingPtr<T>::operator=(const SharingPtr &r) {
218  SharingPtr(r).swap(*this);
219  return *this;
220}
221
222template <class T>
223template <class Y>
224inline SharingPtr<T> &SharingPtr<T>::operator=(const SharingPtr<Y> &r) {
225  SharingPtr(r).swap(*this);
226  return *this;
227}
228
229template <class T> inline void SharingPtr<T>::swap(SharingPtr &r) {
230  std::swap(ptr_, r.ptr_);
231  std::swap(cntrl_, r.cntrl_);
232}
233
234template <class T> inline void SharingPtr<T>::reset() {
235  SharingPtr().swap(*this);
236}
237
238template <class T> inline void SharingPtr<T>::reset(std::nullptr_t p) {
239  reset();
240}
241
242template <class T> template <class Y> inline void SharingPtr<T>::reset(Y *p) {
243  SharingPtr(p).swap(*this);
244}
245
246template <class T> SharingPtr<T> SharingPtr<T>::make_shared() {
247  typedef imp::shared_ptr_emplace<T> CntrlBlk;
248  SharingPtr<T> r;
249  r.cntrl_ = new CntrlBlk();
250  r.ptr_ = static_cast<CntrlBlk *>(r.cntrl_)->get();
251  return r;
252}
253
254template <class T>
255template <class A0>
256SharingPtr<T> SharingPtr<T>::make_shared(A0 &a0) {
257  typedef imp::shared_ptr_emplace<T> CntrlBlk;
258  SharingPtr<T> r;
259  r.cntrl_ = new CntrlBlk(a0);
260  r.ptr_ = static_cast<CntrlBlk *>(r.cntrl_)->get();
261  return r;
262}
263
264template <class T>
265template <class A0, class A1>
266SharingPtr<T> SharingPtr<T>::make_shared(A0 &a0, A1 &a1) {
267  typedef imp::shared_ptr_emplace<T> CntrlBlk;
268  SharingPtr<T> r;
269  r.cntrl_ = new CntrlBlk(a0, a1);
270  r.ptr_ = static_cast<CntrlBlk *>(r.cntrl_)->get();
271  return r;
272}
273
274template <class T>
275template <class A0, class A1, class A2>
276SharingPtr<T> SharingPtr<T>::make_shared(A0 &a0, A1 &a1, A2 &a2) {
277  typedef imp::shared_ptr_emplace<T> CntrlBlk;
278  SharingPtr<T> r;
279  r.cntrl_ = new CntrlBlk(a0, a1, a2);
280  r.ptr_ = static_cast<CntrlBlk *>(r.cntrl_)->get();
281  return r;
282}
283
284template <class T>
285template <class A0, class A1, class A2, class A3>
286SharingPtr<T> SharingPtr<T>::make_shared(A0 &a0, A1 &a1, A2 &a2, A3 &a3) {
287  typedef imp::shared_ptr_emplace<T> CntrlBlk;
288  SharingPtr<T> r;
289  r.cntrl_ = new CntrlBlk(a0, a1, a2, a3);
290  r.ptr_ = static_cast<CntrlBlk *>(r.cntrl_)->get();
291  return r;
292}
293
294template <class T>
295template <class A0, class A1, class A2, class A3, class A4>
296SharingPtr<T> SharingPtr<T>::make_shared(A0 &a0, A1 &a1, A2 &a2, A3 &a3,
297                                         A4 &a4) {
298  typedef imp::shared_ptr_emplace<T> CntrlBlk;
299  SharingPtr<T> r;
300  r.cntrl_ = new CntrlBlk(a0, a1, a2, a3, a4);
301  r.ptr_ = static_cast<CntrlBlk *>(r.cntrl_)->get();
302  return r;
303}
304
305template <class T> inline SharingPtr<T> make_shared() {
306  return SharingPtr<T>::make_shared();
307}
308
309template <class T, class A0> inline SharingPtr<T> make_shared(A0 &a0) {
310  return SharingPtr<T>::make_shared(a0);
311}
312
313template <class T, class A0, class A1>
314inline SharingPtr<T> make_shared(A0 &a0, A1 &a1) {
315  return SharingPtr<T>::make_shared(a0, a1);
316}
317
318template <class T, class A0, class A1, class A2>
319inline SharingPtr<T> make_shared(A0 &a0, A1 &a1, A2 &a2) {
320  return SharingPtr<T>::make_shared(a0, a1, a2);
321}
322
323template <class T, class A0, class A1, class A2, class A3>
324inline SharingPtr<T> make_shared(A0 &a0, A1 &a1, A2 &a2, A3 &a3) {
325  return SharingPtr<T>::make_shared(a0, a1, a2, a3);
326}
327
328template <class T, class A0, class A1, class A2, class A3, class A4>
329inline SharingPtr<T> make_shared(A0 &a0, A1 &a1, A2 &a2, A3 &a3, A4 &a4) {
330  return SharingPtr<T>::make_shared(a0, a1, a2, a3, a4);
331}
332
333template <class T, class U>
334inline bool operator==(const SharingPtr<T> &__x, const SharingPtr<U> &__y) {
335  return __x.get() == __y.get();
336}
337
338template <class T, class U>
339inline bool operator!=(const SharingPtr<T> &__x, const SharingPtr<U> &__y) {
340  return !(__x == __y);
341}
342
343template <class T, class U>
344inline bool operator<(const SharingPtr<T> &__x, const SharingPtr<U> &__y) {
345  return __x.get() < __y.get();
346}
347
348template <class T> inline void swap(SharingPtr<T> &__x, SharingPtr<T> &__y) {
349  __x.swap(__y);
350}
351
352template <class T, class U>
353inline SharingPtr<T> static_pointer_cast(const SharingPtr<U> &r) {
354  return SharingPtr<T>(r, static_cast<T *>(r.get()));
355}
356
357template <class T, class U>
358SharingPtr<T> const_pointer_cast(const SharingPtr<U> &r) {
359  return SharingPtr<T>(r, const_cast<T *>(r.get()));
360}
361
362template <class T> class LoggingSharingPtr : public SharingPtr<T> {
363  typedef SharingPtr<T> base;
364
365public:
366  typedef void (*Callback)(void *, const LoggingSharingPtr &, bool action);
367  // action:  false means increment just happened
368  //          true  means decrement is about to happen
369
370  LoggingSharingPtr() : cb_(0), baton_(nullptr) {}
371
372  LoggingSharingPtr(Callback cb, void *baton) : cb_(cb), baton_(baton) {
373    if (cb_)
374      cb_(baton_, *this, false);
375  }
376
377  template <class Y>
378  LoggingSharingPtr(Y *p) : base(p), cb_(0), baton_(nullptr) {}
379
380  template <class Y>
381  LoggingSharingPtr(Y *p, Callback cb, void *baton)
382      : base(p), cb_(cb), baton_(baton) {
383    if (cb_)
384      cb_(baton_, *this, false);
385  }
386
387  ~LoggingSharingPtr() {
388    if (cb_)
389      cb_(baton_, *this, true);
390  }
391
392  LoggingSharingPtr(const LoggingSharingPtr &p)
393      : base(p), cb_(p.cb_), baton_(p.baton_) {
394    if (cb_)
395      cb_(baton_, *this, false);
396  }
397
398  LoggingSharingPtr &operator=(const LoggingSharingPtr &p) {
399    if (cb_)
400      cb_(baton_, *this, true);
401    base::operator=(p);
402    cb_ = p.cb_;
403    baton_ = p.baton_;
404    if (cb_)
405      cb_(baton_, *this, false);
406    return *this;
407  }
408
409  void reset() {
410    if (cb_)
411      cb_(baton_, *this, true);
412    base::reset();
413  }
414
415  template <class Y> void reset(Y *p) {
416    if (cb_)
417      cb_(baton_, *this, true);
418    base::reset(p);
419    if (cb_)
420      cb_(baton_, *this, false);
421  }
422
423  void SetCallback(Callback cb, void *baton) {
424    cb_ = cb;
425    baton_ = baton;
426  }
427
428  void ClearCallback() {
429    cb_ = 0;
430    baton_ = 0;
431  }
432
433private:
434  Callback cb_;
435  void *baton_;
436};
437
438template <class T> class IntrusiveSharingPtr;
439
440template <class T> class ReferenceCountedBase {
441public:
442  explicit ReferenceCountedBase() : shared_owners_(-1) {}
443
444  void add_shared();
445
446  void release_shared();
447
448  long use_count() const { return shared_owners_ + 1; }
449
450protected:
451  long shared_owners_;
452
453  friend class IntrusiveSharingPtr<T>;
454
455private:
456  ReferenceCountedBase(const ReferenceCountedBase &) = delete;
457  ReferenceCountedBase &operator=(const ReferenceCountedBase &) = delete;
458};
459
460template <class T> void lldb_private::ReferenceCountedBase<T>::add_shared() {
461#ifdef _MSC_VER
462  _InterlockedIncrement(&shared_owners_);
463#else
464  ++shared_owners_;
465#endif
466}
467
468template <class T>
469void lldb_private::ReferenceCountedBase<T>::release_shared() {
470#ifdef _MSC_VER
471  if (_InterlockedDecrement(&shared_owners_) == -1)
472#else
473  if (--shared_owners_ == -1)
474#endif
475    delete static_cast<T *>(this);
476}
477
478template <class T>
479class ReferenceCountedBaseVirtual : public imp::shared_count {
480public:
481  explicit ReferenceCountedBaseVirtual() : imp::shared_count(-1) {}
482
483  ~ReferenceCountedBaseVirtual() override = default;
484
485  void on_zero_shared() override;
486};
487
488template <class T> void ReferenceCountedBaseVirtual<T>::on_zero_shared() {}
489
490template <typename T> class IntrusiveSharingPtr {
491public:
492  typedef T element_type;
493
494  explicit IntrusiveSharingPtr() : ptr_(0) {}
495
496  explicit IntrusiveSharingPtr(T *ptr) : ptr_(ptr) { add_shared(); }
497
498  IntrusiveSharingPtr(const IntrusiveSharingPtr &rhs) : ptr_(rhs.ptr_) {
499    add_shared();
500  }
501
502  template <class X>
503  IntrusiveSharingPtr(const IntrusiveSharingPtr<X> &rhs) : ptr_(rhs.get()) {
504    add_shared();
505  }
506
507  IntrusiveSharingPtr &operator=(const IntrusiveSharingPtr &rhs) {
508    reset(rhs.get());
509    return *this;
510  }
511
512  template <class X>
513  IntrusiveSharingPtr &operator=(const IntrusiveSharingPtr<X> &rhs) {
514    reset(rhs.get());
515    return *this;
516  }
517
518  IntrusiveSharingPtr &operator=(T *ptr) {
519    reset(ptr);
520    return *this;
521  }
522
523  ~IntrusiveSharingPtr() {
524    release_shared();
525    ptr_ = nullptr;
526  }
527
528  T &operator*() const { return *ptr_; }
529
530  T *operator->() const { return ptr_; }
531
532  T *get() const { return ptr_; }
533
534  explicit operator bool() const { return ptr_ != 0; }
535
536  void swap(IntrusiveSharingPtr &rhs) {
537    std::swap(ptr_, rhs.ptr_);
538#if defined(ENABLE_SP_LOGGING)
539    track_sp(this, ptr_, use_count());
540    track_sp(&rhs, rhs.ptr_, rhs.use_count());
541#endif
542  }
543
544  void reset(T *ptr = nullptr) { IntrusiveSharingPtr(ptr).swap(*this); }
545
546  long use_count() const {
547    if (ptr_)
548      return ptr_->use_count();
549    return 0;
550  }
551
552  bool unique() const { return use_count() == 1; }
553
554private:
555  element_type *ptr_;
556
557  void add_shared() {
558    if (ptr_) {
559      ptr_->add_shared();
560#if defined(ENABLE_SP_LOGGING)
561      track_sp(this, ptr_, ptr_->use_count());
562#endif
563    }
564  }
565  void release_shared() {
566    if (ptr_) {
567#if defined(ENABLE_SP_LOGGING)
568      track_sp(this, nullptr, ptr_->use_count() - 1);
569#endif
570      ptr_->release_shared();
571    }
572  }
573};
574
575template <class T, class U>
576inline bool operator==(const IntrusiveSharingPtr<T> &lhs,
577                       const IntrusiveSharingPtr<U> &rhs) {
578  return lhs.get() == rhs.get();
579}
580
581template <class T, class U>
582inline bool operator!=(const IntrusiveSharingPtr<T> &lhs,
583                       const IntrusiveSharingPtr<U> &rhs) {
584  return lhs.get() != rhs.get();
585}
586
587template <class T, class U>
588inline bool operator==(const IntrusiveSharingPtr<T> &lhs, U *rhs) {
589  return lhs.get() == rhs;
590}
591
592template <class T, class U>
593inline bool operator!=(const IntrusiveSharingPtr<T> &lhs, U *rhs) {
594  return lhs.get() != rhs;
595}
596
597template <class T, class U>
598inline bool operator==(T *lhs, const IntrusiveSharingPtr<U> &rhs) {
599  return lhs == rhs.get();
600}
601
602template <class T, class U>
603inline bool operator!=(T *lhs, const IntrusiveSharingPtr<U> &rhs) {
604  return lhs != rhs.get();
605}
606
607} // namespace lldb_private
608
609#endif // utility_SharingPtr_h_
610