VirtualBox

source: vbox/trunk/src/VBox/Main/EventImpl.cpp@ 30477

最後變更 在這個檔案從30477是 30477,由 vboxsync 提交於 15 年 前

Main: finer events locks

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 20.1 KB
 
1/* $Id: EventImpl.cpp 30477 2010-06-28 16:56:57Z vboxsync $ */
2/** @file
3 * VirtualBox COM Event class implementation
4 */
5
6/*
7 * Copyright (C) 2010 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.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 <list>
19#include <map>
20#include <deque>
21
22#include "EventImpl.h"
23#include "AutoCaller.h"
24#include "Logging.h"
25
26#include <iprt/semaphore.h>
27#include <iprt/critsect.h>
28#include <iprt/asm.h>
29
30#include <VBox/com/array.h>
31
32struct VBoxEvent::Data
33{
34 Data()
35 :
36 mType(VBoxEventType_Invalid),
37 mWaitEvent(NIL_RTSEMEVENT),
38 mWaitable(FALSE),
39 mProcessed(FALSE)
40 {}
41 VBoxEventType_T mType;
42 RTSEMEVENT mWaitEvent;
43 BOOL mWaitable;
44 BOOL mProcessed;
45 ComPtr<IEventSource> mSource;
46};
47
48HRESULT VBoxEvent::FinalConstruct()
49{
50 m = new Data;
51 return S_OK;
52}
53
54void VBoxEvent::FinalRelease()
55{
56 if (m)
57 {
58 uninit();
59 delete m;
60 m = 0;
61 }
62}
63
64
65HRESULT VBoxEvent::init(IEventSource *aSource, VBoxEventType_T aType, BOOL aWaitable)
66{
67 HRESULT rc = S_OK;
68
69 AssertReturn(aSource != NULL, E_INVALIDARG);
70
71 AutoInitSpan autoInitSpan(this);
72 AssertReturn(autoInitSpan.isOk(), E_FAIL);
73
74 m->mSource = aSource;
75 m->mType = aType;
76 m->mWaitable = aWaitable;
77 m->mProcessed = !aWaitable;
78
79 do {
80 if (aWaitable)
81 {
82 int vrc = ::RTSemEventCreate (&m->mWaitEvent);
83
84 if (RT_FAILURE(vrc))
85 {
86 AssertFailed ();
87 return setError(E_FAIL,
88 tr("Internal error (%Rrc)"), vrc);
89 }
90 }
91 } while (0);
92
93 /* Confirm a successful initialization */
94 autoInitSpan.setSucceeded();
95
96 return rc;
97}
98
99void VBoxEvent::uninit()
100{
101 if (!m)
102 return;
103
104 m->mProcessed = TRUE;
105 m->mType = VBoxEventType_Invalid;
106 m->mSource.setNull();
107
108 if (m->mWaitEvent != NIL_RTSEMEVENT)
109 {
110 Assert(m->mWaitable);
111 ::RTSemEventDestroy(m->mWaitEvent);
112 m->mWaitEvent = NIL_RTSEMEVENT;
113 }
114}
115
116STDMETHODIMP VBoxEvent::COMGETTER(Type)(VBoxEventType_T *aType)
117{
118 CheckComArgNotNull(aType);
119
120 AutoCaller autoCaller(this);
121 if (FAILED(autoCaller.rc())) return autoCaller.rc();
122
123 // never changes till event alive, no locking?
124 *aType = m->mType;
125 return S_OK;
126}
127
128STDMETHODIMP VBoxEvent::COMGETTER(Source)(IEventSource* *aSource)
129{
130 CheckComArgOutPointerValid(aSource);
131
132 AutoCaller autoCaller(this);
133 if (FAILED(autoCaller.rc())) return autoCaller.rc();
134
135 m->mSource.queryInterfaceTo(aSource);
136 return S_OK;
137}
138
139STDMETHODIMP VBoxEvent::COMGETTER(Waitable)(BOOL *aWaitable)
140{
141 CheckComArgNotNull(aWaitable);
142
143 AutoCaller autoCaller(this);
144 if (FAILED(autoCaller.rc())) return autoCaller.rc();
145
146 // never changes till event alive, no locking?
147 *aWaitable = m->mWaitable;
148 return S_OK;
149}
150
151
152STDMETHODIMP VBoxEvent::SetProcessed()
153{
154 AutoCaller autoCaller(this);
155 if (FAILED(autoCaller.rc())) return autoCaller.rc();
156
157 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
158
159 if (m->mProcessed)
160 return S_OK;
161
162 m->mProcessed = TRUE;
163
164 // notify waiters
165 ::RTSemEventSignal(m->mWaitEvent);
166
167 return S_OK;
168}
169
170STDMETHODIMP VBoxEvent::WaitProcessed(LONG aTimeout, BOOL *aResult)
171{
172 CheckComArgNotNull(aResult);
173
174 AutoCaller autoCaller(this);
175 if (FAILED(autoCaller.rc())) return autoCaller.rc();
176
177 {
178 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
179
180 if (m->mProcessed)
181 {
182 *aResult = TRUE;
183 return S_OK;
184 }
185
186 if (aTimeout == 0)
187 {
188 *aResult = m->mProcessed;
189 return S_OK;
190 }
191 }
192
193 int vrc = ::RTSemEventWait(m->mWaitEvent, aTimeout);
194 AssertMsg(RT_SUCCESS(vrc) || vrc == VERR_TIMEOUT || vrc == VERR_INTERRUPTED,
195 ("RTSemEventWait returned %Rrc\n", vrc));
196
197 if (RT_SUCCESS(vrc))
198 {
199 AssertMsg(m->mProcessed,
200 ("mProcessed must be set here\n"));
201 *aResult = m->mProcessed;
202 }
203 else
204 {
205 *aResult = FALSE;
206 }
207
208 return S_OK;
209}
210
211static const int FirstEvent = (int)VBoxEventType_LastWildcard + 1;
212static const int LastEvent = (int)VBoxEventType_Last;
213static const int NumEvents = LastEvent - FirstEvent;
214
215class ListenerRecord;
216typedef std::list<ListenerRecord*> EventMap[NumEvents];
217typedef std::map<IEvent*, int32_t> PendingEventsMap;
218typedef std::deque<ComPtr<IEvent> > PassiveQueue;
219
220class ListenerRecord
221{
222private:
223 ComPtr<IEventListener> mListener;
224 BOOL mActive;
225 EventSource* mOwner;
226
227 RTSEMEVENT mQEvent;
228 RTCRITSECT mcsQLock;
229 PassiveQueue mQueue;
230 int32_t mRefCnt;
231
232public:
233 ListenerRecord(IEventListener* aListener,
234 com::SafeArray<VBoxEventType_T>& aInterested,
235 BOOL aActive,
236 EventSource* aOwner);
237 ~ListenerRecord();
238
239 HRESULT process(IEvent* aEvent, BOOL aWaitable, PendingEventsMap::iterator& pit, AutoLockBase& alock);
240 HRESULT enqueue(IEvent* aEvent);
241 HRESULT dequeue(IEvent* *aEvent, LONG aTimeout, AutoLockBase& aAlock);
242 HRESULT eventProcessed(IEvent * aEvent, PendingEventsMap::iterator& pit);
243 void addRef()
244 {
245 ASMAtomicIncS32(&mRefCnt);
246 }
247 void release()
248 {
249 if (ASMAtomicDecS32(&mRefCnt) <= 0) delete this;
250 }
251 BOOL isActive()
252 {
253 return mActive;
254 }
255};
256
257/* Handy class with semantics close to ComPtr, but for ListenerRecord */
258class ListenerRecordHolder
259{
260public:
261 ListenerRecordHolder(ListenerRecord* lr)
262 :
263 held(lr)
264 {
265 addref();
266 }
267 ListenerRecordHolder(const ListenerRecordHolder& that)
268 :
269 held(that.held)
270 {
271 addref();
272 }
273 ListenerRecordHolder()
274 :
275 held(0)
276 {
277 }
278 ~ListenerRecordHolder()
279 {
280 release();
281 }
282
283 ListenerRecord* obj()
284 {
285 return held;
286 }
287
288 ListenerRecordHolder &operator=(const ListenerRecordHolder &that)
289 {
290 safe_assign(that.held);
291 return *this;
292 }
293private:
294 ListenerRecord* held;
295
296 void addref()
297 {
298 if (held)
299 held->addRef();
300 }
301 void release()
302 {
303 if (held)
304 held->release();
305 }
306 void safe_assign (ListenerRecord *that_p)
307 {
308 if (that_p)
309 that_p->addRef();
310 release();
311 held = that_p;
312 }
313};
314
315typedef std::map<IEventListener*, ListenerRecordHolder> Listeners;
316
317struct EventSource::Data
318{
319 Data() {}
320 Listeners mListeners;
321 EventMap mEvMap;
322 PendingEventsMap mPendingMap;
323};
324
325/**
326 * This function defines what wildcard expands to.
327 */
328static BOOL implies(VBoxEventType_T who, VBoxEventType_T what)
329{
330 switch (who)
331 {
332 case VBoxEventType_Any:
333 return TRUE;
334 case VBoxEventType_MachineEvent:
335 return (what == VBoxEventType_OnMachineStateChange)
336 || (what == VBoxEventType_OnMachineDataChange)
337 || (what == VBoxEventType_OnMachineRegistered)
338 || (what == VBoxEventType_OnSessionStateChange)
339 || (what == VBoxEventType_OnGuestPropertyChange);
340 case VBoxEventType_SnapshotEvent:
341 return (what == VBoxEventType_OnSnapshotTaken)
342 || (what == VBoxEventType_OnSnapshotDeleted)
343 || (what == VBoxEventType_OnSnapshotChange)
344 ;
345 case VBoxEventType_Invalid:
346 return FALSE;
347 }
348 return who == what;
349}
350
351ListenerRecord::ListenerRecord(IEventListener* aListener,
352 com::SafeArray<VBoxEventType_T>& aInterested,
353 BOOL aActive,
354 EventSource* aOwner)
355 :
356 mActive(aActive),
357 mOwner(aOwner),
358 mRefCnt(0)
359{
360 mListener = aListener;
361 EventMap* aEvMap = &aOwner->m->mEvMap;
362
363 for (size_t i = 0; i < aInterested.size(); ++i)
364 {
365 VBoxEventType_T interested = aInterested[i];
366 for (int j = FirstEvent; j < LastEvent; j++)
367 {
368 VBoxEventType_T candidate = (VBoxEventType_T)j;
369 if (implies(interested, candidate))
370 {
371 (*aEvMap)[j - FirstEvent].push_back(this);
372 }
373 }
374 }
375
376 if (!mActive)
377 {
378 ::RTCritSectInit(&mcsQLock);
379 ::RTSemEventCreate (&mQEvent);
380 }
381}
382
383ListenerRecord::~ListenerRecord()
384{
385 /* Remove references to us from the event map */
386 EventMap* aEvMap = &mOwner->m->mEvMap;
387 for (int j = FirstEvent; j < LastEvent; j++)
388 {
389 (*aEvMap)[j - FirstEvent].remove(this);
390 }
391
392 if (!mActive)
393 {
394 ::RTCritSectDelete(&mcsQLock);
395 ::RTSemEventDestroy(mQEvent);
396 }
397}
398
399HRESULT ListenerRecord::process(IEvent* aEvent,
400 BOOL aWaitable,
401 PendingEventsMap::iterator& pit,
402 AutoLockBase& aAlock)
403{
404 if (mActive)
405 {
406 /*
407 * We release lock here to allow modifying ops on EventSource inside callback.
408 */
409 HRESULT rc = S_OK;
410 if (mListener)
411 {
412 aAlock.release();
413 rc = mListener->HandleEvent(aEvent);
414 aAlock.acquire();
415 }
416 if (aWaitable)
417 eventProcessed(aEvent, pit);
418 return rc;
419 }
420 else
421 return enqueue(aEvent);
422}
423
424
425HRESULT ListenerRecord::enqueue (IEvent* aEvent)
426{
427 AssertMsg(!mActive, ("must be passive\n"));
428 // put an event the queue
429 ::RTCritSectEnter(&mcsQLock);
430 mQueue.push_back(aEvent);
431 ::RTCritSectLeave(&mcsQLock);
432
433 // notify waiters
434 ::RTSemEventSignal(mQEvent);
435
436 return S_OK;
437}
438
439HRESULT ListenerRecord::dequeue (IEvent* *aEvent,
440 LONG aTimeout,
441 AutoLockBase& aAlock)
442{
443 AssertMsg(!mActive, ("must be passive\n"));
444
445 ::RTCritSectEnter(&mcsQLock);
446 if (mQueue.empty())
447 {
448 // retain listener record
449 ListenerRecordHolder holder(this);
450 ::RTCritSectLeave(&mcsQLock);
451 // Speed up common case
452 if (aTimeout == 0)
453 {
454 *aEvent = NULL;
455 return S_OK;
456 }
457 // release lock while waiting, listener will not go away due to above holder
458 aAlock.release();
459 ::RTSemEventWait(mQEvent, aTimeout);
460 // reacquire lock
461 aAlock.acquire();
462 ::RTCritSectEnter(&mcsQLock);
463 }
464 if (mQueue.empty())
465 {
466 *aEvent = NULL;
467 }
468 else
469 {
470 mQueue.front().queryInterfaceTo(aEvent);
471 mQueue.pop_front();
472 }
473 ::RTCritSectLeave(&mcsQLock);
474 return S_OK;
475}
476
477HRESULT ListenerRecord::eventProcessed (IEvent* aEvent, PendingEventsMap::iterator& pit)
478{
479 if (--pit->second == 0)
480 {
481 Assert(pit->first == aEvent);
482 aEvent->SetProcessed();
483 mOwner->m->mPendingMap.erase(pit);
484 }
485
486 Assert(pit->second >= 0);
487 return S_OK;
488}
489
490EventSource::EventSource()
491{}
492
493EventSource::~EventSource()
494{}
495
496HRESULT EventSource::FinalConstruct()
497{
498 m = new Data;
499 return S_OK;
500}
501
502void EventSource::FinalRelease()
503{
504 uninit();
505 delete m;
506}
507
508HRESULT EventSource::init(IUnknown *)
509{
510 HRESULT rc = S_OK;
511
512 AutoInitSpan autoInitSpan(this);
513 AssertReturn(autoInitSpan.isOk(), E_FAIL);
514
515 /* Confirm a successful initialization */
516 autoInitSpan.setSucceeded();
517 return rc;
518}
519
520void EventSource::uninit()
521{
522 m->mListeners.clear();
523 // m->mEvMap shall be cleared at this point too by destructors, assert?
524}
525
526STDMETHODIMP EventSource::RegisterListener(IEventListener * aListener,
527 ComSafeArrayIn(VBoxEventType_T, aInterested),
528 BOOL aActive)
529{
530 CheckComArgNotNull(aListener);
531 CheckComArgSafeArrayNotNull(aInterested);
532
533 AutoCaller autoCaller(this);
534 if (FAILED(autoCaller.rc())) return autoCaller.rc();
535
536 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
537
538 Listeners::const_iterator it = m->mListeners.find(aListener);
539 if (it != m->mListeners.end())
540 return setError(E_INVALIDARG,
541 tr("This listener already registered"));
542
543 com::SafeArray<VBoxEventType_T> interested(ComSafeArrayInArg (aInterested));
544 ListenerRecordHolder lrh(new ListenerRecord(aListener, interested, aActive, this));
545 m->mListeners.insert(Listeners::value_type(aListener, lrh));
546
547 return S_OK;
548}
549
550STDMETHODIMP EventSource::UnregisterListener(IEventListener * aListener)
551{
552 CheckComArgNotNull(aListener);
553
554 AutoCaller autoCaller(this);
555 if (FAILED(autoCaller.rc())) return autoCaller.rc();
556
557 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
558
559 Listeners::iterator it = m->mListeners.find(aListener);
560 HRESULT rc;
561
562 if (it != m->mListeners.end())
563 {
564 m->mListeners.erase(it);
565 // destructor removes refs from the event map
566 rc = S_OK;
567 }
568 else
569 {
570 rc = setError(VBOX_E_OBJECT_NOT_FOUND,
571 tr("Listener was never registered"));
572 }
573
574 return rc;
575}
576
577STDMETHODIMP EventSource::FireEvent(IEvent * aEvent,
578 LONG aTimeout,
579 BOOL *aProcessed)
580{
581 CheckComArgNotNull(aEvent);
582 CheckComArgOutPointerValid(aProcessed);
583
584 AutoCaller autoCaller(this);
585 if (FAILED(autoCaller.rc())) return autoCaller.rc();
586
587 HRESULT hrc;
588 BOOL aWaitable = FALSE;
589 aEvent->COMGETTER(Waitable)(&aWaitable);
590
591 do {
592 /* See comment in EventSource::GetEvent() */
593 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
594
595 VBoxEventType_T evType;
596 hrc = aEvent->COMGETTER(Type)(&evType);
597 AssertComRCReturn(hrc, VERR_ACCESS_DENIED);
598
599 std::list<ListenerRecord*>& listeners = m->mEvMap[(int)evType-FirstEvent];
600
601 /* Anyone interested in this event? */
602 uint32_t cListeners = listeners.size();
603 if (cListeners == 0)
604 break; // just leave the lock and update event object state
605
606 PendingEventsMap::iterator pit;
607
608 if (aWaitable)
609 {
610 m->mPendingMap.insert(PendingEventsMap::value_type(aEvent, cListeners));
611 // we keep iterator here to allow processing active listeners without
612 // pending events lookup
613 pit = m->mPendingMap.find(aEvent);
614 }
615 for(std::list<ListenerRecord*>::const_iterator it = listeners.begin();
616 it != listeners.end(); ++it)
617 {
618 HRESULT cbRc;
619 // keep listener record reference, in case someone will remove it while in callback
620 ListenerRecordHolder record(*it);
621
622 /**
623 * We pass lock here to allow modifying ops on EventSource inside callback
624 * in active mode. Note that we expect list iterator stability as 'alock'
625 * could be temporary released when calling event handler.
626 */
627 cbRc = record.obj()->process(aEvent, aWaitable, pit, alock);
628 // what to do with cbRc?
629 }
630 } while (0);
631 /* We leave the lock here */
632
633 if (aWaitable)
634 hrc = aEvent->WaitProcessed(aTimeout, aProcessed);
635 else
636 *aProcessed = TRUE;
637
638 return hrc;
639}
640
641
642STDMETHODIMP EventSource::GetEvent(IEventListener * aListener,
643 LONG aTimeout,
644 IEvent ** aEvent)
645{
646
647 CheckComArgNotNull(aListener);
648
649 AutoCaller autoCaller(this);
650 if (FAILED(autoCaller.rc())) return autoCaller.rc();
651
652 /**
653 * There's subtle dependency between this lock and one in FireEvent():
654 * we need to be able to access event queue in FireEvent() while waiting
655 * here, to make this wait preemptible, thus both take read lock (write
656 * lock in FireEvent() would do too, and probably is a bit stricter),
657 * but will be unable to .
658 */
659 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
660
661 Listeners::iterator it = m->mListeners.find(aListener);
662 HRESULT rc;
663
664 if (it != m->mListeners.end())
665 rc = it->second.obj()->dequeue(aEvent, aTimeout, alock);
666 else
667 rc = setError(VBOX_E_OBJECT_NOT_FOUND,
668 tr("Listener was never registered"));
669
670 return rc;
671}
672
673STDMETHODIMP EventSource::EventProcessed(IEventListener * aListener,
674 IEvent * aEvent)
675{
676 CheckComArgNotNull(aListener);
677 CheckComArgNotNull(aEvent);
678
679 AutoCaller autoCaller(this);
680 if (FAILED(autoCaller.rc())) return autoCaller.rc();
681
682 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
683
684 Listeners::iterator it = m->mListeners.find(aListener);
685 HRESULT rc;
686
687 BOOL aWaitable = FALSE;
688 aEvent->COMGETTER(Waitable)(&aWaitable);
689
690 if (it != m->mListeners.end())
691 {
692 ListenerRecord* aRecord = it->second.obj();
693
694 if (aRecord->isActive())
695 return setError(E_INVALIDARG,
696 tr("Only applicable to passive listeners"));
697
698 if (aWaitable)
699 {
700 PendingEventsMap::iterator pit = m->mPendingMap.find(aEvent);
701
702 if (pit == m->mPendingMap.end())
703 {
704 AssertFailed();
705 rc = setError(VBOX_E_OBJECT_NOT_FOUND,
706 tr("Unknown event"));
707 }
708 else
709 rc = aRecord->eventProcessed(aEvent, pit);
710 }
711 else
712 {
713 // for non-waitable events we're done
714 rc = S_OK;
715 }
716 }
717 else
718 {
719 rc = setError(VBOX_E_OBJECT_NOT_FOUND,
720 tr("Listener was never registered"));
721 }
722
723 return rc;
724}
725
726/**
727 * This class serves as feasible listener implementation
728 * which could be used by clients not able to create local
729 * COM objects, but still willing to recieve event
730 * notifications in passive mode, such as webservices.
731 */
732class ATL_NO_VTABLE PassiveEventListener :
733 public VirtualBoxBase,
734 public VirtualBoxSupportErrorInfoImpl<PassiveEventListener, IEventListener>,
735 public VirtualBoxSupportTranslation<PassiveEventListener>,
736 VBOX_SCRIPTABLE_IMPL(IEventListener)
737{
738public:
739
740 VIRTUALBOXBASE_ADD_ERRORINFO_SUPPORT(PassiveEventListener)
741
742 DECLARE_NOT_AGGREGATABLE(PassiveEventListener)
743
744 DECLARE_PROTECT_FINAL_CONSTRUCT()
745
746 BEGIN_COM_MAP(PassiveEventListener)
747 COM_INTERFACE_ENTRY(ISupportErrorInfo)
748 COM_INTERFACE_ENTRY(IEventListener)
749 COM_INTERFACE_ENTRY(IDispatch)
750 END_COM_MAP()
751
752 PassiveEventListener()
753 {}
754 ~PassiveEventListener()
755 {}
756
757 HRESULT FinalConstruct()
758 {
759 return S_OK;
760 }
761 void FinalRelease()
762 {}
763
764 // IEventListener methods
765 STDMETHOD(HandleEvent)(IEvent *)
766 {
767 ComAssertMsgRet(false, ("HandleEvent() of wrapper shall never be called"),
768 E_FAIL);
769 }
770 // for VirtualBoxSupportErrorInfoImpl
771 static const wchar_t *getComponentName() { return L"PassiveEventListener"; }
772};
773
774#ifdef VBOX_WITH_XPCOM
775NS_DECL_CLASSINFO(PassiveEventListener)
776NS_IMPL_THREADSAFE_ISUPPORTS1_CI(PassiveEventListener, IEventListener)
777NS_DECL_CLASSINFO(VBoxEvent)
778NS_IMPL_THREADSAFE_ISUPPORTS1_CI(VBoxEvent, IEvent)
779#endif
780
781STDMETHODIMP EventSource::CreateListener(IEventListener ** aListener)
782{
783 CheckComArgOutPointerValid(aListener);
784
785 AutoCaller autoCaller(this);
786 if (FAILED(autoCaller.rc())) return autoCaller.rc();
787
788 ComObjPtr<PassiveEventListener> listener;
789
790 HRESULT rc = listener.createObject();
791 ComAssertMsgRet(SUCCEEDED(rc), ("Could not create wrapper object (%Rrc)", rc),
792 E_FAIL);
793 listener.queryInterfaceTo(aListener);
794 return S_OK;
795}
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette