VirtualBox

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

Last change on this file since 16560 was 14949, checked in by vboxsync, 15 years ago

Appended vim modeline to set tabstop and expand tabs (in the way
suggested by our coding guidelines).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.5 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);
109 * // share with another instance (thatData defined somewhere else)
110 * {
111 * AutoReadLock thatLock (thatData);
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); // protect Shareable members (read-only)
133 * AutoReadLock dataLock (mData.data()); // protect Data members (read-only)
134 * if (mData->mSomeVield) ...
135 * }
136 * ...
137 * {
138 * // data modification
139 * AutoReadLock lock (mData); // protect Shareable members (still read-only)
140 * AutoWriteLock dataLock (mData.data()); // 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