Skip to content

Commit ef5c6f0

Browse files
authored
Merge pull request #1742 from owen-jones-diffblue/owen-jones-diffblue/small-shared-ptr
Owen jones diffblue/small shared ptr
2 parents 8360233 + 7a0de46 commit ef5c6f0

File tree

4 files changed

+550
-297
lines changed

4 files changed

+550
-297
lines changed

src/util/cow.h

+254
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
/*******************************************************************\
2+
3+
Module: Copy on write class
4+
5+
Author: Reuben Thomas, [email protected]
6+
7+
\*******************************************************************/
8+
9+
#ifndef CPROVER_UTIL_COW_H
10+
#define CPROVER_UTIL_COW_H
11+
12+
#include "invariant.h"
13+
#include "small_shared_ptr.h"
14+
15+
#include <limits>
16+
17+
/// A utility class for writing types with copy-on-write behaviour (like irep).
18+
/// This is a thin wrapper over a shared pointer, but instead of a single
19+
/// getter for the pointer value, we supply a separate 'read' and 'write'
20+
/// method. 'read' returns a reference to the owned object, while 'write'
21+
/// will create a copy of the owned object if more than one copy_on_write
22+
/// instance points to it.
23+
template <typename T>
24+
class copy_on_writet final
25+
{
26+
public:
27+
// Note that this *is* the default constructor. An invariant of a
28+
// copy-on-write object is that it is never null.
29+
template <typename... Ts>
30+
explicit copy_on_writet(Ts &&... ts)
31+
: t_(make_small_shared_ptr<T>(std::forward<Ts>(ts)...))
32+
{
33+
INVARIANT(
34+
pointee_is_shareable(*t_),
35+
"newly-constructed COW pointers must be shareable");
36+
}
37+
38+
copy_on_writet(const copy_on_writet &rhs)
39+
: t_(
40+
pointee_is_shareable(*rhs.t_) ? rhs.t_
41+
: make_small_shared_ptr<T>(*rhs.t_))
42+
{
43+
INVARIANT(
44+
pointee_is_shareable(*t_),
45+
"newly-constructed COW pointers must be shareable");
46+
}
47+
48+
copy_on_writet &operator=(const copy_on_writet &rhs)
49+
{
50+
auto copy(rhs);
51+
swap(copy);
52+
return *this;
53+
}
54+
55+
copy_on_writet(copy_on_writet &&rhs)
56+
{
57+
swap(rhs);
58+
}
59+
60+
copy_on_writet &operator=(copy_on_writet &&rhs)
61+
{
62+
swap(rhs);
63+
return *this;
64+
}
65+
66+
void swap(copy_on_writet &rhs)
67+
{
68+
std::swap(t_, rhs.t_);
69+
}
70+
71+
const T &read() const
72+
{
73+
return *t_;
74+
}
75+
76+
T &write(bool mark_shareable)
77+
{
78+
if(pointee_use_count(*t_) != 1)
79+
{
80+
t_ = make_small_shared_ptr<T>(*t_);
81+
}
82+
INVARIANT(
83+
pointee_use_count(*t_) == 1,
84+
"mutable references to a COW pointer must be unique");
85+
pointee_set_shareable(*t_, mark_shareable);
86+
return *t_;
87+
}
88+
89+
// Ideally these would be non-members, but they depend on private member t_
90+
91+
template <typename U>
92+
bool operator==(const copy_on_writet<U> &rhs) const
93+
{
94+
return t_ == rhs.t_;
95+
}
96+
97+
template <typename U>
98+
bool operator!=(const copy_on_writet<U> &rhs) const
99+
{
100+
return t_ != rhs.t_;
101+
}
102+
103+
template <typename U>
104+
bool operator<(const copy_on_writet<U> &rhs) const
105+
{
106+
return t_ < rhs.t_;
107+
}
108+
109+
template <typename U>
110+
bool operator>(const copy_on_writet<U> &rhs) const
111+
{
112+
return t_ > rhs.t_;
113+
}
114+
115+
template <typename U>
116+
bool operator<=(const copy_on_writet<U> &rhs) const
117+
{
118+
return t_ <= rhs.t_;
119+
}
120+
121+
template <typename U>
122+
bool operator>=(const copy_on_writet<U> &rhs) const
123+
{
124+
return t_ >= rhs.t_;
125+
}
126+
127+
private:
128+
small_shared_ptrt<T> t_;
129+
};
130+
131+
////////////////////////////////////////////////////////////////////////////////
132+
133+
/// A helper class to store use-counts of copy-on-write objects.
134+
/// The suggested usage pattern is to have copy-on-write data types inherit
135+
/// from this class, and then to access them through a copy_on_writet.
136+
/// \tparam Num some numeric type, used to store a reference count
137+
template <typename Num>
138+
class copy_on_write_pointeet
139+
{
140+
public:
141+
copy_on_write_pointeet() = default;
142+
143+
copy_on_write_pointeet(const copy_on_write_pointeet &)
144+
{
145+
}
146+
147+
copy_on_write_pointeet &operator=(const copy_on_write_pointeet &)
148+
{
149+
return *this;
150+
}
151+
152+
copy_on_write_pointeet(copy_on_write_pointeet &&)
153+
{
154+
}
155+
156+
copy_on_write_pointeet &operator=(copy_on_write_pointeet &&)
157+
{
158+
return *this;
159+
}
160+
161+
void increment_use_count()
162+
{
163+
INVARIANT(
164+
is_shareable(),
165+
"cannot increment the use count of a non-shareable reference");
166+
++use_count_;
167+
}
168+
169+
void decrement_use_count()
170+
{
171+
INVARIANT(
172+
is_shareable(),
173+
"cannot decrement the use count of a non-shareable reference");
174+
--use_count_;
175+
}
176+
177+
Num use_count() const
178+
{
179+
return is_shareable() ? use_count_ : 1;
180+
}
181+
182+
void set_shareable(bool u)
183+
{
184+
use_count_ = u ? 1 : unshareable;
185+
}
186+
187+
bool is_shareable() const
188+
{
189+
return use_count_ != unshareable;
190+
}
191+
192+
protected:
193+
~copy_on_write_pointeet() = default;
194+
195+
private:
196+
/// A special sentry value which will be assigned to use_count_ if
197+
/// a mutable reference to the held object has been created. We check for
198+
/// this sentry value, and use it to decide whether the next copy
199+
/// constructor/assignment call should invoke a deep or shallow copy.
200+
/// Note that this is set to the max value that can be held by Num, but
201+
/// this cannot be done inline.
202+
static const Num unshareable;
203+
Num use_count_ = 0;
204+
};
205+
206+
template <typename Num>
207+
const Num copy_on_write_pointeet<Num>::unshareable =
208+
(std::numeric_limits<Num>::max)(); // suppress macro expansion on windows
209+
210+
/// The following functions are required by copy_on_writet, and by default pass
211+
/// through to the member functions of copy_on_write_pointeet by the same name.
212+
/// We provide these as non-members just in case a future client wants to
213+
/// implement a copy-on-write class, which is unable to inherit from
214+
/// copy_on_write_pointeet for some reason. In this case, new overloads for
215+
/// the functions below can be provided, with appropriate behavior for the new
216+
/// type.
217+
218+
template <typename Num>
219+
inline void pointee_increment_use_count(copy_on_write_pointeet<Num> &p)
220+
{
221+
p.increment_use_count();
222+
}
223+
224+
template <typename Num>
225+
inline void pointee_decrement_use_count(copy_on_write_pointeet<Num> &p)
226+
{
227+
p.decrement_use_count();
228+
}
229+
230+
template <typename Num>
231+
inline Num pointee_use_count(const copy_on_write_pointeet<Num> &p)
232+
{
233+
return p.use_count();
234+
}
235+
236+
template <typename Num, typename T>
237+
inline void pointee_set_use_count(copy_on_write_pointeet<Num> &p, T count)
238+
{
239+
p.set_use_count(count);
240+
}
241+
242+
template <typename Num>
243+
inline void pointee_set_shareable(copy_on_write_pointeet<Num> &p, bool u)
244+
{
245+
p.set_shareable(u);
246+
}
247+
248+
template <typename Num>
249+
inline bool pointee_is_shareable(const copy_on_write_pointeet<Num> &p)
250+
{
251+
return p.is_shareable();
252+
}
253+
254+
#endif

0 commit comments

Comments
 (0)