VirtualBox

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

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

The Giant CDDL Dual-License Header Change.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.3 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 AutoLock::Lockable as the second argument to the template. In this
95 * case, you can explicitly lock instances of the template (using the AutoLock
96 * and AutoReaderLock classes) before accessing any of its members, as follows:
97 * <code>
98 * struct Data { ... };
99 * Shareable <Data, AutoLock::Lockable> mData;
100 * ...
101 * {
102 * // acquire the lock until the end of the block
103 * AutoLock lock (mData);
104 * // share with another instance (thatData defined somewhere else)
105 * {
106 * AutoReaderLock thatLock (thatData);
107 * mData = thatData;
108 * }
109 * // access managed data (through #operator->())
110 * mData->mSomeVield = someValue;
111 * }
112 * </code>
113 *
114 * Making this class thread-safe usually assumes that the managed data
115 * structure must be also thread-safe and must share its own lock object with
116 * all other Shareable instances referring it (so locking it through one
117 * Shareable instance will prevent another one sharing the same data from
118 * accessing it). This can be done in a similar way by deriving the data
119 * structure to manage from AutoLock::Lockable and using the #data() method to
120 * lock it before accessing:
121 * <code>
122 * struct Data : public AutoLock::Lockable { ... };
123 * Shareable <Data, AutoLock::Lockable> mData;
124 * ...
125 * {
126 * // read-only data access
127 * AutoReaderLock lock (mData); // protect Shareable members (read-only)
128 * AutoReaderLock dataLock (mData.data()); // protect Data members (read-only)
129 * if (mData->mSomeVield) ...
130 * }
131 * ...
132 * {
133 * // data modification
134 * AutoReaderLock lock (mData); // protect Shareable members (still read-only)
135 * AutoLock dataLock (mData.data()); // protect Data members (exclusive)
136 * mData->mSomeVield = someValue;
137 * }
138 * </code>
139 *
140 * Please note that if you want to access managed data through #data() or
141 * #operator->(), you have to enter both locks! If you only need to access
142 * Shareable members themselves (i.e. to assign one Shareable to another or to
143 * call #setNull()) it's enough to enter the Shareable lock only (as it's shown
144 * at the beginning of the first example).
145 *
146 * @param D data structure to manage
147 * @param Extra extra class the template instantiation will be publicly
148 * derived from (by default, a dummy empty class)
149 */
150template <class D, class Extra = internal::Dummy>
151class Shareable : public internal::Shareable_base, public Extra
152{
153public:
154
155 /** Creates a new instance with managed data set to |NULL|. */
156 Shareable() : mData (NULL) {}
157
158 /** Calls #setNull() and deletes this instance. */
159 virtual ~Shareable() { setNull(); };
160
161 /**
162 * Allocates the managed data structure on the heap using the default
163 * constructor. The current data, if not |NULL|, is dereferenced (see
164 * #setNull()).
165 */
166 void create()
167 {
168 Data data = new Data();
169 AssertReturn (data != NULL, (void) 0);
170 setData (data);
171 }
172
173 /**
174 * Allocates a copy of the managed data structure represented by the given
175 * instance using the copy constructor. The current data, if not |NULL|,
176 * is dereferenced (see #setNull()).
177 *
178 * @note The newly allocated data is not shared with the given instance
179 * (they are fully independent of each other).
180 */
181 void createCopy (const Shareable &that)
182 {
183 Data data = NULL;
184 if (that.mData)
185 {
186 data = new Data (*that.mData);
187 AssertReturn (data != NULL, (void) 0);
188 }
189 setData (data);
190 }
191
192 /**
193 * Starts sharing the managed data structure represented by the given
194 * instance. The data reference counter is incremented by one.
195 */
196 Shareable &operator= (const Shareable &that)
197 {
198 setData (that->mData);
199 return *this;
200 }
201
202 /**
203 * Dereferences the managed data (decrements the reference counter) to
204 * effectively stop sharing and sets the managed data pointer to |NULL|.
205 * If this instance is the last (the only) one that to refers non-NULL data,
206 * this data will be deallocated (deleted). Does nothing if data is |NULL|.
207 */
208 virtual void setNull() { setData (NULL); }
209
210 /**
211 * Returns |true| if the managed data pointer is |NULL| (not allocated and
212 * not shared).
213 */
214 bool isNull() const { return mData == NULL; }
215 /**
216 * Converts this instance to |bool| (returns |true| when #isNull() is
217 * |false|)
218 */
219 operator bool() const { return !isNull(); }
220
221 /**
222 * Returns a pointer to the managed data structure (|NULL| when #isNull()
223 * returns |true|).
224 */
225 D *data() { return mData; }
226 /**
227 * Returns a pointer to the managed data structure (|NULL| when #isNull()
228 * returns |true|).
229 */
230 const D *data() const { return mData; }
231
232 /**
233 * A convenience shortcut to #data() (implements direct pointer semantics).
234 * @note This operator will fail if the managed data pointer is |NULL|.
235 */
236 D *operator->()
237 {
238 AssertMsg (mData != NULL, ("Managed data is NULL\n"));
239 return mData;
240 }
241 /**
242 * A convenience shortcut to #data() (implements direct pointer semantics).
243 * @note This operator will fail if the managed data pointer is |NULL|.
244 */
245 const D *operator->() const
246 {
247 AssertMsg (mData != NULL, ("Managed data is NULL\n"));
248 return mData;
249 }
250
251protected:
252
253 typedef internal::Shareable_base::Data BD;
254 class Data : public D, public BD
255 {
256 public:
257 Data() : D(), BD() {}
258 Data (const Data &that) : D (&that), BD() {}
259 private:
260 Data &operator= (const Data &that);
261 };
262
263 void setData (Data *aData)
264 {
265 // beware of self assignment
266 if (aData)
267 aData->addRef();
268 if (mData)
269 mData->release();
270 mData = aData;
271 }
272
273private:
274
275 Data *mData;
276};
277
278WORKAROUND_MSVC7_ERROR_C2593_FOR_BOOL_OP_TPL (Shareable, <class D>, <D>)
279
280/**
281 * Template class that adds support for data backup to the Shareable template.
282 *
283 * All thread safety remarks mentioned in the descrition of the Shareable
284 * template are appliable to this class as well. In particular, all new methods
285 * of this template are not implicitly thread-safe, so if you add thread
286 * safety using the AutoLock::Lockable class, don't forget to lock the
287 * Backupable instance before doing #backup(), #commit() or #rollback().
288 *
289 * The managed data structure (passed as the first argument to the template)
290 * besides requirements mentioned in Shareable, must also provide a comparison
291 * operation (|bool operator== (const D &that) const|).
292 *
293 * @param D data structure to manage
294 * @param Extra extra class the template instantiation will be publicly
295 * derived from (by default, a dummy empty class)
296 */
297template <class D, class Extra = internal::Dummy>
298class Backupable : public Shareable <D, Extra>
299{
300public:
301
302 /**
303 * Creates a new instance with both managed data and its backup copy
304 * set to |NULL|.
305 */
306 Backupable() : mBackupData (NULL) {}
307
308 /**
309 * Calls #rollback() and then calls Shareable::setNull().
310 */
311 virtual void setNull()
312 {
313 rollback();
314 Shareable::setNull();
315 }
316
317 /**
318 * Stores the current data pointer in the backup area and allocates a new
319 * data using the copy constructor. The new data becomes the active one
320 * (returned by #data() and #operator->()).
321 * The method does nothing if the managed data is already backed up.
322 * @note The managed data pointer must not be |NULL|, otherwise this method
323 * will fail.
324 */
325 void backup()
326 {
327 if (!mBackupData)
328 {
329 AssertMsgReturn (this->mData, ("Managed data must not be NULL\n"),
330 (void) 0);
331 Data data = new Data (*this->mData);
332 AssertReturn (data != NULL, (void) 0);
333 mBackupData = this->mData;
334 mBackupData->addRef();
335 setData (data);
336 }
337 }
338
339 /**
340 * Dereferences the new data allocated by #backup() and restores the
341 * previous data pointer from the backup area, making it active again.
342 * If this instance is the last (the only) one that refers to the new data,
343 * this data will be deallocated (deleted).
344 * @note The managed data must be backed up, otherwise this method will fail.
345 */
346 void rollback()
347 {
348 AssertMsgReturn (this->mData, ("Managed data must not be NULL\n"),
349 (void) 0);
350 AssertMsgReturn (mBackupData, ("Backed up data must not be NULL\n"),
351 (void) 0);
352 setData (mBackupData);
353 mBackupData->release();
354 mBackupData = NULL;
355 }
356
357 /**
358 * Dereferences the data backed up by #backup() (see #setNull()) keeping
359 * the new data active.
360 * If this instance is the last (the only) one that refers to the backed up
361 * data, this data will be deallocated (deleted).
362 * @note The managed data must be backed up, otherwise this method will fail.
363 */
364 void commit()
365 {
366 AssertMsgReturn (this->mData, ("Managed data must not be NULL\n"),
367 (void) 0);
368 AssertMsgReturn (mBackupData, ("Backed up data must not be NULL\n"),
369 (void) 0);
370 mBackupData->release();
371 mBackupData = NULL;
372 }
373
374 /** Returns |true| if the managed data is currently backed up. */
375 bool isBackedUp() const { return mBackupData != NULL; }
376
377 /**
378 * Returns |true| if the managed data is currently backed up and the backed
379 * up copy differs from the current data. The comparison is done using
380 * the comparison operator provided by the managed data structure.
381 * @note |false| is returned if the data is not currently backed up.
382 */
383 bool hasActualChanges() const
384 {
385 AssertMsgReturn (this->mData, ("Managed data must not be NULL"), false);
386 return mBackupData != NULL &&
387 !(this->mData->D::operator== (*mBackupData));
388 }
389
390 /**
391 * Returns a pointer to the backed up copy of the managed data structure
392 * (|NULL| when #isBackedUp() returns |false|).
393 */
394 const D *backupData() const { return mBackupData; }
395
396protected:
397
398 typedef Shareable::Data Data;
399
400private:
401
402 Data *mBackupData;
403};
404
405} /* namespace util */
406
407#endif // ____H_SHAREABLE
408
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