VirtualBox

source: vbox/trunk/src/VBox/Main/glue/AutoLock.cpp@ 44948

Last change on this file since 44948 was 41528, checked in by vboxsync, 13 years ago

Main/HostUSBDevice(all platforms)+USBProxyService: redo USB locking, fixes major regression, added lots of assertions to catch locking flaws early, whitespace cleanup
Main/Machine: small USB locking fix to be consistent with the remaining code
Main/Host+glue/AutoLock: replace USB list lock by host lock, small numbering cleanup
Main/Console: redo USB locking, do less in USB callbacks/EMT (addresses long standing todo items), eliminate unsafe iterator parameters

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.6 KB
Line 
1/** @file
2 *
3 * Automatic locks, implementation
4 */
5
6/*
7 * Copyright (C) 2006-2012 Oracle Corporation
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#include <iprt/cdefs.h>
19#include <iprt/critsect.h>
20#include <iprt/thread.h>
21#include <iprt/semaphore.h>
22
23#include <iprt/err.h>
24#include <iprt/assert.h>
25
26#if defined(RT_LOCK_STRICT)
27# include <iprt/asm.h> // for ASMReturnAddress
28#endif
29
30#include <iprt/string.h>
31#include <iprt/path.h>
32#include <iprt/stream.h>
33
34#include "VBox/com/AutoLock.h"
35#include <VBox/com/string.h>
36
37#include <vector>
38#include <list>
39#include <map>
40
41namespace util
42{
43
44////////////////////////////////////////////////////////////////////////////////
45//
46// RuntimeLockClass
47//
48////////////////////////////////////////////////////////////////////////////////
49
50#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
51typedef std::map<VBoxLockingClass, RTLOCKVALCLASS> LockValidationClassesMap;
52LockValidationClassesMap g_mapLockValidationClasses;
53#endif
54
55/**
56 * Called from initterm.cpp on process initialization (on the main thread)
57 * to give us a chance to initialize lock validation runtime data.
58 */
59void InitAutoLockSystem()
60{
61#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
62 struct
63 {
64 VBoxLockingClass cls;
65 const char *pcszDescription;
66 } aClasses[] =
67 {
68 { LOCKCLASS_VIRTUALBOXOBJECT, "2-VIRTUALBOXOBJECT" },
69 { LOCKCLASS_HOSTOBJECT, "3-HOSTOBJECT" },
70 { LOCKCLASS_LISTOFMACHINES, "4-LISTOFMACHINES" },
71 { LOCKCLASS_MACHINEOBJECT, "5-MACHINEOBJECT" },
72 { LOCKCLASS_SNAPSHOTOBJECT, "6-SNAPSHOTOBJECT" },
73 { LOCKCLASS_MEDIUMQUERY, "7-MEDIUMQUERY" },
74 { LOCKCLASS_LISTOFMEDIA, "8-LISTOFMEDIA" },
75 { LOCKCLASS_LISTOFOTHEROBJECTS, "9-LISTOFOTHEROBJECTS" },
76 { LOCKCLASS_OTHEROBJECT, "10-OTHEROBJECT" },
77 { LOCKCLASS_PROGRESSLIST, "11-PROGRESSLIST" },
78 { LOCKCLASS_OBJECTSTATE, "12-OBJECTSTATE" }
79 };
80
81 RTLOCKVALCLASS hClass;
82 int vrc;
83 for (unsigned i = 0; i < RT_ELEMENTS(aClasses); ++i)
84 {
85 vrc = RTLockValidatorClassCreate(&hClass,
86 true, /*fAutodidact*/
87 RT_SRC_POS,
88 aClasses[i].pcszDescription);
89 AssertRC(vrc);
90
91 // teach the new class that the classes created previously can be held
92 // while the new class is being acquired
93 for (LockValidationClassesMap::iterator it = g_mapLockValidationClasses.begin();
94 it != g_mapLockValidationClasses.end();
95 ++it)
96 {
97 RTLOCKVALCLASS &canBeHeld = it->second;
98 vrc = RTLockValidatorClassAddPriorClass(hClass,
99 canBeHeld);
100 AssertRC(vrc);
101 }
102
103 // and store the new class
104 g_mapLockValidationClasses[aClasses[i].cls] = hClass;
105 }
106
107/* WriteLockHandle critsect1(LOCKCLASS_VIRTUALBOXOBJECT);
108 WriteLockHandle critsect2(LOCKCLASS_VIRTUALBOXLIST);
109
110 AutoWriteLock lock1(critsect1 COMMA_LOCKVAL_SRC_POS);
111 AutoWriteLock lock2(critsect2 COMMA_LOCKVAL_SRC_POS);*/
112#endif
113}
114
115bool AutoLockHoldsLocksInClass(VBoxLockingClass lockClass)
116{
117#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
118 return RTLockValidatorHoldsLocksInClass(NIL_RTTHREAD,
119 g_mapLockValidationClasses[lockClass]);
120#else /* !VBOX_WITH_MAIN_LOCK_VALIDATION */
121 return false;
122#endif /* !VBOX_WITH_MAIN_LOCK_VALIDATION */
123}
124
125////////////////////////////////////////////////////////////////////////////////
126//
127// RWLockHandle
128//
129////////////////////////////////////////////////////////////////////////////////
130
131struct RWLockHandle::Data
132{
133 Data()
134 { }
135
136 RTSEMRW sem;
137 VBoxLockingClass lockClass;
138
139#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
140 com::Utf8Str strDescription;
141#endif
142};
143
144RWLockHandle::RWLockHandle(VBoxLockingClass lockClass)
145{
146 m = new Data();
147
148 m->lockClass = lockClass;
149
150#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
151 m->strDescription = com::Utf8StrFmt("r/w %RCv", this);
152 int vrc = RTSemRWCreateEx(&m->sem, 0 /*fFlags*/, g_mapLockValidationClasses[lockClass], RTLOCKVAL_SUB_CLASS_ANY, NULL);
153#else
154 int vrc = RTSemRWCreateEx(&m->sem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, RTLOCKVAL_SUB_CLASS_ANY, NULL);
155#endif
156 AssertRC(vrc);
157}
158
159/*virtual*/ RWLockHandle::~RWLockHandle()
160{
161 RTSemRWDestroy(m->sem);
162 delete m;
163}
164
165/*virtual*/ bool RWLockHandle::isWriteLockOnCurrentThread() const
166{
167 return RTSemRWIsWriteOwner(m->sem);
168}
169
170/*virtual*/ void RWLockHandle::lockWrite(LOCKVAL_SRC_POS_DECL)
171{
172#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
173 int vrc = RTSemRWRequestWriteDebug(m->sem, RT_INDEFINITE_WAIT, (uintptr_t)ASMReturnAddress(), RT_SRC_POS_ARGS);
174#else
175 int vrc = RTSemRWRequestWrite(m->sem, RT_INDEFINITE_WAIT);
176#endif
177 AssertRC(vrc);
178}
179
180/*virtual*/ void RWLockHandle::unlockWrite()
181{
182 int vrc = RTSemRWReleaseWrite(m->sem);
183 AssertRC(vrc);
184
185}
186
187/*virtual*/ void RWLockHandle::lockRead(LOCKVAL_SRC_POS_DECL)
188{
189#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
190 int vrc = RTSemRWRequestReadDebug(m->sem, RT_INDEFINITE_WAIT, (uintptr_t)ASMReturnAddress(), RT_SRC_POS_ARGS);
191#else
192 int vrc = RTSemRWRequestRead(m->sem, RT_INDEFINITE_WAIT);
193#endif
194 AssertRC(vrc);
195}
196
197/*virtual*/ void RWLockHandle::unlockRead()
198{
199 int vrc = RTSemRWReleaseRead(m->sem);
200 AssertRC(vrc);
201}
202
203/*virtual*/ uint32_t RWLockHandle::writeLockLevel() const
204{
205 /* Note! This does not include read recursions done by the writer! */
206 return RTSemRWGetWriteRecursion(m->sem);
207}
208
209#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
210/*virtual*/ const char* RWLockHandle::describe() const
211{
212 return m->strDescription.c_str();
213}
214#endif
215
216////////////////////////////////////////////////////////////////////////////////
217//
218// WriteLockHandle
219//
220////////////////////////////////////////////////////////////////////////////////
221
222struct WriteLockHandle::Data
223{
224 Data()
225 { }
226
227 mutable RTCRITSECT sem;
228 VBoxLockingClass lockClass;
229
230#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
231 com::Utf8Str strDescription;
232#endif
233};
234
235WriteLockHandle::WriteLockHandle(VBoxLockingClass lockClass)
236{
237 m = new Data;
238
239 m->lockClass = lockClass;
240
241#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
242 m->strDescription = com::Utf8StrFmt("crit %RCv", this);
243 int vrc = RTCritSectInitEx(&m->sem, 0/*fFlags*/, g_mapLockValidationClasses[lockClass], RTLOCKVAL_SUB_CLASS_ANY, NULL);
244#else
245 int vrc = RTCritSectInitEx(&m->sem, 0/*fFlags*/, NIL_RTLOCKVALCLASS, RTLOCKVAL_SUB_CLASS_ANY, NULL);
246#endif
247 AssertRC(vrc);
248}
249
250WriteLockHandle::~WriteLockHandle()
251{
252 RTCritSectDelete(&m->sem);
253 delete m;
254}
255
256/*virtual*/ bool WriteLockHandle::isWriteLockOnCurrentThread() const
257{
258 return RTCritSectIsOwner(&m->sem);
259}
260
261/*virtual*/ void WriteLockHandle::lockWrite(LOCKVAL_SRC_POS_DECL)
262{
263#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
264 RTCritSectEnterDebug(&m->sem, (uintptr_t)ASMReturnAddress(), RT_SRC_POS_ARGS);
265#else
266 RTCritSectEnter(&m->sem);
267#endif
268}
269
270/*virtual*/ void WriteLockHandle::unlockWrite()
271{
272 RTCritSectLeave(&m->sem);
273}
274
275/*virtual*/ void WriteLockHandle::lockRead(LOCKVAL_SRC_POS_DECL)
276{
277 lockWrite(LOCKVAL_SRC_POS_ARGS);
278}
279
280/*virtual*/ void WriteLockHandle::unlockRead()
281{
282 unlockWrite();
283}
284
285/*virtual*/ uint32_t WriteLockHandle::writeLockLevel() const
286{
287 return RTCritSectGetRecursion(&m->sem);
288}
289
290#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
291/*virtual*/ const char* WriteLockHandle::describe() const
292{
293 return m->strDescription.c_str();
294}
295#endif
296
297////////////////////////////////////////////////////////////////////////////////
298//
299// AutoLockBase
300//
301////////////////////////////////////////////////////////////////////////////////
302
303typedef std::vector<LockHandle*> HandlesVector;
304
305struct AutoLockBase::Data
306{
307 Data(size_t cHandles
308#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
309 , const char *pcszFile_,
310 unsigned uLine_,
311 const char *pcszFunction_
312#endif
313 )
314 : fIsLocked(false),
315 aHandles(cHandles) // size of array
316#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
317 , pcszFile(pcszFile_),
318 uLine(uLine_),
319 pcszFunction(pcszFunction_)
320#endif
321 {
322 for (uint32_t i = 0; i < cHandles; ++i)
323 aHandles[i] = NULL;
324 }
325
326 bool fIsLocked; // if true, then all items in aHandles are locked by this AutoLock and
327 // need to be unlocked in the destructor
328 HandlesVector aHandles; // array (vector) of LockHandle instances; in the case of AutoWriteLock
329 // and AutoReadLock, there will only be one item on the list; with the
330 // AutoMulti* derivatives, there will be multiple
331
332#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
333 // information about where the lock occurred (passed down from the AutoLock classes)
334 const char *pcszFile;
335 unsigned uLine;
336 const char *pcszFunction;
337#endif
338};
339
340AutoLockBase::AutoLockBase(uint32_t cHandles
341 COMMA_LOCKVAL_SRC_POS_DECL)
342{
343 m = new Data(cHandles
344 COMMA_LOCKVAL_SRC_POS_ARGS);
345}
346
347AutoLockBase::AutoLockBase(uint32_t cHandles,
348 LockHandle *pHandle
349 COMMA_LOCKVAL_SRC_POS_DECL)
350{
351 Assert(cHandles == 1);
352 m = new Data(1
353 COMMA_LOCKVAL_SRC_POS_ARGS);
354 m->aHandles[0] = pHandle;
355}
356
357AutoLockBase::~AutoLockBase()
358{
359 delete m;
360}
361
362/**
363 * Requests ownership of all contained lock handles by calling
364 * the pure virtual callLockImpl() function on each of them,
365 * which must be implemented by the descendant class; in the
366 * implementation, AutoWriteLock will request a write lock
367 * whereas AutoReadLock will request a read lock.
368 *
369 * Does *not* modify the lock counts in the member variables.
370 */
371void AutoLockBase::callLockOnAllHandles()
372{
373 for (HandlesVector::iterator it = m->aHandles.begin();
374 it != m->aHandles.end();
375 ++it)
376 {
377 LockHandle *pHandle = *it;
378 if (pHandle)
379 // call virtual function implemented in AutoWriteLock or AutoReadLock
380 this->callLockImpl(*pHandle);
381 }
382}
383
384/**
385 * Releases ownership of all contained lock handles by calling
386 * the pure virtual callUnlockImpl() function on each of them,
387 * which must be implemented by the descendant class; in the
388 * implementation, AutoWriteLock will release a write lock
389 * whereas AutoReadLock will release a read lock.
390 *
391 * Does *not* modify the lock counts in the member variables.
392 */
393void AutoLockBase::callUnlockOnAllHandles()
394{
395 // unlock in reverse order!
396 for (HandlesVector::reverse_iterator it = m->aHandles.rbegin();
397 it != m->aHandles.rend();
398 ++it)
399 {
400 LockHandle *pHandle = *it;
401 if (pHandle)
402 // call virtual function implemented in AutoWriteLock or AutoReadLock
403 this->callUnlockImpl(*pHandle);
404 }
405}
406
407/**
408 * Destructor implementation that can also be called explicitly, if required.
409 * Restores the exact state before the AutoLock was created; that is, unlocks
410 * all contained semaphores.
411 */
412void AutoLockBase::cleanup()
413{
414 if (m->fIsLocked)
415 callUnlockOnAllHandles();
416}
417
418/**
419 * Requests ownership of all contained semaphores. Public method that can
420 * only be called once and that also gets called by the AutoLock constructors.
421 */
422void AutoLockBase::acquire()
423{
424 AssertMsgReturnVoid(!m->fIsLocked, ("m->fIsLocked is true, attempting to lock twice!"));
425 callLockOnAllHandles();
426 m->fIsLocked = true;
427}
428
429/**
430 * Releases ownership of all contained semaphores. Public method.
431 */
432void AutoLockBase::release()
433{
434 AssertMsgReturnVoid(m->fIsLocked, ("m->fIsLocked is false, cannot release!"));
435 callUnlockOnAllHandles();
436 m->fIsLocked = false;
437}
438
439////////////////////////////////////////////////////////////////////////////////
440//
441// AutoReadLock
442//
443////////////////////////////////////////////////////////////////////////////////
444
445/**
446 * Release all read locks acquired by this instance through the #lock()
447 * call and destroys the instance.
448 *
449 * Note that if there there are nested #lock() calls without the
450 * corresponding number of #unlock() calls when the destructor is called, it
451 * will assert. This is because having an unbalanced number of nested locks
452 * is a program logic error which must be fixed.
453 */
454/*virtual*/ AutoReadLock::~AutoReadLock()
455{
456 LockHandle *pHandle = m->aHandles[0];
457
458 if (pHandle)
459 {
460 if (m->fIsLocked)
461 callUnlockImpl(*pHandle);
462 }
463}
464
465/**
466 * Implementation of the pure virtual declared in AutoLockBase.
467 * This gets called by AutoLockBase.acquire() to actually request
468 * the semaphore; in the AutoReadLock implementation, we request
469 * the semaphore in read mode.
470 */
471/*virtual*/ void AutoReadLock::callLockImpl(LockHandle &l)
472{
473#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
474 l.lockRead(m->pcszFile, m->uLine, m->pcszFunction);
475#else
476 l.lockRead();
477#endif
478}
479
480/**
481 * Implementation of the pure virtual declared in AutoLockBase.
482 * This gets called by AutoLockBase.release() to actually release
483 * the semaphore; in the AutoReadLock implementation, we release
484 * the semaphore in read mode.
485 */
486/*virtual*/ void AutoReadLock::callUnlockImpl(LockHandle &l)
487{
488 l.unlockRead();
489}
490
491////////////////////////////////////////////////////////////////////////////////
492//
493// AutoWriteLockBase
494//
495////////////////////////////////////////////////////////////////////////////////
496
497/**
498 * Implementation of the pure virtual declared in AutoLockBase.
499 * This gets called by AutoLockBase.acquire() to actually request
500 * the semaphore; in the AutoWriteLock implementation, we request
501 * the semaphore in write mode.
502 */
503/*virtual*/ void AutoWriteLockBase::callLockImpl(LockHandle &l)
504{
505#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
506 l.lockWrite(m->pcszFile, m->uLine, m->pcszFunction);
507#else
508 l.lockWrite();
509#endif
510}
511
512/**
513 * Implementation of the pure virtual declared in AutoLockBase.
514 * This gets called by AutoLockBase.release() to actually release
515 * the semaphore; in the AutoWriteLock implementation, we release
516 * the semaphore in write mode.
517 */
518/*virtual*/ void AutoWriteLockBase::callUnlockImpl(LockHandle &l)
519{
520 l.unlockWrite();
521}
522
523////////////////////////////////////////////////////////////////////////////////
524//
525// AutoWriteLock
526//
527////////////////////////////////////////////////////////////////////////////////
528
529AutoWriteLock::AutoWriteLock(uint32_t cHandles,
530 LockHandle** pHandles
531 COMMA_LOCKVAL_SRC_POS_DECL)
532 : AutoWriteLockBase(cHandles
533 COMMA_LOCKVAL_SRC_POS_ARGS)
534{
535 Assert(cHandles);
536 Assert(pHandles);
537
538 for (uint32_t i = 0; i < cHandles; ++i)
539 m->aHandles[i] = pHandles[i];
540
541 acquire();
542}
543
544
545
546/**
547 * Attaches another handle to this auto lock instance.
548 *
549 * The previous object's lock is completely released before the new one is
550 * acquired. The lock level of the new handle will be the same. This
551 * also means that if the lock was not acquired at all before #attach(), it
552 * will not be acquired on the new handle too.
553 *
554 * @param aHandle New handle to attach.
555 */
556void AutoWriteLock::attach(LockHandle *aHandle)
557{
558 LockHandle *pHandle = m->aHandles[0];
559
560 /* detect simple self-reattachment */
561 if (pHandle != aHandle)
562 {
563 bool fWasLocked = m->fIsLocked;
564
565 cleanup();
566
567 m->aHandles[0] = aHandle;
568 m->fIsLocked = fWasLocked;
569
570 if (aHandle)
571 if (fWasLocked)
572 callLockImpl(*aHandle);
573 }
574}
575
576/**
577 * Returns @c true if the current thread holds a write lock on the managed
578 * read/write semaphore. Returns @c false if the managed semaphore is @c
579 * NULL.
580 *
581 * @note Intended for debugging only.
582 */
583bool AutoWriteLock::isWriteLockOnCurrentThread() const
584{
585 return m->aHandles[0] ? m->aHandles[0]->isWriteLockOnCurrentThread() : false;
586}
587
588 /**
589 * Returns the current write lock level of the managed semaphore. The lock
590 * level determines the number of nested #lock() calls on the given
591 * semaphore handle. Returns @c 0 if the managed semaphore is @c
592 * NULL.
593 *
594 * Note that this call is valid only when the current thread owns a write
595 * lock on the given semaphore handle and will assert otherwise.
596 *
597 * @note Intended for debugging only.
598 */
599uint32_t AutoWriteLock::writeLockLevel() const
600{
601 return m->aHandles[0] ? m->aHandles[0]->writeLockLevel() : 0;
602}
603
604////////////////////////////////////////////////////////////////////////////////
605//
606// AutoMultiWriteLock*
607//
608////////////////////////////////////////////////////////////////////////////////
609
610AutoMultiWriteLock2::AutoMultiWriteLock2(Lockable *pl1,
611 Lockable *pl2
612 COMMA_LOCKVAL_SRC_POS_DECL)
613 : AutoWriteLockBase(2
614 COMMA_LOCKVAL_SRC_POS_ARGS)
615{
616 if (pl1)
617 m->aHandles[0] = pl1->lockHandle();
618 if (pl2)
619 m->aHandles[1] = pl2->lockHandle();
620 acquire();
621}
622
623AutoMultiWriteLock2::AutoMultiWriteLock2(LockHandle *pl1,
624 LockHandle *pl2
625 COMMA_LOCKVAL_SRC_POS_DECL)
626 : AutoWriteLockBase(2
627 COMMA_LOCKVAL_SRC_POS_ARGS)
628{
629 m->aHandles[0] = pl1;
630 m->aHandles[1] = pl2;
631 acquire();
632}
633
634AutoMultiWriteLock3::AutoMultiWriteLock3(Lockable *pl1,
635 Lockable *pl2,
636 Lockable *pl3
637 COMMA_LOCKVAL_SRC_POS_DECL)
638 : AutoWriteLockBase(3
639 COMMA_LOCKVAL_SRC_POS_ARGS)
640{
641 if (pl1)
642 m->aHandles[0] = pl1->lockHandle();
643 if (pl2)
644 m->aHandles[1] = pl2->lockHandle();
645 if (pl3)
646 m->aHandles[2] = pl3->lockHandle();
647 acquire();
648}
649
650AutoMultiWriteLock3::AutoMultiWriteLock3(LockHandle *pl1,
651 LockHandle *pl2,
652 LockHandle *pl3
653 COMMA_LOCKVAL_SRC_POS_DECL)
654 : AutoWriteLockBase(3
655 COMMA_LOCKVAL_SRC_POS_ARGS)
656{
657 m->aHandles[0] = pl1;
658 m->aHandles[1] = pl2;
659 m->aHandles[2] = pl3;
660 acquire();
661}
662
663AutoMultiWriteLock4::AutoMultiWriteLock4(Lockable *pl1,
664 Lockable *pl2,
665 Lockable *pl3,
666 Lockable *pl4
667 COMMA_LOCKVAL_SRC_POS_DECL)
668 : AutoWriteLockBase(4
669 COMMA_LOCKVAL_SRC_POS_ARGS)
670{
671 if (pl1)
672 m->aHandles[0] = pl1->lockHandle();
673 if (pl2)
674 m->aHandles[1] = pl2->lockHandle();
675 if (pl3)
676 m->aHandles[2] = pl3->lockHandle();
677 if (pl4)
678 m->aHandles[3] = pl4->lockHandle();
679 acquire();
680}
681
682AutoMultiWriteLock4::AutoMultiWriteLock4(LockHandle *pl1,
683 LockHandle *pl2,
684 LockHandle *pl3,
685 LockHandle *pl4
686 COMMA_LOCKVAL_SRC_POS_DECL)
687 : AutoWriteLockBase(4
688 COMMA_LOCKVAL_SRC_POS_ARGS)
689{
690 m->aHandles[0] = pl1;
691 m->aHandles[1] = pl2;
692 m->aHandles[2] = pl3;
693 m->aHandles[3] = pl4;
694 acquire();
695}
696
697} /* namespace util */
698/* vi: set tabstop=4 shiftwidth=4 expandtab: */
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