VirtualBox

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

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

InnoTek -> innotek: all the headers and comments.

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