VirtualBox

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

Last change on this file since 8083 was 8083, checked in by vboxsync, 17 years ago

Main: Renamed AutoLock => AutoWriteLock; AutoReaderLock => AutoReadLock.

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette