-
Notifications
You must be signed in to change notification settings - Fork 0
/
sharedobj.h
304 lines (258 loc) · 9.73 KB
/
sharedobj.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
/*
Copyright (C) 2017,2018 Blaise Dias
sharedobj is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with sharedobj. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef BENEDIAS_SHARED_OBJ_H
#define BENEDIAS_SHARED_OBJ_H
#include <atomic>
#include <utility>
//#define SHARED_OBJ_DEBUG_TRACE
#ifdef SHARED_OBJ_DEBUG_TRACE
#include <iostream>
#ifndef sharedobj_traceout
#define sharedobj_traceout std::cout
#endif
#endif
namespace benedias {
//sharedobj_ptr class template implements a subset of std::shared_ptr functionality,
//for classes subclassing the sharedobj (see below).
//The only advantages of this class over std::shared_ptr is the size of
//sharedobj_ptr, which is the same as that of a pointer, and possibly its simplicity.
//The reference counter in the sharedobj class template is atomic, and
//sharedobj methods are thread safe.
//However this is not true for sharedobj_ptr, this is intentional.
//There is no need to share sharedobj_ptr instances between threads,
//creating new instances is cheap in terms of size (the primary motive for the
//existence of sharedobj_ptr) and not too expensive in terms of execution.
//sharedobj_ptr should be passed between threads by copying,
//i.e. make a copy and pass that to another thread.
//Q: What about models where a central shared repository of sharedobj_ptrs
//is used?
// @brief A smart pointer to classes derived from sharedobj or implementing
// the sharedobj interface, with reference-counted copy semantics.
//
// The object pointed to is deleted when there are no sharedobj_ptr pointing to it.
template <typename T> class sharedobj_ptr
{
private:
T* real_ptr = nullptr;
inline T* acquire() const
{
if (real_ptr)
{
real_ptr->sharedobj_acquire();
return real_ptr;
}
return nullptr;
}
inline void release()
{
if (real_ptr)
{
if (real_ptr->sharedobj_release())
{
#ifdef SHARED_OBJ_DEBUG_TRACE
sharedobj_traceout << "deleting " << real_ptr << " @release" << std::endl;
#endif
delete real_ptr;
}
real_ptr = nullptr;
}
}
T* get_n_zap()
{
T* tmp = real_ptr;
real_ptr = nullptr;
return tmp;
}
public:
//(constructor)
// @brief Consructor an empty sharedobj_ptr
sharedobj_ptr()
{
}
// @brief Consructor an sharedobj_ptr instance that shares ownership with @a sp_other.
sharedobj_ptr(const sharedobj_ptr<T>& sp_other)
{
real_ptr = sp_other.acquire();
}
// @brief Consructor an sharedobj_ptr instance that takes ownership from @a sp_other.
sharedobj_ptr(sharedobj_ptr<T>&& sp_other)
{
// using std::move this breaks! sp_other.real_ptr is not cleared
// real_ptr = std::move(sp_other.real_ptr);
// adding this line below fixes it.
// sp_other.real_ptr = nullptr;
// real_ptr = sp_other.acquire();
// sp_other.real_ptr = sp_other.release();
real_ptr = sp_other.get_n_zap();
}
// @brief Consructor an sharedobj_ptr instance that points to @a ptr.
sharedobj_ptr(T* ptr)
{
if (ptr)
{
ptr->sharedobj_acquire();
real_ptr = ptr;
}
}
//(destructor)
//@brief If this instance owns an object and it is the last sharedobj_ptr owning it,
//the object is destroyed.
// After the destruction, the smart pointers that shared ownership with this instance,
// if any, will report a use_count() that is one less than its previous value.
~sharedobj_ptr()
{
release();
}
//@brief return true if the sharedobj_ptr instance is pointing to an object.
explicit operator bool() const noexcept
{
return real_ptr != nullptr;
}
//Dereferencing operators
//@brief Dereferences the stored pointer. The behavior is undefined if the stored pointer is null.
T* operator->() const noexcept
{
return real_ptr;
}
//@brief Dereferences the stored pointer. The behavior is undefined if the stored pointer is null.
T operator*() const noexcept
{
return *real_ptr;
}
//Assignment operators
//@brief Replaces the managed object with the one managed by sp_other.
//If this instance owns an object, destructor semantics apply.
// After the assignment, the smart pointers that shared ownership with this instance,
// if any, will report a use_count() that is one more than its previous value.
void operator=(const sharedobj_ptr<T>& sp_other)
{
release();
real_ptr = sp_other.acquire();
}
//@brief Move-assigns the managed object with the one managed by @a sp_other.
// If this instance owns an object, destructor semantics apply.
// After the assignment, the smart pointers that shared ownership with this instance,
// if any, will report a use_count() that is the same as its previous value.
void operator=(sharedobj_ptr<T>&& sp_other)
{
release();
// using std::move this breaks! sp_other.real_ptr is not cleared
// real_ptr = std::move(sp_other.real_ptr);
// real_ptr = sp_other.acquire();
// sp_other.real_ptr = sp_other.release();
real_ptr = sp_other.get_n_zap();
}
// Equality operators
//@brief Returns true if the managed object of @a sp_other is the
// same as the managed object of this instance.
bool operator==(const sharedobj_ptr<T>& sp_other) const
{
return real_ptr == sp_other.real_ptr;
}
//@brief Returns true if the managed object of @a sp_other is not
// the same as the managed object of this instance.
bool operator!=(const sharedobj_ptr<T>& sp_other) const
{
return real_ptr != sp_other.real_ptr;
}
//@brief Returns true if the pointer value of the managed object of
// @a sp_other is less then pointer value to the managed object of this
// instance.
bool operator<(const sharedobj_ptr<T>& sp_other) const
{
return real_ptr < sp_other.real_ptr;
}
// Utility
//@brief Returns the pointer to the managed object.
T* get() const
{
return real_ptr;
}
//@brief Returns the pointer value to this instance.
const void *self() const
{
return this;
}
//@brief Returns the number of sharedobj_ptr instances referring to
// the same object.
unsigned int use_count() const
{
return real_ptr->sharedobj_use_count();
}
};
//sharedobj class template implements an interface required by sharedobj_ptr implementation,
//for reference counting the "liveness" of an object.
template <typename T> class sharedobj {
private:
protected:
friend sharedobj_ptr<T>;
std::atomic_uint refcount;
//@brief atomically increment the use count of the shared object.
void sharedobj_acquire()
{
++refcount;
#ifdef SHARED_OBJ_DEBUG_TRACE
sharedobj_traceout << "sharedobj_acquire " << this << " " << refcount << std::endl;
#endif
}
//@brief atomically decrement the use count of the shared object.
//@return true if the instance should be destroyed.
bool sharedobj_release()
{
#ifdef SHARED_OBJ_DEBUG_TRACE
sharedobj_traceout << "sharedobj_release " << this << " " << refcount << std::endl;
#endif
return 0 == --refcount;
}
//@brief Returns the number of sharedobj_ptr instances referring to
// the same object.
unsigned int sharedobj_use_count() const
{
return refcount.load();
}
//@brief Constructor of the sharedobj, use count is set to 0.
sharedobj():refcount(0)
{
#ifdef SHARED_OBJ_DEBUG_TRACE
sharedobj_traceout << "CTOR sharedobj " << this << " " << refcount << std::endl;
#endif
}
//@brief Destructor of the sharedobj, use count must be 0.
virtual ~sharedobj()
{
#ifdef SHARED_OBJ_DEBUG_TRACE
sharedobj_traceout << "DTOR sharedobj " << this << " " << refcount << std::endl;
#endif
}
};
//@brief Allocates and constructs and object of type @a T and returns a
//object of type @a sharedobj_ptr<T>.
template <typename T, typename... Args> sharedobj_ptr<T> make_sharedobj(Args&&... args)
{
T* ptr = ::new T(std::forward<Args>(args)...);
return sharedobj_ptr<T>(ptr);
}
} //namespace benedias
namespace std
{
template <typename T>
struct hash<benedias::sharedobj_ptr<T>>
{
size_t operator()(benedias::sharedobj_ptr<T>& rcp) const
{
return std::hash<T>()(*rcp.get());
}
};
} //namespace std
#endif // #define BENEDIAS_SHARED_OBJ_H