1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3 /*
4 * Ceph - scalable distributed file system
5 *
6 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
7 *
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
12 *
13 */
14
15 #ifndef CEPH_REFCOUNTEDOBJ_H
16 #define CEPH_REFCOUNTEDOBJ_H
17
18 #include "common/ceph_mutex.h"
19 #include "common/ref.h"
20
21 #include <atomic>
22
23 /* This class provides mechanisms to make a sub-class work with
24 * boost::intrusive_ptr (aka ceph::ref_t).
25 *
26 * Generally, you'll want to inherit from RefCountedObjectSafe and not from
27 * RefCountedObject directly. This is because the ::get and ::put methods are
28 * public and can be used to create/delete references outside of the
29 * ceph::ref_t pointers with the potential to leak memory.
30 *
31 * It is also suggested that you make constructors and destructors private in
32 * your final class. This prevents instantiation of the object with assignment
33 * to a raw pointer. Consequently, you'll want to use ceph::make_ref<> to
34 * create a ceph::ref_t<> holding your object:
35 *
36 * auto ptr = ceph::make_ref<Foo>(...);
37 *
38 * Use FRIEND_MAKE_REF(ClassName) to allow ceph::make_ref to call the private
39 * constructors.
40 *
41 */
42
43 class RefCountedObject {
44 public:
45 void set_cct(class CephContext *c) {
46 cct = c;
47 }
48
49 uint64_t get_nref() const {
50 return nref;
51 }
52
53 const RefCountedObject *get() const {
54 _get();
55 return this;
56 }
57 RefCountedObject *get() {
58 _get();
59 return this;
60 }
61 void put() const;
62
63 protected:
64 RefCountedObject() = default;
65 RefCountedObject(const RefCountedObject& o) : cct(o.cct) {}
66 RefCountedObject& operator=(const RefCountedObject& o) = delete;
67 RefCountedObject(RefCountedObject&&) = delete;
68 RefCountedObject& operator=(RefCountedObject&&) = delete;
69 RefCountedObject(class CephContext* c) : cct(c) {}
70
71 virtual ~RefCountedObject();
72
73 private:
74 void _get() const;
75
76 #ifndef WITH_SEASTAR
77 mutable std::atomic<uint64_t> nref{1};
78 #else
79 // crimson is single threaded at the moment
80 mutable uint64_t nref{1};
81 #endif
82 class CephContext *cct{nullptr};
83 };
84
85 class RefCountedObjectSafe : public RefCountedObject {
86 public:
87 RefCountedObject *get() = delete;
88 const RefCountedObject *get() const = delete;
89 void put() const = delete;
90 protected:
91 template<typename... Args>
92 RefCountedObjectSafe(Args&&... args) : RefCountedObject(std::forward<Args>(args)...) {}
93 virtual ~RefCountedObjectSafe() override {}
94 };
95
96 #ifndef WITH_SEASTAR
97
98 /**
99 * RefCountedCond
100 *
101 * a refcounted condition, will be removed when all references are dropped
102 */
103 struct RefCountedCond : public RefCountedObject {
104 RefCountedCond() = default;
105 ~RefCountedCond() = default;
106
107 int wait() {
108 std::unique_lock l(lock);
109 while (!complete) {
110 cond.wait(l);
111 }
112 return rval;
113 }
114
115 void done(int r) {
116 std::lock_guard l(lock);
117 rval = r;
118 complete = true;
119 cond.notify_all();
120 }
121
122 void done() {
123 done(0);
124 }
125
126 private:
127 bool complete = false;
128 ceph::mutex lock = ceph::make_mutex("RefCountedCond::lock");
129 ceph::condition_variable cond;
130 int rval = 0;
131 };
132
133 /**
134 * RefCountedWaitObject
135 *
136 * refcounted object that allows waiting for the object's last reference.
137 * Any referrer can either put or put_wait(). A simple put() will return
138 * immediately, a put_wait() will return only when the object is destroyed.
139 * e.g., useful when we want to wait for a specific event completion. We
140 * use RefCountedCond, as the condition can be referenced after the object
141 * destruction.
142 *
143 */
144 struct RefCountedWaitObject {
145 std::atomic<uint64_t> nref = { 1 };
146 RefCountedCond *c;
147
148 RefCountedWaitObject() {
149 c = new RefCountedCond;
150 }
(1) Event exn_spec_violation: |
An exception of type "std::length_error" is thrown but the throw list "throw()" doesn't allow it to be thrown. This will cause a call to unexpected() which usually calls terminate(). |
Also see events: |
[fun_call_w_exception] |
151 virtual ~RefCountedWaitObject() {
(2) Event fun_call_w_exception: |
Called function throws an exception of type "std::length_error". [details] |
Also see events: |
[exn_spec_violation] |
152 c->put();
153 }
154
155 RefCountedWaitObject *get() {
156 nref++;
157 return this;
158 }
159
160 bool put() {
161 bool ret = false;
162 RefCountedCond *cond = c;
163 cond->get();
164 if (--nref == 0) {
165 cond->done();
166 delete this;
167 ret = true;
168 }
169 cond->put();
170 return ret;
171 }
172
173 void put_wait() {
174 RefCountedCond *cond = c;
175
176 cond->get();
177 if (--nref == 0) {
178 cond->done();
179 delete this;
180 } else {
181 cond->wait();
182 }
183 cond->put();
184 }
185 };
186
187 #endif // WITH_SEASTAR
188
189 static inline void intrusive_ptr_add_ref(const RefCountedObject *p) {
190 p->get();
191 }
192 static inline void intrusive_ptr_release(const RefCountedObject *p) {
193 p->put();
194 }
195
196 using RefCountedPtr = ceph::ref_t<RefCountedObject>;
197
198 #endif
199