1 | /** @file
|
---|
2 | *
|
---|
3 | * AutoLock: smart critical section wrapper
|
---|
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 | #ifndef ____H_AUTOLOCK
|
---|
23 | #define ____H_AUTOLOCK
|
---|
24 |
|
---|
25 | #include <iprt/cdefs.h>
|
---|
26 | #include <iprt/types.h>
|
---|
27 | #include <iprt/critsect.h>
|
---|
28 | #include <iprt/thread.h>
|
---|
29 |
|
---|
30 | #if defined(DEBUG)
|
---|
31 | # include <iprt/asm.h> // for ASMReturnAddress
|
---|
32 | #endif
|
---|
33 |
|
---|
34 | namespace util
|
---|
35 | {
|
---|
36 |
|
---|
37 | template <size_t> class AutoMultiLock;
|
---|
38 | namespace internal { struct LockableTag; }
|
---|
39 |
|
---|
40 | /**
|
---|
41 | * Smart class to safely manage critical sections. Also provides ecplicit
|
---|
42 | * lock, unlock leave and enter operations.
|
---|
43 | *
|
---|
44 | * When constructing an instance, it enters the given critical
|
---|
45 | * section. This critical section will be exited automatically when the
|
---|
46 | * instance goes out of scope (i.e. gets destroyed).
|
---|
47 | */
|
---|
48 | class AutoLock
|
---|
49 | {
|
---|
50 | public:
|
---|
51 |
|
---|
52 | #if defined(DEBUG)
|
---|
53 | # define ___CritSectEnter(cs) \
|
---|
54 | RTCritSectEnterDebug ((cs), \
|
---|
55 | "AutoLock::lock()/enter() return address >>>", 0, \
|
---|
56 | (RTUINTPTR) ASMReturnAddress())
|
---|
57 | #else
|
---|
58 | # define ___CritSectEnter(cs) RTCritSectEnter ((cs))
|
---|
59 | #endif
|
---|
60 |
|
---|
61 | /**
|
---|
62 | * Lock (critical section) handle. An auxiliary base class for structures
|
---|
63 | * that need locking. Instances of classes inherited from it can be passed
|
---|
64 | * as arguments to the AutoLock constructor.
|
---|
65 | */
|
---|
66 | class Handle
|
---|
67 | {
|
---|
68 | public:
|
---|
69 |
|
---|
70 | Handle() { RTCritSectInit (&mCritSect); }
|
---|
71 | virtual ~Handle() { RTCritSectDelete (&mCritSect); }
|
---|
72 |
|
---|
73 | /** Returns |true| if this handle is locked on the current thread. */
|
---|
74 | bool isLockedOnCurrentThread() const
|
---|
75 | {
|
---|
76 | return RTCritSectIsOwner (&mCritSect);
|
---|
77 | }
|
---|
78 |
|
---|
79 | /** Returns a tag to lock this handle for reading by AutoMultiLock */
|
---|
80 | internal::LockableTag rlock() const;
|
---|
81 | /** Returns a tag to lock this handle for writing by AutoMultiLock */
|
---|
82 | internal::LockableTag wlock() const;
|
---|
83 |
|
---|
84 | private:
|
---|
85 |
|
---|
86 | DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP (Handle)
|
---|
87 |
|
---|
88 | mutable RTCRITSECT mCritSect;
|
---|
89 |
|
---|
90 | friend class AutoLock;
|
---|
91 | template <size_t> friend class AutoMultiLock;
|
---|
92 | };
|
---|
93 |
|
---|
94 | /**
|
---|
95 | * Lockable interface. An abstract base for classes that need locking.
|
---|
96 | * Unlike Handle that makes the lock handle a part of class data, this
|
---|
97 | * class allows subclasses to decide which lock handle to use.
|
---|
98 | */
|
---|
99 | class Lockable
|
---|
100 | {
|
---|
101 | public:
|
---|
102 |
|
---|
103 | /**
|
---|
104 | * Returns a pointer to a Handle used by AutoLock for locking.
|
---|
105 | * Subclasses are allowed to return |NULL| -- in this case,
|
---|
106 | * the AutoLock object constructed using an instance of such
|
---|
107 | * subclass will simply turn into no-op.
|
---|
108 | */
|
---|
109 | virtual Handle *lockHandle() const = 0;
|
---|
110 |
|
---|
111 | /**
|
---|
112 | * Equivalent to |#lockHandle()->isLockedOnCurrentThread()|.
|
---|
113 | * Returns |false| if lockHandle() returns NULL.
|
---|
114 | */
|
---|
115 | bool isLockedOnCurrentThread()
|
---|
116 | {
|
---|
117 | Handle *h = lockHandle();
|
---|
118 | return h ? h->isLockedOnCurrentThread() : false;
|
---|
119 | }
|
---|
120 |
|
---|
121 | /**
|
---|
122 | * Returns a tag to lock this handle for reading by AutoMultiLock.
|
---|
123 | * Shortcut to |lockHandle()->rlock()|.
|
---|
124 | * The returned tag is a no-op, when lockHandle() returns |NULL|.
|
---|
125 | */
|
---|
126 | internal::LockableTag rlock() const;
|
---|
127 |
|
---|
128 | /**
|
---|
129 | * Returns a tag to lock this handle for writing by AutoMultiLock.
|
---|
130 | * Shortcut to |lockHandle()->wlock()|.
|
---|
131 | * The returned tag is a no-op, when lockHandle() returns |NULL|.
|
---|
132 | */
|
---|
133 | internal::LockableTag wlock() const;
|
---|
134 | };
|
---|
135 |
|
---|
136 | AutoLock() : mCritSect (NULL), mLevel (0), mLeftLevel (0) {}
|
---|
137 |
|
---|
138 | AutoLock (RTCRITSECT &aCritSect)
|
---|
139 | : mCritSect (&aCritSect), mLevel (0), mLeftLevel (0) { lock(); }
|
---|
140 |
|
---|
141 | AutoLock (RTCRITSECT *aCritSect)
|
---|
142 | : mCritSect (aCritSect), mLevel (0), mLeftLevel (0) { lock(); }
|
---|
143 |
|
---|
144 | AutoLock (const Handle &aHandle)
|
---|
145 | : mCritSect (&aHandle.mCritSect), mLevel (0), mLeftLevel (0) { lock(); }
|
---|
146 |
|
---|
147 | AutoLock (const Handle *aHandle)
|
---|
148 | : mCritSect (aHandle ? &aHandle->mCritSect : NULL)
|
---|
149 | , mLevel (0), mLeftLevel (0) { lock(); }
|
---|
150 |
|
---|
151 | AutoLock (const Lockable &aLockable)
|
---|
152 | : mCritSect (critSect (&aLockable))
|
---|
153 | , mLevel (0), mLeftLevel (0) { lock(); }
|
---|
154 |
|
---|
155 | AutoLock (const Lockable *aLockable)
|
---|
156 | : mCritSect (aLockable ? critSect (aLockable) : NULL)
|
---|
157 | , mLevel (0), mLeftLevel (0) { lock(); }
|
---|
158 |
|
---|
159 | ~AutoLock()
|
---|
160 | {
|
---|
161 | if (mCritSect)
|
---|
162 | {
|
---|
163 | if (mLeftLevel)
|
---|
164 | {
|
---|
165 | mLeftLevel -= mLevel;
|
---|
166 | mLevel = 0;
|
---|
167 | for (; mLeftLevel; -- mLeftLevel)
|
---|
168 | RTCritSectEnter (mCritSect);
|
---|
169 | }
|
---|
170 | AssertMsg (mLevel <= 1, ("Lock level > 1: %d\n", mLevel));
|
---|
171 | for (; mLevel; -- mLevel)
|
---|
172 | RTCritSectLeave (mCritSect);
|
---|
173 | }
|
---|
174 | }
|
---|
175 |
|
---|
176 | /**
|
---|
177 | * Tries to acquire the lock or increases the lock level
|
---|
178 | * if the lock is already owned by this thread.
|
---|
179 | */
|
---|
180 | void lock()
|
---|
181 | {
|
---|
182 | if (mCritSect)
|
---|
183 | {
|
---|
184 | AssertMsgReturn (mLeftLevel == 0, ("lock() after leave()\n"), (void) 0);
|
---|
185 | ___CritSectEnter (mCritSect);
|
---|
186 | ++ mLevel;
|
---|
187 | }
|
---|
188 | }
|
---|
189 |
|
---|
190 | /**
|
---|
191 | * Decreases the lock level. If the level goes to zero, the lock
|
---|
192 | * is released by the current thread.
|
---|
193 | */
|
---|
194 | void unlock()
|
---|
195 | {
|
---|
196 | if (mCritSect)
|
---|
197 | {
|
---|
198 | AssertMsgReturn (mLevel > 0, ("Lock level is zero\n"), (void) 0);
|
---|
199 | AssertMsgReturn (mLeftLevel == 0, ("lock() after leave()\n"), (void) 0);
|
---|
200 | -- mLevel;
|
---|
201 | RTCritSectLeave (mCritSect);
|
---|
202 | }
|
---|
203 | }
|
---|
204 |
|
---|
205 | /**
|
---|
206 | * Causes the current thread to completely release the lock
|
---|
207 | * (including locks acquired by all other instances of this class
|
---|
208 | * referring to the same object or handle). #enter() must be called
|
---|
209 | * to acquire the lock back and restore all lock levels.
|
---|
210 | */
|
---|
211 | void leave()
|
---|
212 | {
|
---|
213 | if (mCritSect)
|
---|
214 | {
|
---|
215 | AssertMsg (mLevel > 0, ("Lock level is zero\n"));
|
---|
216 | AssertMsgReturn (mLeftLevel == 0, ("leave() w/o enter()\n"), (void) 0);
|
---|
217 | mLeftLevel = RTCritSectGetRecursion (mCritSect);
|
---|
218 | for (uint32_t left = mLeftLevel; left; -- left)
|
---|
219 | RTCritSectLeave (mCritSect);
|
---|
220 | Assert (mLeftLevel >= mLevel);
|
---|
221 | }
|
---|
222 | }
|
---|
223 |
|
---|
224 | /**
|
---|
225 | * Causes the current thread to acquire the lock again and restore
|
---|
226 | * all lock levels after calling #leave().
|
---|
227 | */
|
---|
228 | void enter()
|
---|
229 | {
|
---|
230 | if (mCritSect)
|
---|
231 | {
|
---|
232 | AssertMsg (mLevel > 0, ("Lock level is zero\n"));
|
---|
233 | AssertMsgReturn (mLeftLevel > 0, ("enter() w/o leave()\n"), (void) 0);
|
---|
234 | for (; mLeftLevel; -- mLeftLevel)
|
---|
235 | ___CritSectEnter (mCritSect);
|
---|
236 | }
|
---|
237 | }
|
---|
238 |
|
---|
239 | /**
|
---|
240 | * Current level of the nested lock. 1 means the lock is not currently
|
---|
241 | * nested.
|
---|
242 | */
|
---|
243 | uint32_t level() const { return RTCritSectGetRecursion (mCritSect); }
|
---|
244 |
|
---|
245 | bool isNull() const { return mCritSect == NULL; }
|
---|
246 | bool operator !() const { return isNull(); }
|
---|
247 |
|
---|
248 | /** Returns |true| if this instance manages the given lock handle. */
|
---|
249 | bool belongsTo (const Handle &aHandle)
|
---|
250 | {
|
---|
251 | return &aHandle.mCritSect == mCritSect;
|
---|
252 | }
|
---|
253 |
|
---|
254 | /** Returns |true| if this instance manages the given lock handle. */
|
---|
255 | bool belongsTo (const Handle *aHandle)
|
---|
256 | {
|
---|
257 | return aHandle && &aHandle->mCritSect == mCritSect;
|
---|
258 | }
|
---|
259 |
|
---|
260 | /** Returns |true| if this instance manages the given lockable object. */
|
---|
261 | bool belongsTo (const Lockable &aLockable)
|
---|
262 | {
|
---|
263 | return belongsTo (aLockable.lockHandle());
|
---|
264 | }
|
---|
265 |
|
---|
266 | /** Returns |true| if this instance manages the given lockable object. */
|
---|
267 | bool belongsTo (const Lockable *aLockable)
|
---|
268 | {
|
---|
269 | return aLockable && belongsTo (aLockable->lockHandle());
|
---|
270 | }
|
---|
271 |
|
---|
272 | private:
|
---|
273 |
|
---|
274 | DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP (AutoLock)
|
---|
275 | DECLARE_CLS_NEW_DELETE_NOOP (AutoLock)
|
---|
276 |
|
---|
277 | inline static RTCRITSECT *critSect (const Lockable *l)
|
---|
278 | {
|
---|
279 | Assert (l);
|
---|
280 | Handle *h = l->lockHandle();
|
---|
281 | return h ? &h->mCritSect : NULL;
|
---|
282 | }
|
---|
283 |
|
---|
284 | RTCRITSECT *mCritSect;
|
---|
285 | uint32_t mLevel;
|
---|
286 | uint32_t mLeftLevel;
|
---|
287 |
|
---|
288 | #undef ___CritSectEnter
|
---|
289 | };
|
---|
290 |
|
---|
291 | /**
|
---|
292 | * Prototype. Later will be used to acquire a read-only lock
|
---|
293 | * (read-only locks differ from regular (write) locks so that more than one
|
---|
294 | * read lock can be acquired simultaneously provided that there are no
|
---|
295 | * active write locks).
|
---|
296 | * @todo Implement it!
|
---|
297 | */
|
---|
298 | class AutoReaderLock : public AutoLock
|
---|
299 | {
|
---|
300 | public:
|
---|
301 |
|
---|
302 | AutoReaderLock (const Handle &aHandle) : AutoLock (aHandle) {}
|
---|
303 | AutoReaderLock (const Handle *aHandle) : AutoLock (aHandle) {}
|
---|
304 |
|
---|
305 | AutoReaderLock (const Lockable &aLockable) : AutoLock (aLockable) {}
|
---|
306 | AutoReaderLock (const Lockable *aLockable) : AutoLock (aLockable) {}
|
---|
307 |
|
---|
308 | private:
|
---|
309 |
|
---|
310 | DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP (AutoReaderLock)
|
---|
311 | DECLARE_CLS_NEW_DELETE_NOOP (AutoReaderLock)
|
---|
312 | };
|
---|
313 |
|
---|
314 | namespace internal
|
---|
315 | {
|
---|
316 | /**
|
---|
317 | * @internal
|
---|
318 | * Special struct to differentiate between read and write locks
|
---|
319 | * in AutoMultiLock constructors.
|
---|
320 | */
|
---|
321 | struct LockableTag
|
---|
322 | {
|
---|
323 | LockableTag (const AutoLock::Handle *h, char m)
|
---|
324 | : handle (h), mode (m) {}
|
---|
325 | const AutoLock::Handle * const handle;
|
---|
326 | const char mode;
|
---|
327 | };
|
---|
328 | }
|
---|
329 |
|
---|
330 | inline internal::LockableTag AutoLock::Handle::rlock() const
|
---|
331 | {
|
---|
332 | return internal::LockableTag (this, 'r');
|
---|
333 | }
|
---|
334 |
|
---|
335 | inline internal::LockableTag AutoLock::Handle::wlock() const
|
---|
336 | {
|
---|
337 | return internal::LockableTag (this, 'w');
|
---|
338 | }
|
---|
339 |
|
---|
340 | inline internal::LockableTag AutoLock::Lockable::rlock() const
|
---|
341 | {
|
---|
342 | return internal::LockableTag (lockHandle(), 'r');
|
---|
343 | }
|
---|
344 |
|
---|
345 | inline internal::LockableTag AutoLock::Lockable::wlock() const
|
---|
346 | {
|
---|
347 | return internal::LockableTag (lockHandle(), 'w');
|
---|
348 | }
|
---|
349 |
|
---|
350 | /**
|
---|
351 | * Smart template class to safely enter and leave a list of critical sections.
|
---|
352 | *
|
---|
353 | * When constructing an instance, it enters all given critical sections
|
---|
354 | * (in an "atomic", "all or none" fashion). These critical sections will be
|
---|
355 | * exited automatically when the instance goes out of scope (i.e. gets destroyed).
|
---|
356 | *
|
---|
357 | * It is possible to lock different critical sections in two different modes:
|
---|
358 | * for writing (as AutoLock does) or for reading (as AutoReaderLock does).
|
---|
359 | * The lock mode is determined by the method called on an AutoLock::Handle or
|
---|
360 | * AutoLock::Lockable instance when passing it to the AutoMultiLock constructor:
|
---|
361 | * |rlock()| to lock for reading or |wlock()| to lock for writing.
|
---|
362 | *
|
---|
363 | * Instances of this class are constructed as follows:
|
---|
364 | * <code>
|
---|
365 | * ...
|
---|
366 | * AutoLock::Handle data1, data2;
|
---|
367 | * ...
|
---|
368 | * {
|
---|
369 | * AutoMultiLock <2> multiLock (data1.wlock(), data2.rlock());
|
---|
370 | * // all locks are entered here:
|
---|
371 | * // data1 is entered in write mode (like AutoLock)
|
---|
372 | * // data2 is entered in read mode (like AutoReaderLock),
|
---|
373 | * }
|
---|
374 | * // all locks are exited here
|
---|
375 | * </code>
|
---|
376 | *
|
---|
377 | * The number of critical sections passed to the constructor must exactly
|
---|
378 | * match the number specified as the @a tCnt parameter of the template.
|
---|
379 | *
|
---|
380 | * @param tCnt number of critical sections to manage
|
---|
381 | */
|
---|
382 | template <size_t tCnt>
|
---|
383 | class AutoMultiLock
|
---|
384 | {
|
---|
385 | public:
|
---|
386 |
|
---|
387 | #if defined(DEBUG)
|
---|
388 | # define ___CritSectEnterMulti(n, acs) \
|
---|
389 | RTCritSectEnterMultipleDebug ((n), (acs), \
|
---|
390 | "AutoMultiLock instantiation address >>>", 0, \
|
---|
391 | (RTUINTPTR) ASMReturnAddress())
|
---|
392 | #else
|
---|
393 | # define ___CritSectEnterMulti(n, acs) RTCritSectEnterMultiple ((n), (acs))
|
---|
394 | #endif
|
---|
395 |
|
---|
396 | #define A(n) internal::LockableTag l##n
|
---|
397 | #define B(n) if (l##n.handle) { /* skip NULL tags */ \
|
---|
398 | mS[i] = &l##n.handle->mCritSect; \
|
---|
399 | mM[i++] = l##n.mode; \
|
---|
400 | } else
|
---|
401 | #define C(n) \
|
---|
402 | mLocked = true; \
|
---|
403 | mM [0] = 0; /* for safety in case of early return */ \
|
---|
404 | AssertMsg (tCnt == n, \
|
---|
405 | ("This AutoMultiLock is for %d locks, but %d were passed!\n", tCnt, n)); \
|
---|
406 | if (tCnt != n) return; \
|
---|
407 | int i = 0
|
---|
408 | /// @todo (dmik) this will change when we switch to RTSemRW*
|
---|
409 | #define D() mM [i] = 0; /* end of array */ \
|
---|
410 | ___CritSectEnterMulti (strlen (mM), mS)
|
---|
411 |
|
---|
412 | AutoMultiLock (A(0), A(1))
|
---|
413 | { C(2); B(0); B(1); D(); }
|
---|
414 | AutoMultiLock (A(0), A(1), A(2))
|
---|
415 | { C(3); B(0); B(1); B(2); D(); }
|
---|
416 | AutoMultiLock (A(0), A(1), A(2), A(3))
|
---|
417 | { C(4); B(0); B(1); B(2); B(3); D(); }
|
---|
418 | AutoMultiLock (A(0), A(1), A(2), A(3), A(4))
|
---|
419 | { C(5); B(0); B(1); B(2); B(3); B(4); D(); }
|
---|
420 | AutoMultiLock (A(0), A(1), A(2), A(3), A(4), A(5))
|
---|
421 | { C(6); B(0); B(1); B(2); B(3); B(4); B(5); D(); }
|
---|
422 | AutoMultiLock (A(0), A(1), A(2), A(3), A(4), A(5), A(6))
|
---|
423 | { C(7); B(0); B(1); B(2); B(3); B(4); B(5); B(6); D(); }
|
---|
424 | AutoMultiLock (A(0), A(1), A(2), A(3), A(4), A(5), A(6), A(7))
|
---|
425 | { C(8); B(0); B(1); B(2); B(3); B(4); B(5); B(6); B(7); D(); }
|
---|
426 | AutoMultiLock (A(0), A(1), A(2), A(3), A(4), A(5), A(6), A(7), A(8))
|
---|
427 | { C(9); B(0); B(1); B(2); B(3); B(4); B(5); B(6); B(7); B(8); D(); }
|
---|
428 | AutoMultiLock (A(0), A(1), A(2), A(3), A(4), A(5), A(6), A(7), A(8), A(9))
|
---|
429 | { C(10); B(0); B(1); B(2); B(3); B(4); B(5); B(6); B(7); B(8); B(9); D(); }
|
---|
430 |
|
---|
431 | #undef D
|
---|
432 | #undef C
|
---|
433 | #undef B
|
---|
434 | #undef A
|
---|
435 |
|
---|
436 | /**
|
---|
437 | * Releases all locks if not yet released by #leave() and
|
---|
438 | * destroys the instance.
|
---|
439 | */
|
---|
440 | ~AutoMultiLock()
|
---|
441 | {
|
---|
442 | /// @todo (dmik) this will change when we switch to RTSemRW*
|
---|
443 | if (mLocked)
|
---|
444 | RTCritSectLeaveMultiple (strlen (mM), mS);
|
---|
445 | }
|
---|
446 |
|
---|
447 | /**
|
---|
448 | * Releases all locks temporarily in order to enter them again using
|
---|
449 | * #enter().
|
---|
450 | *
|
---|
451 | * @note There is no need to call this method unless you want later call
|
---|
452 | * #enter(). The destructor calls it automatically when necessary.
|
---|
453 | *
|
---|
454 | * @note Calling this method twice without calling #enter() in between will
|
---|
455 | * definitely fail.
|
---|
456 | *
|
---|
457 | * @note Unlike AutoLock::leave(), this method doesn't cause a complete
|
---|
458 | * release of all involved locks; if any of the locks was entered on the
|
---|
459 | * current thread prior constructing the AutoMultiLock instanve, they will
|
---|
460 | * remain acquired after this call! For this reason, using this method in
|
---|
461 | * the custom code doesn't make any practical sense.
|
---|
462 | *
|
---|
463 | * @todo Rename this method to unlock() and rename #enter() to lock()
|
---|
464 | * for similarity with AutoLock.
|
---|
465 | */
|
---|
466 | void leave()
|
---|
467 | {
|
---|
468 | AssertMsgReturn (mLocked, ("Already released all locks"), (void) 0);
|
---|
469 | /// @todo (dmik) this will change when we switch to RTSemRW*
|
---|
470 | RTCritSectLeaveMultiple (strlen (mM), mS);
|
---|
471 | mLocked = false;
|
---|
472 | }
|
---|
473 |
|
---|
474 | /**
|
---|
475 | * Tries to enter all locks temporarily released by #unlock().
|
---|
476 | *
|
---|
477 | * @note This method succeeds only after #unlock() and always fails
|
---|
478 | * otherwise.
|
---|
479 | */
|
---|
480 | void enter()
|
---|
481 | {
|
---|
482 | AssertMsgReturn (!mLocked, ("Already entered all locks"), (void) 0);
|
---|
483 | ___CritSectEnterMulti (strlen (mM), mS);
|
---|
484 | mLocked = true;
|
---|
485 | }
|
---|
486 |
|
---|
487 | private:
|
---|
488 |
|
---|
489 | AutoMultiLock();
|
---|
490 |
|
---|
491 | DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP (AutoMultiLock)
|
---|
492 | DECLARE_CLS_NEW_DELETE_NOOP (AutoMultiLock)
|
---|
493 |
|
---|
494 | RTCRITSECT *mS [tCnt];
|
---|
495 | char mM [tCnt + 1];
|
---|
496 | bool mLocked;
|
---|
497 |
|
---|
498 | #undef ___CritSectEnterMulti
|
---|
499 | };
|
---|
500 |
|
---|
501 | /**
|
---|
502 | * Disable instantiations of AutoMultiLock for zero and one
|
---|
503 | * number of locks.
|
---|
504 | */
|
---|
505 | template<>
|
---|
506 | class AutoMultiLock <0> { private : AutoMultiLock(); };
|
---|
507 |
|
---|
508 | template<>
|
---|
509 | class AutoMultiLock <1> { private : AutoMultiLock(); };
|
---|
510 |
|
---|
511 | } // namespace util
|
---|
512 |
|
---|
513 | #endif // ____H_AUTOLOCK
|
---|
514 |
|
---|