VirtualBox

source: vbox/trunk/src/VBox/Main/include/Shareable.h@ 25414

Last change on this file since 25414 was 25310, checked in by vboxsync, 14 years ago

Main: lock validator, first batch: implement per-thread stack to trace locking (disabled by default, use VBOX_WITH_LOCK_VALIDATOR, but that WILL FAIL presently)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.6 KB
Line 
1/** @file
2 *
3 * Data structure management templates (Shareable and friends)
4 */
5
6/*
7 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22/// @todo (dmik) This header is not yet used.
23// Templates defined here are to replace Shareable and Backupable
24// defined in VirtualBoxBase.h
25
26#ifndef ____H_SHAREABLE
27#define ____H_SHAREABLE
28
29#include <iprt/asm.h>
30#include <iprt/assert.h>
31
32namespace util
33{
34
35namespace internal
36{
37
38class Dummy
39{
40};
41
42class Shareable_base
43{
44protected:
45
46 class Data
47 {
48 public:
49
50 Data() : mRefCnt (0) {}
51 virtual ~Data() {}
52
53 uint32_t addRef() { return ASMAtomicIncU32 (&mRefCnt); }
54 uint32_t release()
55 {
56 uint32_t refCnt = ASMAtomicDecU32 (&mRefCnt);
57 if (refCnt == 0)
58 delete this;
59 return refCnt;
60 }
61
62 private:
63
64 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP (Data)
65
66 uint32_t volatile mRefCnt;
67 };
68};
69
70} /* namespace internal */
71
72/**
73 * Template class to manage allocation/deallocation of data structures on the
74 * heap with the ability to share allocated data among several instances.
75 *
76 * Data sharing is implemented using the concept of reference counting.
77 * When an instance allocates the managed data structure from scratch, it sets
78 * the reference counter to one. When it starts sharing the existing data
79 * structure, it simply increments the reference counter by one. When it stops
80 * sharing data, the reference counter is decremented. Once this counter drops
81 * to zero, the managed data structure is automatically deallocated (deleted).
82 *
83 * Data managed by instances of this class can be either |NULL| (not allocated
84 * and not shared) or non-|NULL| (either allocated using #create() or shared
85 * with another instance using one of assignment operators or methods), as
86 * indicated by the #isNull() method and by the data pointer returned by
87 * the #data() method. Note that the #operator->() method will fail (hit an
88 * assertion) if the managed data pointer is |NULL|.
89 *
90 * The managed data structure (passed as the first argument to the template)
91 * must meet the following requirements:
92 * <ul>
93 * <li>Must have a public default constructor (with no arguments)
94 * <li>Must define a public copy constructor and assignment operation
95 * </ul>
96 *
97 * This template class is NOT thread-safe. If you need thread safefy, you can
98 * specify util::Lockable as the second argument to the template. In this
99 * case, you can explicitly lock instances of the template (using the
100 * AutoWriteLock and AutoReadLock classes) before accessing any of its
101 * members, as follows:
102 * <code>
103 * struct Data { ... };
104 * Shareable <Data, util::Lockable> mData;
105 * ...
106 * {
107 * // acquire the lock until the end of the block
108 * AutoWriteLock alock(mData COMMA_LOCKVAL_SRC_POS);
109 * // share with another instance (thatData defined somewhere else)
110 * {
111 * AutoReadLock thatLock(thatData COMMA_LOCKVAL_SRC_POS);
112 * mData = thatData;
113 * }
114 * // access managed data (through #operator->())
115 * mData->mSomeVield = someValue;
116 * }
117 * </code>
118 *
119 * Making this class thread-safe usually assumes that the managed data
120 * structure must be also thread-safe and must share its own lock object with
121 * all other Shareable instances referring it (so locking it through one
122 * Shareable instance will prevent another one sharing the same data from
123 * accessing it). This can be done in a similar way by deriving the data
124 * structure to manage from util::Lockable and using the #data() method to
125 * lock it before accessing:
126 * <code>
127 * struct Data : public util::Lockable { ... };
128 * Shareable <Data, util::Lockable> mData;
129 * ...
130 * {
131 * // read-only data access
132 * AutoReadLock lock(mData COMMA_LOCKVAL_SRC_POS); // protect Shareable members (read-only)
133 * AutoReadLock dataLock(mData.data() COMMA_LOCKVAL_SRC_POS); // protect Data members (read-only)
134 * if (mData->mSomeVield) ...
135 * }
136 * ...
137 * {
138 * // data modification
139 * AutoReadLock lock(mData COMMA_LOCKVAL_SRC_POS); // protect Shareable members (still read-only)
140 * AutoWriteLock dataLock(mData.data() COMMA_LOCKVAL_SRC_POS); // protect Data members (exclusive)
141 * mData->mSomeVield = someValue;
142 * }
143 * </code>
144 *
145 * Please note that if you want to access managed data through #data() or
146 * #operator->(), you have to enter both locks! If you only need to access
147 * Shareable members themselves (i.e. to assign one Shareable to another or to
148 * call #setNull()) it's enough to enter the Shareable lock only (as it's shown
149 * at the beginning of the first example).
150 *
151 * @param D data structure to manage
152 * @param Extra extra class the template instantiation will be publicly
153 * derived from (by default, a dummy empty class)
154 */
155template <class D, class Extra = internal::Dummy>
156class Shareable : public internal::Shareable_base, public Extra
157{
158public:
159
160 /** Creates a new instance with managed data set to |NULL|. */
161 Shareable() : mData (NULL) {}
162
163 /** Calls #setNull() and deletes this instance. */
164 virtual ~Shareable() { setNull(); };
165
166 /**
167 * Allocates the managed data structure on the heap using the default
168 * constructor. The current data, if not |NULL|, is dereferenced (see
169 * #setNull()).
170 */
171 void create()
172 {
173 Data data = new Data();
174 AssertReturn (data != NULL, (void) 0);
175 setData (data);
176 }
177
178 /**
179 * Allocates a copy of the managed data structure represented by the given
180 * instance using the copy constructor. The current data, if not |NULL|,
181 * is dereferenced (see #setNull()).
182 *
183 * @note The newly allocated data is not shared with the given instance
184 * (they are fully independent of each other).
185 */
186 void createCopy (const Shareable &that)
187 {
188 Data data = NULL;
189 if (that.mData)
190 {
191 data = new Data (*that.mData);
192 AssertReturn (data != NULL, (void) 0);
193 }
194 setData (data);
195 }
196
197 /**
198 * Starts sharing the managed data structure represented by the given
199 * instance. The data reference counter is incremented by one.
200 */
201 Shareable &operator= (const Shareable &that)
202 {
203 setData (that->mData);
204 return *this;
205 }
206
207 /**
208 * Dereferences the managed data (decrements the reference counter) to
209 * effectively stop sharing and sets the managed data pointer to |NULL|.
210 * If this instance is the last (the only) one that to refers non-NULL data,
211 * this data will be deallocated (deleted). Does nothing if data is |NULL|.
212 */
213 virtual void setNull() { setData (NULL); }
214
215 /**
216 * Returns |true| if the managed data pointer is |NULL| (not allocated and
217 * not shared).
218 */
219 bool isNull() const { return mData == NULL; }
220 /**
221 * Converts this instance to |bool| (returns |true| when #isNull() is
222 * |false|)
223 */
224 operator bool() const { return !isNull(); }
225
226 /**
227 * Returns a pointer to the managed data structure (|NULL| when #isNull()
228 * returns |true|).
229 */
230 D *data() { return mData; }
231 /**
232 * Returns a pointer to the managed data structure (|NULL| when #isNull()
233 * returns |true|).
234 */
235 const D *data() const { return mData; }
236
237 /**
238 * A convenience shortcut to #data() (implements direct pointer semantics).
239 * @note This operator will fail if the managed data pointer is |NULL|.
240 */
241 D *operator->()
242 {
243 AssertMsg (mData != NULL, ("Managed data is NULL\n"));
244 return mData;
245 }
246 /**
247 * A convenience shortcut to #data() (implements direct pointer semantics).
248 * @note This operator will fail if the managed data pointer is |NULL|.
249 */
250 const D *operator->() const
251 {
252 AssertMsg (mData != NULL, ("Managed data is NULL\n"));
253 return mData;
254 }
255
256protected:
257
258 typedef internal::Shareable_base::Data BD;
259 class Data : public D, public BD
260 {
261 public:
262 Data() : D(), BD() {}
263 Data (const Data &that) : D (&that), BD() {}
264 private:
265 Data &operator= (const Data &that);
266 };
267
268 void setData (Data *aData)
269 {
270 // beware of self assignment
271 if (aData)
272 aData->addRef();
273 if (mData)
274 mData->release();
275 mData = aData;
276 }
277
278private:
279
280 Data *mData;
281};
282
283WORKAROUND_MSVC7_ERROR_C2593_FOR_BOOL_OP_TPL (Shareable, <class D>, <D>)
284
285/**
286 * Template class that adds support for data backup to the Shareable template.
287 *
288 * All thread safety remarks mentioned in the descrition of the Shareable
289 * template are appliable to this class as well. In particular, all new methods
290 * of this template are not implicitly thread-safe, so if you add thread
291 * safety using the util::Lockable class, don't forget to lock the
292 * Backupable instance before doing #backup(), #commit() or #rollback().
293 *
294 * The managed data structure (passed as the first argument to the template)
295 * besides requirements mentioned in Shareable, must also provide a comparison
296 * operation (|bool operator== (const D &that) const|).
297 *
298 * @param D data structure to manage
299 * @param Extra extra class the template instantiation will be publicly
300 * derived from (by default, a dummy empty class)
301 */
302template <class D, class Extra = internal::Dummy>
303class Backupable : public Shareable <D, Extra>
304{
305public:
306
307 /**
308 * Creates a new instance with both managed data and its backup copy
309 * set to |NULL|.
310 */
311 Backupable() : mBackupData (NULL) {}
312
313 /**
314 * Calls #rollback() and then calls Shareable::setNull().
315 */
316 virtual void setNull()
317 {
318 rollback();
319 Shareable::setNull();
320 }
321
322 /**
323 * Stores the current data pointer in the backup area and allocates a new
324 * data using the copy constructor. The new data becomes the active one
325 * (returned by #data() and #operator->()).
326 * The method does nothing if the managed data is already backed up.
327 * @note The managed data pointer must not be |NULL|, otherwise this method
328 * will fail.
329 */
330 void backup()
331 {
332 if (!mBackupData)
333 {
334 AssertMsgReturn (this->mData, ("Managed data must not be NULL\n"),
335 (void) 0);
336 Data data = new Data (*this->mData);
337 AssertReturn (data != NULL, (void) 0);
338 mBackupData = this->mData;
339 mBackupData->addRef();
340 setData (data);
341 }
342 }
343
344 /**
345 * Dereferences the new data allocated by #backup() and restores the
346 * previous data pointer from the backup area, making it active again.
347 * If this instance is the last (the only) one that refers to the new data,
348 * this data will be deallocated (deleted).
349 * @note The managed data must be backed up, otherwise this method will fail.
350 */
351 void rollback()
352 {
353 AssertMsgReturn (this->mData, ("Managed data must not be NULL\n"),
354 (void) 0);
355 AssertMsgReturn (mBackupData, ("Backed up data must not be NULL\n"),
356 (void) 0);
357 setData (mBackupData);
358 mBackupData->release();
359 mBackupData = NULL;
360 }
361
362 /**
363 * Dereferences the data backed up by #backup() (see #setNull()) keeping
364 * the new data active.
365 * If this instance is the last (the only) one that refers to the backed up
366 * data, this data will be deallocated (deleted).
367 * @note The managed data must be backed up, otherwise this method will fail.
368 */
369 void commit()
370 {
371 AssertMsgReturn (this->mData, ("Managed data must not be NULL\n"),
372 (void) 0);
373 AssertMsgReturn (mBackupData, ("Backed up data must not be NULL\n"),
374 (void) 0);
375 mBackupData->release();
376 mBackupData = NULL;
377 }
378
379 /** Returns |true| if the managed data is currently backed up. */
380 bool isBackedUp() const { return mBackupData != NULL; }
381
382 /**
383 * Returns |true| if the managed data is currently backed up and the backed
384 * up copy differs from the current data. The comparison is done using
385 * the comparison operator provided by the managed data structure.
386 * @note |false| is returned if the data is not currently backed up.
387 */
388 bool hasActualChanges() const
389 {
390 AssertMsgReturn (this->mData, ("Managed data must not be NULL"), false);
391 return mBackupData != NULL &&
392 !(this->mData->D::operator== (*mBackupData));
393 }
394
395 /**
396 * Returns a pointer to the backed up copy of the managed data structure
397 * (|NULL| when #isBackedUp() returns |false|).
398 */
399 const D *backupData() const { return mBackupData; }
400
401protected:
402
403 typedef Shareable::Data Data;
404
405private:
406
407 Data *mBackupData;
408};
409
410} /* namespace util */
411
412#endif // ____H_SHAREABLE
413
414/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use