Branch data 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 : 30489441 : static void incrementCount( ref_count_t& rCount ) { ++rCount; }
42 : 41138186 : 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 : 15 : static void incrementCount( ref_count_t& rCount ) { osl_incrementInterlockedCount(&rCount); }
54 : 40 : static bool decrementCount( ref_count_t& rCount )
55 : : {
56 [ + + ]: 40 : if( rCount == 1 ) // caller is already the only/last reference
57 : 25 : return false;
58 : : else
59 : 40 : return osl_decrementInterlockedCount(&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 [ + - ]: 10651221 : struct impl_t : private boost::noncopyable
171 : : {
172 : 2047 : impl_t() :
173 : : m_value(),
174 [ + - ]: 2047 : m_ref_count(1)
175 : : {
176 : 2047 : }
177 : :
178 : 10650206 : explicit impl_t( const T& v ) :
179 : : m_value(v),
180 [ + - ]: 10650206 : m_ref_count(1)
181 : : {
182 : 10650206 : }
183 : :
184 : : T m_value;
185 : : typename MTPolicy::ref_count_t m_ref_count;
186 : : };
187 : :
188 : 41138226 : void release()
189 : : {
190 [ + + ][ + + ]: 41138226 : if( !MTPolicy::decrementCount(m_pimpl->m_ref_count) )
191 : 10651221 : boost::checked_delete(m_pimpl), m_pimpl=0;
192 : 41138226 : }
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 : 2047 : cow_wrapper() :
203 [ + - ]: 2047 : m_pimpl( new impl_t() )
204 : : {
205 : 2047 : }
206 : :
207 : : /** Copy-construct wrapped type instance from given object
208 : : */
209 : 3823719 : explicit cow_wrapper( const value_type& r ) :
210 [ + - ]: 3823719 : m_pimpl( new impl_t(r) )
211 : : {
212 : 3823719 : }
213 : :
214 : : /** Shallow-copy given cow_wrapper
215 : : */
216 : 29852836 : explicit cow_wrapper( const cow_wrapper& rSrc ) : // nothrow
217 : 29852836 : m_pimpl( rSrc.m_pimpl )
218 : : {
219 : 29852836 : MTPolicy::incrementCount( m_pimpl->m_ref_count );
220 : 29852836 : }
221 : :
222 : 33675119 : ~cow_wrapper() // nothrow, if ~T does not throw
223 : : {
224 : 33675119 : release();
225 : 33675119 : }
226 : :
227 : : /// now sharing rSrc cow_wrapper instance with us
228 : 636620 : cow_wrapper& operator=( const cow_wrapper& rSrc ) // nothrow
229 : : {
230 : : // this already guards against self-assignment
231 : 636620 : MTPolicy::incrementCount( rSrc.m_pimpl->m_ref_count );
232 : :
233 : 636620 : release();
234 : 636620 : m_pimpl = rSrc.m_pimpl;
235 : :
236 : 636620 : return *this;
237 : : }
238 : :
239 : : /// unshare with any other cow_wrapper instance
240 : 33314772 : value_type& make_unique()
241 : : {
242 [ + + ][ + + ]: 33314772 : if( m_pimpl->m_ref_count > 1 )
243 : : {
244 [ + - ]: 6826487 : impl_t* pimpl = new impl_t(m_pimpl->m_value);
245 : 6826487 : release();
246 : 6826487 : m_pimpl = pimpl;
247 : : }
248 : :
249 : 33314772 : return m_pimpl->m_value;
250 : : }
251 : :
252 : : /// true, if not shared with any other cow_wrapper instance
253 : 75 : bool is_unique() const // nothrow
254 : : {
255 : 75 : return m_pimpl->m_ref_count == 1;
256 : : }
257 : :
258 : : /// return number of shared instances (1 for unique object)
259 : 135 : typename MTPolicy::ref_count_t use_count() const // nothrow
260 : : {
261 : 135 : return m_pimpl->m_ref_count;
262 : : }
263 : :
264 : 15 : void swap(cow_wrapper& r) // never throws
265 : : {
266 : 15 : std::swap(m_pimpl, r.m_pimpl);
267 : 15 : }
268 : :
269 : 33252102 : pointer operator->() { return &make_unique(); }
270 : 62355 : value_type& operator*() { return make_unique(); }
271 : 157743109 : const_pointer operator->() const { return &m_pimpl->m_value; }
272 : 776297 : 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 : 2939288 : bool same_object( const cow_wrapper& rOther ) const
279 : : {
280 : 2939288 : return rOther.m_pimpl == m_pimpl;
281 : : }
282 : :
283 : : private:
284 : : impl_t* m_pimpl;
285 : : };
286 : :
287 : :
288 : 90 : template<class T, class P> inline bool operator==( const cow_wrapper<T,P>& a,
289 : : const cow_wrapper<T,P>& b )
290 : : {
291 [ + + ][ + + ]: 90 : return a.same_object(b) ? true : *a == *b;
292 : : }
293 : :
294 : 45 : template<class T, class P> inline bool operator!=( const cow_wrapper<T,P>& a,
295 : : const cow_wrapper<T,P>& b )
296 : : {
297 [ - + ][ - + ]: 45 : return a.same_object(b) ? false : *a != *b;
298 : : }
299 : :
300 : 30 : 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 : 30 : return *a < *b;
304 : : }
305 : :
306 : 15 : template<class T, class P> inline void swap( cow_wrapper<T,P>& a,
307 : : cow_wrapper<T,P>& b )
308 : : {
309 : 15 : a.swap(b);
310 : 15 : }
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: */
|