Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 : #ifndef INCLUDED_O3TL_COW_WRAPPER_HXX
21 : #define INCLUDED_O3TL_COW_WRAPPER_HXX
22 :
23 : #include <osl/interlck.h>
24 :
25 : #include <algorithm>
26 :
27 : #include <boost/utility.hpp>
28 : #include <boost/checked_delete.hpp>
29 :
30 : namespace o3tl
31 : {
32 : /** Thread-unsafe refcounting
33 :
34 : This is the default locking policy for cow_wrapper. No
35 : locking/guarding against concurrent access is performed
36 : whatsoever.
37 : */
38 : struct UnsafeRefCountingPolicy
39 : {
40 : typedef sal_uInt32 ref_count_t;
41 0 : static void incrementCount( ref_count_t& rCount ) { ++rCount; }
42 0 : static bool decrementCount( ref_count_t& rCount ) { return --rCount != 0; }
43 : };
44 :
45 : /** Thread-safe refcounting
46 :
47 : Use this to have the cow_wrapper refcounting mechanisms employ
48 : the thread-safe oslInterlockedCount .
49 : */
50 : struct ThreadSafeRefCountingPolicy
51 : {
52 : typedef oslInterlockedCount ref_count_t;
53 0 : static void incrementCount( ref_count_t& rCount ) { osl_atomic_increment(&rCount); }
54 0 : static bool decrementCount( ref_count_t& rCount )
55 : {
56 0 : if( rCount == 1 ) // caller is already the only/last reference
57 0 : return false;
58 : else
59 0 : return osl_atomic_decrement(&rCount) != 0;
60 : }
61 : };
62 :
63 : /** Copy-on-write wrapper.
64 :
65 : This template provides copy-on-write semantics for the wrapped
66 : type: when copying, the operation is performed shallow,
67 : i.e. different cow_wrapper objects share the same underlying
68 : instance. Only when accessing the underlying object via
69 : non-const methods, a unique copy is provided.
70 :
71 : The type parameter <code>T</code> must satisfy the following
72 : requirements: it must be default-constructible, copyable (it
73 : need not be assignable), and be of non-reference type. Note
74 : that, despite the fact that this template provides access to
75 : the wrapped type via pointer-like methods
76 : (<code>operator->()</code> and <code>operator*()</code>), it does
77 : <em>not</em> work like e.g. the boost pointer wrappers
78 : (shared_ptr, scoped_ptr, etc.). Internally, the cow_wrapper
79 : holds a by-value instance of the wrapped object. This is to
80 : avoid one additional heap allocation, and providing access via
81 : <code>operator->()</code>/<code>operator*()</code> is because
82 : <code>operator.()</code> cannot be overridden.
83 :
84 : Regarding thread safety: this wrapper is <em>not</em>
85 : thread-safe per se, because cow_wrapper has no way of
86 : syncronizing the potentially many different cow_wrapper
87 : instances, that reference a single shared value_type
88 : instance. That said, when passing
89 : <code>ThreadSafeRefCountingPolicy</code> as the
90 : <code>MTPolicy</code> parameter, accessing a thread-safe
91 : pointee through multiple cow_wrapper instances might be
92 : thread-safe, if the individual pointee methods are
93 : thread-safe, <em>including</em> pointee's copy
94 : constructor. Any wrapped object that needs external
95 : synchronisation (e.g. via an external mutex, which arbitrates
96 : access to object methods, and can be held across multiple
97 : object method calls) cannot easily be dealt with in a
98 : thread-safe way, because, as noted, objects are shared behind
99 : the client's back.
100 :
101 : @attention if one wants to use the pimpl idiom together with
102 : cow_wrapper (i.e. put an opaque type into the cow_wrapper),
103 : then <em>all<em> methods in the surrounding class needs to be
104 : non-inline (<em>including</em> destructor, copy constructor
105 : and assignment operator).
106 :
107 : @example
108 : <pre>
109 : class cow_wrapper_client_impl;
110 :
111 : class cow_wrapper_client
112 : {
113 : public:
114 : cow_wrapper_client();
115 : cow_wrapper_client( const cow_wrapper_client& );
116 : ~cow_wrapper_client();
117 :
118 : cow_wrapper_client& operator=( const cow_wrapper_client& );
119 :
120 : void modify( int nVal );
121 : int queryUnmodified() const;
122 :
123 : private:
124 : otl::cow_wrapper< cow_wrapper_client_impl > maImpl;
125 : };
126 : </pre>
127 : and the implementation file would look like this:
128 : <pre>
129 : class cow_wrapper_client_impl
130 : {
131 : public:
132 : void setValue( int nVal ) { mnValue = nVal; }
133 : int getValue() const { return mnValue; }
134 :
135 : private:
136 : int mnValue;
137 : }
138 :
139 : cow_wrapper_client::cow_wrapper_client() :
140 : maImpl()
141 : {
142 : }
143 : cow_wrapper_client::cow_wrapper_client( const cow_wrapper_client& rSrc ) :
144 : maImpl( rSrc.maImpl )
145 : {
146 : }
147 : cow_wrapper_client::~cow_wrapper_client()
148 : {
149 : }
150 : cow_wrapper_client& cow_wrapper_client::operator=( const cow_wrapper_client& rSrc )
151 : {
152 : maImpl = rSrc.maImpl;
153 : return *this;
154 : }
155 : void cow_wrapper_client::modify( int nVal )
156 : {
157 : maImpl->setValue( nVal );
158 : }
159 : int cow_wrapper_client::queryUnmodified() const
160 : {
161 : return maImpl->getValue();
162 : }
163 : </pre>
164 : */
165 : template<typename T, class MTPolicy=UnsafeRefCountingPolicy> class cow_wrapper
166 : {
167 : /** shared value object - gets cloned before cow_wrapper hands
168 : out a non-const reference to it
169 : */
170 0 : struct impl_t : private boost::noncopyable
171 : {
172 0 : impl_t() :
173 : m_value(),
174 0 : m_ref_count(1)
175 : {
176 0 : }
177 :
178 0 : explicit impl_t( const T& v ) :
179 : m_value(v),
180 0 : m_ref_count(1)
181 : {
182 0 : }
183 :
184 : T m_value;
185 : typename MTPolicy::ref_count_t m_ref_count;
186 : };
187 :
188 0 : void release()
189 : {
190 0 : if( !MTPolicy::decrementCount(m_pimpl->m_ref_count) )
191 0 : boost::checked_delete(m_pimpl), m_pimpl=0;
192 0 : }
193 :
194 : public:
195 : typedef T value_type;
196 : typedef T* pointer;
197 : typedef const T* const_pointer;
198 : typedef MTPolicy mt_policy;
199 :
200 : /** Default-construct wrapped type instance
201 : */
202 0 : cow_wrapper() :
203 0 : m_pimpl( new impl_t() )
204 : {
205 0 : }
206 :
207 : /** Copy-construct wrapped type instance from given object
208 : */
209 0 : explicit cow_wrapper( const value_type& r ) :
210 0 : m_pimpl( new impl_t(r) )
211 : {
212 0 : }
213 :
214 : /** Shallow-copy given cow_wrapper
215 : */
216 0 : explicit cow_wrapper( const cow_wrapper& rSrc ) : // nothrow
217 0 : m_pimpl( rSrc.m_pimpl )
218 : {
219 0 : MTPolicy::incrementCount( m_pimpl->m_ref_count );
220 0 : }
221 :
222 0 : ~cow_wrapper() // nothrow, if ~T does not throw
223 : {
224 0 : release();
225 0 : }
226 :
227 : /// now sharing rSrc cow_wrapper instance with us
228 0 : cow_wrapper& operator=( const cow_wrapper& rSrc ) // nothrow
229 : {
230 : // this already guards against self-assignment
231 0 : MTPolicy::incrementCount( rSrc.m_pimpl->m_ref_count );
232 :
233 0 : release();
234 0 : m_pimpl = rSrc.m_pimpl;
235 :
236 0 : return *this;
237 : }
238 :
239 : /// unshare with any other cow_wrapper instance
240 0 : value_type& make_unique()
241 : {
242 0 : if( m_pimpl->m_ref_count > 1 )
243 : {
244 0 : impl_t* pimpl = new impl_t(m_pimpl->m_value);
245 0 : release();
246 0 : m_pimpl = pimpl;
247 : }
248 :
249 0 : return m_pimpl->m_value;
250 : }
251 :
252 : /// true, if not shared with any other cow_wrapper instance
253 : bool is_unique() const // nothrow
254 : {
255 : return m_pimpl->m_ref_count == 1;
256 : }
257 :
258 : /// return number of shared instances (1 for unique object)
259 : typename MTPolicy::ref_count_t use_count() const // nothrow
260 : {
261 : return m_pimpl->m_ref_count;
262 : }
263 :
264 0 : void swap(cow_wrapper& r) // never throws
265 : {
266 0 : std::swap(m_pimpl, r.m_pimpl);
267 0 : }
268 :
269 0 : pointer operator->() { return &make_unique(); }
270 0 : value_type& operator*() { return make_unique(); }
271 0 : const_pointer operator->() const { return &m_pimpl->m_value; }
272 0 : const value_type& operator*() const { return m_pimpl->m_value; }
273 :
274 : pointer get() { return &make_unique(); }
275 : const_pointer get() const { return &m_pimpl->m_value; }
276 :
277 : /// true, if both cow_wrapper internally share the same object
278 0 : bool same_object( const cow_wrapper& rOther ) const
279 : {
280 0 : return rOther.m_pimpl == m_pimpl;
281 : }
282 :
283 : private:
284 : impl_t* m_pimpl;
285 : };
286 :
287 :
288 0 : template<class T, class P> inline bool operator==( const cow_wrapper<T,P>& a,
289 : const cow_wrapper<T,P>& b )
290 : {
291 0 : return a.same_object(b) ? true : *a == *b;
292 : }
293 :
294 : template<class T, class P> inline bool operator!=( const cow_wrapper<T,P>& a,
295 : const cow_wrapper<T,P>& b )
296 : {
297 : return a.same_object(b) ? false : *a != *b;
298 : }
299 :
300 : template<class A, class B, class P> inline bool operator<( const cow_wrapper<A,P>& a,
301 : const cow_wrapper<B,P>& b )
302 : {
303 : return *a < *b;
304 : }
305 :
306 : template<class T, class P> inline void swap( cow_wrapper<T,P>& a,
307 : cow_wrapper<T,P>& b )
308 : {
309 : a.swap(b);
310 : }
311 :
312 : // to enable boost::mem_fn on cow_wrapper
313 : template<class T, class P> inline T * get_pointer( const cow_wrapper<T,P>& r )
314 : {
315 : return r.get();
316 : }
317 :
318 : }
319 :
320 : #endif /* INCLUDED_O3TL_COW_WRAPPER_HXX */
321 :
322 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|