VirtualBox

source: vbox/trunk/src/VBox/Main/src-all/EventImpl.cpp@ 52667

最後變更 在這個檔案從52667是 52413,由 vboxsync 提交於 11 年 前

VBoxSVC: resolve undesirable freeze on shutdown.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 43.6 KB
 
1/* $Id: EventImpl.cpp 52413 2014-08-19 12:48:42Z vboxsync $ */
2/** @file
3 * VirtualBox COM Event class implementation
4 */
5
6/*
7 * Copyright (C) 2010-2014 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/** @page pg_main_events Events
19 *
20 * Theory of operations.
21 *
22 * This code implements easily extensible event mechanism, letting us
23 * to make any VirtualBox object an event source (by aggregating an EventSource instance).
24 * Another entity could subscribe to the event source for events it is interested in.
25 * If an event is waitable, it's possible to wait until all listeners
26 * registered at the moment of firing event as ones interested in this
27 * event acknowledged that they finished event processing (thus allowing
28 * vetoable events).
29 *
30 * Listeners can be registered as active or passive ones, defining policy of delivery.
31 * For *active* listeners, their HandleEvent() method is invoked when event is fired by
32 * the event source (pretty much callbacks).
33 * For *passive* listeners, it's up to an event consumer to perform GetEvent() operation
34 * with given listener, and then perform desired operation with returned event, if any.
35 * For passive listeners case, listener instance serves as merely a key referring to
36 * particular event consumer, thus HandleEvent() implementation isn't that important.
37 * IEventSource's CreateListener() could be used to create such a listener.
38 * Passive mode is designed for transports not allowing callbacks, such as webservices
39 * running on top of HTTP, and for situations where consumer wants exact control on
40 * context where event handler is executed (such as GUI thread for some toolkits).
41 *
42 * Internal EventSource data structures are optimized for fast event delivery, while
43 * listener registration/unregistration operations are expected being pretty rare.
44 * Passive mode listeners keep an internal event queue for all events they receive,
45 * and all waitable events are added to the pending events map. This map keeps track
46 * of how many listeners are still not acknowledged their event, and once this counter
47 * reach zero, element is removed from pending events map, and event is marked as processed.
48 * Thus if passive listener's user forgets to call IEventSource's EventProcessed()
49 * waiters may never know that event processing finished.
50 */
51
52#include <list>
53#include <map>
54#include <deque>
55
56#include "EventImpl.h"
57#include "AutoCaller.h"
58#include "Logging.h"
59
60#include <iprt/semaphore.h>
61#include <iprt/critsect.h>
62#include <iprt/asm.h>
63#include <iprt/time.h>
64
65#include <VBox/com/array.h>
66
67class ListenerRecord;
68
69struct VBoxEvent::Data
70{
71 Data()
72 : mType(VBoxEventType_Invalid),
73 mWaitEvent(NIL_RTSEMEVENT),
74 mWaitable(FALSE),
75 mProcessed(FALSE)
76 {}
77
78 VBoxEventType_T mType;
79 RTSEMEVENT mWaitEvent;
80 BOOL mWaitable;
81 BOOL mProcessed;
82 ComPtr<IEventSource> mSource;
83};
84
85HRESULT VBoxEvent::FinalConstruct()
86{
87 m = new Data;
88 return BaseFinalConstruct();
89}
90
91void VBoxEvent::FinalRelease()
92{
93 if (m)
94 {
95 uninit();
96 delete m;
97 m = 0;
98 BaseFinalRelease();
99 }
100}
101
102HRESULT VBoxEvent::init(IEventSource *aSource, VBoxEventType_T aType, BOOL aWaitable)
103{
104 HRESULT rc = S_OK;
105
106 AssertReturn(aSource != NULL, E_INVALIDARG);
107
108 AutoInitSpan autoInitSpan(this);
109 AssertReturn(autoInitSpan.isOk(), E_FAIL);
110
111 m->mSource = aSource;
112 m->mType = aType;
113 m->mWaitable = aWaitable;
114 m->mProcessed = !aWaitable;
115
116 do {
117 if (aWaitable)
118 {
119 int vrc = ::RTSemEventCreate(&m->mWaitEvent);
120
121 if (RT_FAILURE(vrc))
122 {
123 AssertFailed();
124 return setError(E_FAIL,
125 tr("Internal error (%Rrc)"), vrc);
126 }
127 }
128 } while (0);
129
130 /* Confirm a successful initialization */
131 autoInitSpan.setSucceeded();
132
133 return rc;
134}
135
136void VBoxEvent::uninit()
137{
138 if (!m)
139 return;
140
141 m->mProcessed = TRUE;
142 m->mType = VBoxEventType_Invalid;
143 m->mSource.setNull();
144
145 if (m->mWaitEvent != NIL_RTSEMEVENT)
146 {
147 Assert(m->mWaitable);
148 ::RTSemEventDestroy(m->mWaitEvent);
149 m->mWaitEvent = NIL_RTSEMEVENT;
150 }
151}
152
153STDMETHODIMP VBoxEvent::COMGETTER(Type)(VBoxEventType_T *aType)
154{
155 CheckComArgNotNull(aType);
156
157 AutoCaller autoCaller(this);
158 if (FAILED(autoCaller.rc()))
159 return autoCaller.rc();
160
161 // never changes while event alive, no locking
162 *aType = m->mType;
163 return S_OK;
164}
165
166STDMETHODIMP VBoxEvent::COMGETTER(Source)(IEventSource **aSource)
167{
168 CheckComArgOutPointerValid(aSource);
169
170 AutoCaller autoCaller(this);
171 if (FAILED(autoCaller.rc()))
172 return autoCaller.rc();
173
174 m->mSource.queryInterfaceTo(aSource);
175 return S_OK;
176}
177
178STDMETHODIMP VBoxEvent::COMGETTER(Waitable)(BOOL *aWaitable)
179{
180 CheckComArgNotNull(aWaitable);
181
182 AutoCaller autoCaller(this);
183 if (FAILED(autoCaller.rc()))
184 return autoCaller.rc();
185
186 // never changes while event alive, no locking
187 *aWaitable = m->mWaitable;
188 return S_OK;
189}
190
191
192STDMETHODIMP VBoxEvent::SetProcessed()
193{
194 AutoCaller autoCaller(this);
195 if (FAILED(autoCaller.rc()))
196 return autoCaller.rc();
197
198 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
199
200 if (m->mProcessed)
201 return S_OK;
202
203 m->mProcessed = TRUE;
204
205 // notify waiters
206 ::RTSemEventSignal(m->mWaitEvent);
207
208 return S_OK;
209}
210
211STDMETHODIMP VBoxEvent::WaitProcessed(LONG aTimeout, BOOL *aResult)
212{
213 CheckComArgNotNull(aResult);
214
215 AutoCaller autoCaller(this);
216 if (FAILED(autoCaller.rc()))
217 return autoCaller.rc();
218
219 {
220 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
221
222 if (m->mProcessed)
223 {
224 *aResult = TRUE;
225 return S_OK;
226 }
227
228 if (aTimeout == 0)
229 {
230 *aResult = m->mProcessed;
231 return S_OK;
232 }
233 }
234
235 /* @todo: maybe while loop for spurious wakeups? */
236 int vrc = ::RTSemEventWait(m->mWaitEvent, aTimeout);
237 AssertMsg(RT_SUCCESS(vrc) || vrc == VERR_TIMEOUT || vrc == VERR_INTERRUPTED,
238 ("RTSemEventWait returned %Rrc\n", vrc));
239
240 if (RT_SUCCESS(vrc))
241 {
242 AssertMsg(m->mProcessed,
243 ("mProcessed must be set here\n"));
244 *aResult = m->mProcessed;
245 }
246 else
247 {
248 *aResult = FALSE;
249 }
250
251 return S_OK;
252}
253
254typedef std::list<Bstr> VetoList;
255struct VBoxVetoEvent::Data
256{
257 Data() :
258 mVetoed(FALSE)
259 {}
260 BOOL mVetoed;
261 VetoList mVetoList;
262};
263
264HRESULT VBoxVetoEvent::FinalConstruct()
265{
266 VBoxEvent::FinalConstruct();
267 m = new Data;
268 return S_OK;
269}
270
271void VBoxVetoEvent::FinalRelease()
272{
273 if (m)
274 {
275 uninit();
276 delete m;
277 m = 0;
278 }
279 VBoxEvent::FinalRelease();
280}
281
282
283HRESULT VBoxVetoEvent::init(IEventSource *aSource, VBoxEventType_T aType)
284{
285 HRESULT rc = S_OK;
286 // all veto events are waitable
287 rc = VBoxEvent::init(aSource, aType, TRUE);
288 if (FAILED(rc))
289 return rc;
290
291 m->mVetoed = FALSE;
292 m->mVetoList.clear();
293
294 return rc;
295}
296
297void VBoxVetoEvent::uninit()
298{
299 VBoxEvent::uninit();
300 if (!m)
301 return;
302 m->mVetoed = FALSE;
303}
304
305STDMETHODIMP VBoxVetoEvent::AddVeto(IN_BSTR aVeto)
306{
307 AutoCaller autoCaller(this);
308 if (FAILED(autoCaller.rc()))
309 return autoCaller.rc();
310
311 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
312
313 if (aVeto)
314 m->mVetoList.push_back(aVeto);
315
316 m->mVetoed = TRUE;
317
318 return S_OK;
319}
320
321STDMETHODIMP VBoxVetoEvent::IsVetoed(BOOL *aResult)
322{
323 CheckComArgOutPointerValid(aResult);
324
325 AutoCaller autoCaller(this);
326 if (FAILED(autoCaller.rc()))
327 return autoCaller.rc();
328
329 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
330
331 *aResult = m->mVetoed;
332
333 return S_OK;
334}
335
336STDMETHODIMP VBoxVetoEvent::GetVetos(ComSafeArrayOut(BSTR, aVetos))
337{
338 if (ComSafeArrayOutIsNull(aVetos))
339 return E_POINTER;
340
341 AutoCaller autoCaller(this);
342 if (FAILED(autoCaller.rc()))
343 return autoCaller.rc();
344
345 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
346 com::SafeArray<BSTR> vetos(m->mVetoList.size());
347 int i = 0;
348 for (VetoList::const_iterator it = m->mVetoList.begin();
349 it != m->mVetoList.end();
350 ++it, ++i)
351 {
352 const Bstr &str = *it;
353 str.cloneTo(&vetos[i]);
354 }
355 vetos.detachTo(ComSafeArrayOutArg(aVetos));
356
357 return S_OK;
358
359}
360
361static const int FirstEvent = (int)VBoxEventType_LastWildcard + 1;
362static const int LastEvent = (int)VBoxEventType_Last;
363static const int NumEvents = LastEvent - FirstEvent;
364
365/**
366 * Class replacing std::list and able to provide required stability
367 * during iteration. It's acheived by delaying structural modifications
368 * to the list till the moment particular element is no longer used by
369 * current iterators.
370 */
371class EventMapRecord
372{
373public:
374 /**
375 * We have to be double linked, as structural modifications in list are delayed
376 * till element removed, so we have to know our previous one to update its next
377 */
378 EventMapRecord *mNext;
379 bool mAlive;
380private:
381 EventMapRecord *mPrev;
382 ListenerRecord *mRef; /* must be weak reference */
383 int32_t mRefCnt;
384
385public:
386 EventMapRecord(ListenerRecord *aRef) :
387 mNext(0), mAlive(true), mPrev(0), mRef(aRef), mRefCnt(1)
388 {}
389
390 EventMapRecord(EventMapRecord &aOther)
391 {
392 mNext = aOther.mNext;
393 mPrev = aOther.mPrev;
394 mRef = aOther.mRef;
395 mRefCnt = aOther.mRefCnt;
396 mAlive = aOther.mAlive;
397 }
398
399 ~EventMapRecord()
400 {
401 if (mNext)
402 mNext->mPrev = mPrev;
403 if (mPrev)
404 mPrev->mNext = mNext;
405 }
406
407 void addRef()
408 {
409 ASMAtomicIncS32(&mRefCnt);
410 }
411
412 void release()
413 {
414 if (ASMAtomicDecS32(&mRefCnt) <= 0)
415 delete this;
416 }
417
418 // Called when an element is no longer needed
419 void kill()
420 {
421 mAlive = false;
422 release();
423 }
424
425 ListenerRecord *ref()
426 {
427 return mAlive ? mRef : 0;
428 }
429
430 friend class EventMapList;
431};
432
433
434class EventMapList
435{
436 EventMapRecord *mHead;
437 uint32_t mSize;
438public:
439 EventMapList()
440 :
441 mHead(0),
442 mSize(0)
443 {}
444 ~EventMapList()
445 {
446 EventMapRecord *pCur = mHead;
447 while (pCur)
448 {
449 EventMapRecord *pNext = pCur->mNext;
450 pCur->release();
451 pCur = pNext;
452 }
453 }
454
455 /*
456 * Elements have to be added to the front of the list, to make sure
457 * that iterators doesn't see newly added listeners, and iteration
458 * will always complete.
459 */
460 void add(ListenerRecord *aRec)
461 {
462 EventMapRecord *pNew = new EventMapRecord(aRec);
463 pNew->mNext = mHead;
464 if (mHead)
465 mHead->mPrev = pNew;
466 mHead = pNew;
467 mSize++;
468 }
469
470 /*
471 * Mark element as removed, actual removal could be delayed until
472 * all consumers release it too. This helps to keep list stable
473 * enough for iterators to allow long and probably intrusive callbacks.
474 */
475 void remove(ListenerRecord *aRec)
476 {
477 EventMapRecord *pCur = mHead;
478 while (pCur)
479 {
480 EventMapRecord *aNext = pCur->mNext;
481 if (pCur->ref() == aRec)
482 {
483 if (pCur == mHead)
484 mHead = aNext;
485 pCur->kill();
486 mSize--;
487 // break?
488 }
489 pCur = aNext;
490 }
491 }
492
493 uint32_t size() const
494 {
495 return mSize;
496 }
497
498 struct iterator
499 {
500 EventMapRecord *mCur;
501
502 iterator() :
503 mCur(0)
504 {}
505
506 explicit
507 iterator(EventMapRecord *aCur) :
508 mCur(aCur)
509 {
510 // Prevent element removal, till we're at it
511 if (mCur)
512 mCur->addRef();
513 }
514
515 ~iterator()
516 {
517 if (mCur)
518 mCur->release();
519 }
520
521 ListenerRecord *
522 operator*() const
523 {
524 return mCur->ref();
525 }
526
527 EventMapList::iterator &
528 operator++()
529 {
530 EventMapRecord *pPrev = mCur;
531 do {
532 mCur = mCur->mNext;
533 } while (mCur && !mCur->mAlive);
534
535 // now we can safely release previous element
536 pPrev->release();
537
538 // And grab the new current
539 if (mCur)
540 mCur->addRef();
541
542 return *this;
543 }
544
545 bool
546 operator==(const EventMapList::iterator &aOther) const
547 {
548 return mCur == aOther.mCur;
549 }
550
551 bool
552 operator!=(const EventMapList::iterator &aOther) const
553 {
554 return mCur != aOther.mCur;
555 }
556 };
557
558 iterator begin()
559 {
560 return iterator(mHead);
561 }
562
563 iterator end()
564 {
565 return iterator(0);
566 }
567};
568
569typedef EventMapList EventMap[NumEvents];
570typedef std::map<IEvent *, int32_t> PendingEventsMap;
571typedef std::deque<ComPtr<IEvent> > PassiveQueue;
572
573class ListenerRecord
574{
575private:
576 ComPtr<IEventListener> mListener;
577 BOOL mActive;
578 EventSource *mOwner;
579
580 RTSEMEVENT mQEvent;
581 int32_t volatile mWaitCnt;
582 RTCRITSECT mcsQLock;
583 PassiveQueue mQueue;
584 int32_t volatile mRefCnt;
585 uint64_t mLastRead;
586
587public:
588 ListenerRecord(IEventListener *aListener,
589 com::SafeArray<VBoxEventType_T> &aInterested,
590 BOOL aActive,
591 EventSource *aOwner);
592 ~ListenerRecord();
593
594 HRESULT process(IEvent *aEvent, BOOL aWaitable, PendingEventsMap::iterator &pit, AutoLockBase &alock);
595 HRESULT enqueue(IEvent *aEvent);
596 HRESULT dequeue(IEvent **aEvent, LONG aTimeout, AutoLockBase &aAlock);
597 HRESULT eventProcessed(IEvent *aEvent, PendingEventsMap::iterator &pit);
598 void shutdown();
599
600 void addRef()
601 {
602 ASMAtomicIncS32(&mRefCnt);
603 }
604
605 void release()
606 {
607 if (ASMAtomicDecS32(&mRefCnt) <= 0)
608 delete this;
609 }
610
611 BOOL isActive()
612 {
613 return mActive;
614 }
615
616 friend class EventSource;
617};
618
619/* Handy class with semantics close to ComPtr, but for list records */
620template<typename Held>
621class RecordHolder
622{
623public:
624 RecordHolder(Held *lr) :
625 held(lr)
626 {
627 addref();
628 }
629 RecordHolder(const RecordHolder &that) :
630 held(that.held)
631 {
632 addref();
633 }
634 RecordHolder()
635 :
636 held(0)
637 {
638 }
639 ~RecordHolder()
640 {
641 release();
642 }
643
644 Held *obj()
645 {
646 return held;
647 }
648
649 RecordHolder &operator=(const RecordHolder &that)
650 {
651 safe_assign(that.held);
652 return *this;
653 }
654private:
655 Held *held;
656
657 void addref()
658 {
659 if (held)
660 held->addRef();
661 }
662 void release()
663 {
664 if (held)
665 held->release();
666 }
667 void safe_assign(Held *that_p)
668 {
669 if (that_p)
670 that_p->addRef();
671 release();
672 held = that_p;
673 }
674};
675
676typedef std::map<IEventListener *, RecordHolder<ListenerRecord> > Listeners;
677
678struct EventSource::Data
679{
680 Data() : fShutdown(false)
681 {}
682
683 Listeners mListeners;
684 EventMap mEvMap;
685 PendingEventsMap mPendingMap;
686 bool fShutdown;
687};
688
689/**
690 * This function defines what wildcard expands to.
691 */
692static BOOL implies(VBoxEventType_T who, VBoxEventType_T what)
693{
694 switch (who)
695 {
696 case VBoxEventType_Any:
697 return TRUE;
698 case VBoxEventType_Vetoable:
699 return (what == VBoxEventType_OnExtraDataCanChange)
700 || (what == VBoxEventType_OnCanShowWindow);
701 case VBoxEventType_MachineEvent:
702 return (what == VBoxEventType_OnMachineStateChanged)
703 || (what == VBoxEventType_OnMachineDataChanged)
704 || (what == VBoxEventType_OnMachineRegistered)
705 || (what == VBoxEventType_OnSessionStateChanged)
706 || (what == VBoxEventType_OnGuestPropertyChanged);
707 case VBoxEventType_SnapshotEvent:
708 return (what == VBoxEventType_OnSnapshotTaken)
709 || (what == VBoxEventType_OnSnapshotDeleted)
710 || (what == VBoxEventType_OnSnapshotChanged) ;
711 case VBoxEventType_InputEvent:
712 return (what == VBoxEventType_OnKeyboardLedsChanged)
713 || (what == VBoxEventType_OnMousePointerShapeChanged)
714 || (what == VBoxEventType_OnMouseCapabilityChanged);
715 case VBoxEventType_Invalid:
716 return FALSE;
717 default:
718 break;
719 }
720
721 return who == what;
722}
723
724ListenerRecord::ListenerRecord(IEventListener *aListener,
725 com::SafeArray<VBoxEventType_T> &aInterested,
726 BOOL aActive,
727 EventSource *aOwner) :
728 mActive(aActive), mOwner(aOwner), mWaitCnt(0), mRefCnt(0)
729{
730 mListener = aListener;
731 EventMap *aEvMap = &aOwner->m->mEvMap;
732
733 for (size_t i = 0; i < aInterested.size(); ++i)
734 {
735 VBoxEventType_T interested = aInterested[i];
736 for (int j = FirstEvent; j < LastEvent; j++)
737 {
738 VBoxEventType_T candidate = (VBoxEventType_T)j;
739 if (implies(interested, candidate))
740 {
741 (*aEvMap)[j - FirstEvent].add(this);
742 }
743 }
744 }
745
746 if (!mActive)
747 {
748 ::RTCritSectInit(&mcsQLock);
749 ::RTSemEventCreate(&mQEvent);
750 mLastRead = RTTimeMilliTS();
751 }
752 else
753 {
754 mQEvent = NIL_RTSEMEVENT;
755 RT_ZERO(mcsQLock);
756 mLastRead = 0;
757 }
758}
759
760ListenerRecord::~ListenerRecord()
761{
762 /* Remove references to us from the event map */
763 EventMap *aEvMap = &mOwner->m->mEvMap;
764 for (int j = FirstEvent; j < LastEvent; j++)
765 {
766 (*aEvMap)[j - FirstEvent].remove(this);
767 }
768
769 if (!mActive)
770 {
771 // at this moment nobody could add elements to our queue, so we can safely
772 // clean it up, otherwise there will be pending events map elements
773 PendingEventsMap *aPem = &mOwner->m->mPendingMap;
774 while (true)
775 {
776 ComPtr<IEvent> aEvent;
777
778 if (mQueue.empty())
779 break;
780
781 mQueue.front().queryInterfaceTo(aEvent.asOutParam());
782 mQueue.pop_front();
783
784 BOOL aWaitable = FALSE;
785 aEvent->COMGETTER(Waitable)(&aWaitable);
786 if (aWaitable)
787 {
788 PendingEventsMap::iterator pit = aPem->find(aEvent);
789 if (pit != aPem->end())
790 eventProcessed(aEvent, pit);
791 }
792 }
793
794 ::RTCritSectDelete(&mcsQLock);
795 }
796 shutdown();
797}
798
799HRESULT ListenerRecord::process(IEvent *aEvent,
800 BOOL aWaitable,
801 PendingEventsMap::iterator &pit,
802 AutoLockBase &aAlock)
803{
804 if (mActive)
805 {
806 /*
807 * We release lock here to allow modifying ops on EventSource inside callback.
808 */
809 HRESULT rc = S_OK;
810 if (mListener)
811 {
812 aAlock.release();
813 rc = mListener->HandleEvent(aEvent);
814#ifdef RT_OS_WINDOWS
815 Assert(rc != RPC_E_WRONG_THREAD);
816#endif
817 aAlock.acquire();
818 }
819 if (aWaitable)
820 eventProcessed(aEvent, pit);
821 return rc;
822 }
823 return enqueue(aEvent);
824}
825
826
827HRESULT ListenerRecord::enqueue(IEvent *aEvent)
828{
829 AssertMsg(!mActive, ("must be passive\n"));
830
831 // put an event the queue
832 ::RTCritSectEnter(&mcsQLock);
833
834 // If there was no events reading from the listener for the long time,
835 // and events keep coming, or queue is oversized we shall unregister this listener.
836 uint64_t sinceRead = RTTimeMilliTS() - mLastRead;
837 size_t queueSize = mQueue.size();
838 if ((queueSize > 1000) || ((queueSize > 500) && (sinceRead > 60 * 1000)))
839 {
840 ::RTCritSectLeave(&mcsQLock);
841 return E_ABORT;
842 }
843
844
845 if (queueSize != 0 && mQueue.back() == aEvent)
846 /* if same event is being pushed multiple times - it's reusable event and
847 we don't really need multiple instances of it in the queue */
848 (void)aEvent;
849 else
850 mQueue.push_back(aEvent);
851
852 ::RTCritSectLeave(&mcsQLock);
853
854 // notify waiters
855 ::RTSemEventSignal(mQEvent);
856
857 return S_OK;
858}
859
860HRESULT ListenerRecord::dequeue(IEvent **aEvent,
861 LONG aTimeout,
862 AutoLockBase &aAlock)
863{
864 if (mActive)
865 return VBOX_E_INVALID_OBJECT_STATE;
866
867 // retain listener record
868 RecordHolder<ListenerRecord> holder(this);
869
870 ::RTCritSectEnter(&mcsQLock);
871
872 mLastRead = RTTimeMilliTS();
873
874 if (mQueue.empty())
875 {
876 ::RTCritSectLeave(&mcsQLock);
877 // Speed up common case
878 if (aTimeout == 0)
879 {
880 *aEvent = NULL;
881 return S_OK;
882 }
883 // release lock while waiting, listener will not go away due to above holder
884 aAlock.release();
885
886 // In order to safely shutdown, count all waiting threads here.
887 ASMAtomicIncS32(&mWaitCnt);
888 ::RTSemEventWait(mQEvent, aTimeout);
889 ASMAtomicDecS32(&mWaitCnt);
890
891 // reacquire lock
892 aAlock.acquire();
893 ::RTCritSectEnter(&mcsQLock);
894 }
895 if (mQueue.empty())
896 {
897 *aEvent = NULL;
898 }
899 else
900 {
901 mQueue.front().queryInterfaceTo(aEvent);
902 mQueue.pop_front();
903 }
904 ::RTCritSectLeave(&mcsQLock);
905 return S_OK;
906}
907
908HRESULT ListenerRecord::eventProcessed(IEvent *aEvent, PendingEventsMap::iterator &pit)
909{
910 if (--pit->second == 0)
911 {
912 Assert(pit->first == aEvent);
913 aEvent->SetProcessed();
914 mOwner->m->mPendingMap.erase(pit);
915 }
916
917 return S_OK;
918}
919
920void ListenerRecord::shutdown()
921{
922 if (mQEvent != NIL_RTSEMEVENT)
923 {
924 RTSEMEVENT tmp = mQEvent;
925 mQEvent = NIL_RTSEMEVENT;
926
927 /* On Darwin it is known that RTSemEventDestroy() returns 0 while
928 * corresponding thread remains to be blocked after that. In order to prevent
929 * undesireble freeze on shutdown, this workaround is used. */
930 Log(("Wait for %d waiters to release.\n", ASMAtomicReadS32(&mWaitCnt)));
931 while (ASMAtomicReadS32(&mWaitCnt) > 0)
932 {
933 ::RTSemEventSignal(tmp);
934
935 /* Are we already done? */
936 if (ASMAtomicReadS32(&mWaitCnt) == 0)
937 break;
938
939 RTThreadSleep(10);
940 }
941 Log(("All waiters just released the lock.\n"));
942
943 ::RTSemEventDestroy(tmp);
944 }
945}
946
947EventSource::EventSource()
948{}
949
950EventSource::~EventSource()
951{}
952
953HRESULT EventSource::FinalConstruct()
954{
955 m = new Data;
956 return BaseFinalConstruct();
957}
958
959void EventSource::FinalRelease()
960{
961 uninit();
962 delete m;
963 BaseFinalRelease();
964}
965
966HRESULT EventSource::init()
967{
968 HRESULT rc = S_OK;
969
970 AutoInitSpan autoInitSpan(this);
971 AssertReturn(autoInitSpan.isOk(), E_FAIL);
972
973 /* Confirm a successful initialization */
974 autoInitSpan.setSucceeded();
975 return rc;
976}
977
978void EventSource::uninit()
979{
980 {
981 // First of all (before even thinking about entering the uninit span):
982 // make sure that all listeners are are shut down (no pending events or
983 // wait calls), because they cannot be alive without the associated
984 // event source. Otherwise API clients which use long-term (or
985 // indefinite) waits will block VBoxSVC termination (just one example)
986 // for a long time or even infinitely long.
987 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
988 if (!m->fShutdown)
989 {
990 m->fShutdown = true;
991 for (Listeners::iterator it = m->mListeners.begin();
992 it != m->mListeners.end();
993 ++it)
994 {
995 it->second.obj()->shutdown();
996 }
997 }
998 }
999
1000 AutoUninitSpan autoUninitSpan(this);
1001 if (autoUninitSpan.uninitDone())
1002 return;
1003
1004 m->mListeners.clear();
1005 // m->mEvMap shall be cleared at this point too by destructors, assert?
1006}
1007
1008STDMETHODIMP EventSource::RegisterListener(IEventListener *aListener,
1009 ComSafeArrayIn(VBoxEventType_T, aInterested),
1010 BOOL aActive)
1011{
1012 CheckComArgNotNull(aListener);
1013 CheckComArgSafeArrayNotNull(aInterested);
1014
1015 AutoCaller autoCaller(this);
1016 if (FAILED(autoCaller.rc()))
1017 return autoCaller.rc();
1018
1019 {
1020 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1021
1022 if (m->fShutdown)
1023 return setError(VBOX_E_INVALID_OBJECT_STATE,
1024 tr("This event source is already shut down"));
1025
1026 Listeners::const_iterator it = m->mListeners.find(aListener);
1027 if (it != m->mListeners.end())
1028 return setError(E_INVALIDARG,
1029 tr("This listener already registered"));
1030
1031 com::SafeArray<VBoxEventType_T> interested(ComSafeArrayInArg(aInterested));
1032 RecordHolder<ListenerRecord> lrh(new ListenerRecord(aListener, interested, aActive, this));
1033 m->mListeners.insert(Listeners::value_type(aListener, lrh));
1034 }
1035
1036 VBoxEventDesc evDesc;
1037 evDesc.init(this, VBoxEventType_OnEventSourceChanged, aListener, TRUE);
1038 evDesc.fire(0);
1039
1040 return S_OK;
1041}
1042
1043STDMETHODIMP EventSource::UnregisterListener(IEventListener *aListener)
1044{
1045 CheckComArgNotNull(aListener);
1046
1047 AutoCaller autoCaller(this);
1048 if (FAILED(autoCaller.rc()))
1049 return autoCaller.rc();
1050
1051 HRESULT rc;
1052 {
1053 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1054
1055 Listeners::iterator it = m->mListeners.find(aListener);
1056
1057 if (it != m->mListeners.end())
1058 {
1059 it->second.obj()->shutdown();
1060 m->mListeners.erase(it);
1061 // destructor removes refs from the event map
1062 rc = S_OK;
1063 }
1064 else
1065 {
1066 rc = setError(VBOX_E_OBJECT_NOT_FOUND,
1067 tr("Listener was never registered"));
1068 }
1069 }
1070
1071 if (SUCCEEDED(rc))
1072 {
1073 VBoxEventDesc evDesc;
1074 evDesc.init(this, VBoxEventType_OnEventSourceChanged, aListener, FALSE);
1075 evDesc.fire(0);
1076 }
1077
1078 return rc;
1079}
1080
1081STDMETHODIMP EventSource::FireEvent(IEvent *aEvent,
1082 LONG aTimeout,
1083 BOOL *aProcessed)
1084{
1085 CheckComArgNotNull(aEvent);
1086 CheckComArgOutPointerValid(aProcessed);
1087
1088 AutoCaller autoCaller(this);
1089 if (FAILED(autoCaller.rc()))
1090 return autoCaller.rc();
1091
1092 HRESULT hrc;
1093 BOOL aWaitable = FALSE;
1094 aEvent->COMGETTER(Waitable)(&aWaitable);
1095
1096 do {
1097 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1098
1099 if (m->fShutdown)
1100 return setError(VBOX_E_INVALID_OBJECT_STATE,
1101 tr("This event source is already shut down"));
1102
1103 VBoxEventType_T evType;
1104 hrc = aEvent->COMGETTER(Type)(&evType);
1105 AssertComRCReturn(hrc, hrc);
1106
1107 EventMapList &listeners = m->mEvMap[(int)evType - FirstEvent];
1108
1109 /* Anyone interested in this event? */
1110 uint32_t cListeners = listeners.size();
1111 if (cListeners == 0)
1112 {
1113 aEvent->SetProcessed();
1114 break; // just leave the lock and update event object state
1115 }
1116
1117 PendingEventsMap::iterator pit;
1118
1119 if (aWaitable)
1120 {
1121 m->mPendingMap.insert(PendingEventsMap::value_type(aEvent, cListeners));
1122 // we keep iterator here to allow processing active listeners without
1123 // pending events lookup
1124 pit = m->mPendingMap.find(aEvent);
1125 }
1126 for (EventMapList::iterator it = listeners.begin();
1127 it != listeners.end();
1128 ++it)
1129 {
1130 HRESULT cbRc;
1131 // keep listener record reference, in case someone will remove it while in callback
1132 RecordHolder<ListenerRecord> record(*it);
1133
1134 /*
1135 * We pass lock here to allow modifying ops on EventSource inside callback
1136 * in active mode. Note that we expect list iterator stability as 'alock'
1137 * could be temporary released when calling event handler.
1138 */
1139 cbRc = record.obj()->process(aEvent, aWaitable, pit, alock);
1140
1141 /* Note that E_ABORT is used above to signal that a passive
1142 * listener was unregistered due to not picking up its event.
1143 * This overlaps with XPCOM specific use of E_ABORT to signal
1144 * death of an active listener, but that's irrelevant here. */
1145 if (FAILED_DEAD_INTERFACE(cbRc) || cbRc == E_ABORT)
1146 {
1147 Listeners::iterator lit = m->mListeners.find(record.obj()->mListener);
1148 if (lit != m->mListeners.end())
1149 {
1150 lit->second.obj()->shutdown();
1151 m->mListeners.erase(lit);
1152 }
1153 }
1154 // anything else to do with cbRc?
1155 }
1156 } while (0);
1157 /* We leave the lock here */
1158
1159 if (aWaitable)
1160 hrc = aEvent->WaitProcessed(aTimeout, aProcessed);
1161 else
1162 *aProcessed = TRUE;
1163
1164 return hrc;
1165}
1166
1167
1168STDMETHODIMP EventSource::GetEvent(IEventListener *aListener,
1169 LONG aTimeout,
1170 IEvent **aEvent)
1171{
1172
1173 CheckComArgNotNull(aListener);
1174
1175 AutoCaller autoCaller(this);
1176 if (FAILED(autoCaller.rc()))
1177 return autoCaller.rc();
1178
1179 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1180
1181 if (m->fShutdown)
1182 return setError(VBOX_E_INVALID_OBJECT_STATE,
1183 tr("This event source is already shut down"));
1184
1185 Listeners::iterator it = m->mListeners.find(aListener);
1186 HRESULT rc;
1187
1188 if (it != m->mListeners.end())
1189 rc = it->second.obj()->dequeue(aEvent, aTimeout, alock);
1190 else
1191 rc = setError(VBOX_E_OBJECT_NOT_FOUND,
1192 tr("Listener was never registered"));
1193
1194 if (rc == VBOX_E_INVALID_OBJECT_STATE)
1195 return setError(rc, tr("Listener must be passive"));
1196
1197 return rc;
1198}
1199
1200STDMETHODIMP EventSource::EventProcessed(IEventListener *aListener,
1201 IEvent *aEvent)
1202{
1203 CheckComArgNotNull(aListener);
1204 CheckComArgNotNull(aEvent);
1205
1206 AutoCaller autoCaller(this);
1207 if (FAILED(autoCaller.rc()))
1208 return autoCaller.rc();
1209
1210 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1211
1212 if (m->fShutdown)
1213 return setError(VBOX_E_INVALID_OBJECT_STATE,
1214 tr("This event source is already shut down"));
1215
1216 Listeners::iterator it = m->mListeners.find(aListener);
1217 HRESULT rc;
1218
1219 BOOL aWaitable = FALSE;
1220 aEvent->COMGETTER(Waitable)(&aWaitable);
1221
1222 if (it != m->mListeners.end())
1223 {
1224 ListenerRecord *aRecord = it->second.obj();
1225
1226 if (aRecord->isActive())
1227 return setError(E_INVALIDARG,
1228 tr("Only applicable to passive listeners"));
1229
1230 if (aWaitable)
1231 {
1232 PendingEventsMap::iterator pit = m->mPendingMap.find(aEvent);
1233
1234 if (pit == m->mPendingMap.end())
1235 {
1236 AssertFailed();
1237 rc = setError(VBOX_E_OBJECT_NOT_FOUND,
1238 tr("Unknown event"));
1239 }
1240 else
1241 rc = aRecord->eventProcessed(aEvent, pit);
1242 }
1243 else
1244 {
1245 // for non-waitable events we're done
1246 rc = S_OK;
1247 }
1248 }
1249 else
1250 {
1251 rc = setError(VBOX_E_OBJECT_NOT_FOUND,
1252 tr("Listener was never registered"));
1253 }
1254
1255 return rc;
1256}
1257
1258/**
1259 * This class serves as feasible listener implementation
1260 * which could be used by clients not able to create local
1261 * COM objects, but still willing to receive event
1262 * notifications in passive mode, such as webservices.
1263 */
1264class ATL_NO_VTABLE PassiveEventListener :
1265 public VirtualBoxBase,
1266 VBOX_SCRIPTABLE_IMPL(IEventListener)
1267{
1268public:
1269
1270 VIRTUALBOXBASE_ADD_ERRORINFO_SUPPORT(PassiveEventListener, IEventListener)
1271
1272 DECLARE_NOT_AGGREGATABLE(PassiveEventListener)
1273
1274 DECLARE_PROTECT_FINAL_CONSTRUCT()
1275
1276 BEGIN_COM_MAP(PassiveEventListener)
1277 VBOX_DEFAULT_INTERFACE_ENTRIES(IEventListener)
1278 END_COM_MAP()
1279
1280 PassiveEventListener()
1281 {}
1282 ~PassiveEventListener()
1283 {}
1284
1285 HRESULT FinalConstruct()
1286 {
1287 return BaseFinalConstruct();
1288 }
1289 void FinalRelease()
1290 {
1291 BaseFinalRelease();
1292 }
1293
1294 // IEventListener methods
1295 STDMETHOD(HandleEvent)(IEvent *)
1296 {
1297 ComAssertMsgRet(false, ("HandleEvent() of wrapper shall never be called"),
1298 E_FAIL);
1299 }
1300};
1301
1302/* Proxy listener class, used to aggregate multiple event sources into one */
1303class ATL_NO_VTABLE ProxyEventListener :
1304 public VirtualBoxBase,
1305 VBOX_SCRIPTABLE_IMPL(IEventListener)
1306{
1307 ComPtr<IEventSource> mSource;
1308public:
1309
1310 VIRTUALBOXBASE_ADD_ERRORINFO_SUPPORT(ProxyEventListener, IEventListener)
1311
1312 DECLARE_NOT_AGGREGATABLE(ProxyEventListener)
1313
1314 DECLARE_PROTECT_FINAL_CONSTRUCT()
1315
1316 BEGIN_COM_MAP(ProxyEventListener)
1317 VBOX_DEFAULT_INTERFACE_ENTRIES(IEventListener)
1318 END_COM_MAP()
1319
1320 ProxyEventListener()
1321 {}
1322 ~ProxyEventListener()
1323 {}
1324
1325 HRESULT FinalConstruct()
1326 {
1327 return BaseFinalConstruct();
1328 }
1329 void FinalRelease()
1330 {
1331 BaseFinalRelease();
1332 }
1333
1334 HRESULT init(IEventSource *aSource)
1335 {
1336 mSource = aSource;
1337 return S_OK;
1338 }
1339
1340 // IEventListener methods
1341 STDMETHOD(HandleEvent)(IEvent *aEvent)
1342 {
1343 BOOL fProcessed = FALSE;
1344 if (mSource)
1345 return mSource->FireEvent(aEvent, 0, &fProcessed);
1346 else
1347 return S_OK;
1348 }
1349};
1350
1351class ATL_NO_VTABLE EventSourceAggregator :
1352 public VirtualBoxBase,
1353 VBOX_SCRIPTABLE_IMPL(IEventSource)
1354{
1355 typedef std::list <ComPtr<IEventSource> > EventSourceList;
1356 /* key is weak reference */
1357 typedef std::map<IEventListener *, ComPtr<IEventListener> > ProxyListenerMap;
1358
1359 EventSourceList mEventSources;
1360 ProxyListenerMap mListenerProxies;
1361 ComObjPtr<EventSource> mSource;
1362
1363public:
1364
1365 VIRTUALBOXBASE_ADD_ERRORINFO_SUPPORT(EventSourceAggregator, IEventSource)
1366
1367 DECLARE_NOT_AGGREGATABLE(EventSourceAggregator)
1368
1369 DECLARE_PROTECT_FINAL_CONSTRUCT()
1370
1371 BEGIN_COM_MAP(EventSourceAggregator)
1372 VBOX_DEFAULT_INTERFACE_ENTRIES(IEventSource)
1373 END_COM_MAP()
1374
1375 EventSourceAggregator()
1376 {}
1377 ~EventSourceAggregator()
1378 {}
1379
1380 HRESULT FinalConstruct()
1381 {
1382 return BaseFinalConstruct();
1383 }
1384 void FinalRelease()
1385 {
1386 mEventSources.clear();
1387 mListenerProxies.clear();
1388 mSource->uninit();
1389 BaseFinalRelease();
1390 }
1391
1392 // internal public
1393 HRESULT init(ComSafeArrayIn(IEventSource *, aSources));
1394
1395 // IEventSource methods
1396 STDMETHOD(CreateListener)(IEventListener **aListener);
1397 STDMETHOD(CreateAggregator)(ComSafeArrayIn(IEventSource *, aSubordinates),
1398 IEventSource **aAggregator);
1399 STDMETHOD(RegisterListener)(IEventListener *aListener,
1400 ComSafeArrayIn(VBoxEventType_T, aInterested),
1401 BOOL aActive);
1402 STDMETHOD(UnregisterListener)(IEventListener *aListener);
1403 STDMETHOD(FireEvent)(IEvent *aEvent,
1404 LONG aTimeout,
1405 BOOL *aProcessed);
1406 STDMETHOD(GetEvent)(IEventListener *aListener,
1407 LONG aTimeout,
1408 IEvent **aEvent);
1409 STDMETHOD(EventProcessed)(IEventListener *aListener,
1410 IEvent *aEvent);
1411
1412 protected:
1413 HRESULT createProxyListener(IEventListener *aListener,
1414 IEventListener **aProxy);
1415 HRESULT getProxyListener(IEventListener *aListener,
1416 IEventListener **aProxy);
1417 HRESULT removeProxyListener(IEventListener *aListener);
1418};
1419
1420#ifdef VBOX_WITH_XPCOM
1421NS_DECL_CLASSINFO(ProxyEventListener)
1422NS_IMPL_THREADSAFE_ISUPPORTS1_CI(ProxyEventListener, IEventListener)
1423NS_DECL_CLASSINFO(PassiveEventListener)
1424NS_IMPL_THREADSAFE_ISUPPORTS1_CI(PassiveEventListener, IEventListener)
1425NS_DECL_CLASSINFO(VBoxEvent)
1426NS_IMPL_THREADSAFE_ISUPPORTS1_CI(VBoxEvent, IEvent)
1427NS_DECL_CLASSINFO(VBoxVetoEvent)
1428NS_IMPL_ISUPPORTS_INHERITED1(VBoxVetoEvent, VBoxEvent, IVetoEvent)
1429NS_DECL_CLASSINFO(EventSource)
1430NS_IMPL_THREADSAFE_ISUPPORTS1_CI(EventSource, IEventSource)
1431NS_DECL_CLASSINFO(EventSourceAggregator)
1432NS_IMPL_THREADSAFE_ISUPPORTS1_CI(EventSourceAggregator, IEventSource)
1433#endif
1434
1435
1436STDMETHODIMP EventSource::CreateListener(IEventListener **aListener)
1437{
1438 CheckComArgOutPointerValid(aListener);
1439
1440 AutoCaller autoCaller(this);
1441 if (FAILED(autoCaller.rc()))
1442 return autoCaller.rc();
1443
1444 ComObjPtr<PassiveEventListener> listener;
1445
1446 HRESULT rc = listener.createObject();
1447 ComAssertMsgRet(SUCCEEDED(rc), ("Could not create wrapper object (%Rhrc)", rc),
1448 E_FAIL);
1449 listener.queryInterfaceTo(aListener);
1450 return S_OK;
1451}
1452
1453
1454STDMETHODIMP EventSource::CreateAggregator(ComSafeArrayIn(IEventSource *, aSubordinates),
1455 IEventSource **aResult)
1456{
1457 CheckComArgOutPointerValid(aResult);
1458
1459 AutoCaller autoCaller(this);
1460 if (FAILED(autoCaller.rc()))
1461 return autoCaller.rc();
1462
1463 ComObjPtr<EventSourceAggregator> agg;
1464
1465 HRESULT rc = agg.createObject();
1466 ComAssertMsgRet(SUCCEEDED(rc), ("Could not create aggregator (%Rhrc)", rc),
1467 E_FAIL);
1468
1469 rc = agg->init(ComSafeArrayInArg(aSubordinates));
1470 if (FAILED(rc))
1471 return rc;
1472
1473 agg.queryInterfaceTo(aResult);
1474 return S_OK;
1475}
1476
1477HRESULT EventSourceAggregator::init(ComSafeArrayIn(IEventSource *, aSourcesIn))
1478{
1479 HRESULT rc;
1480
1481 AutoInitSpan autoInitSpan(this);
1482 AssertReturn(autoInitSpan.isOk(), E_FAIL);
1483
1484 rc = mSource.createObject();
1485 ComAssertMsgRet(SUCCEEDED(rc), ("Could not create source (%Rhrc)", rc),
1486 E_FAIL);
1487 rc = mSource->init();
1488 ComAssertMsgRet(SUCCEEDED(rc), ("Could not init source (%Rhrc)", rc),
1489 E_FAIL);
1490
1491 com::SafeIfaceArray<IEventSource> aSources(ComSafeArrayInArg (aSourcesIn));
1492
1493 size_t cSize = aSources.size();
1494
1495 for (size_t i = 0; i < cSize; i++)
1496 {
1497 if (aSources[i] != NULL)
1498 mEventSources.push_back(aSources[i]);
1499 }
1500
1501 /* Confirm a successful initialization */
1502 autoInitSpan.setSucceeded();
1503
1504 return rc;
1505}
1506
1507STDMETHODIMP EventSourceAggregator::CreateListener(IEventListener **aListener)
1508{
1509 return mSource->CreateListener(aListener);
1510}
1511
1512STDMETHODIMP EventSourceAggregator::CreateAggregator(ComSafeArrayIn(IEventSource *, aSubordinates),
1513 IEventSource **aResult)
1514{
1515 return mSource->CreateAggregator(ComSafeArrayInArg(aSubordinates), aResult);
1516}
1517
1518STDMETHODIMP EventSourceAggregator::RegisterListener(IEventListener *aListener,
1519 ComSafeArrayIn(VBoxEventType_T, aInterested),
1520 BOOL aActive)
1521{
1522 CheckComArgNotNull(aListener);
1523 CheckComArgSafeArrayNotNull(aInterested);
1524
1525 AutoCaller autoCaller(this);
1526 if (FAILED(autoCaller.rc()))
1527 return autoCaller.rc();
1528
1529 HRESULT rc;
1530
1531 ComPtr<IEventListener> proxy;
1532 rc = createProxyListener(aListener, proxy.asOutParam());
1533 if (FAILED(rc))
1534 return rc;
1535
1536 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1537 for (EventSourceList::const_iterator it = mEventSources.begin(); it != mEventSources.end();
1538 ++it)
1539 {
1540 ComPtr<IEventSource> es = *it;
1541 /* Register active proxy listener on real event source */
1542 rc = es->RegisterListener(proxy, ComSafeArrayInArg(aInterested), TRUE);
1543 }
1544 /* And add real listener on our event source */
1545 rc = mSource->RegisterListener(aListener, ComSafeArrayInArg(aInterested), aActive);
1546
1547 rc = S_OK;
1548
1549 return rc;
1550}
1551
1552STDMETHODIMP EventSourceAggregator::UnregisterListener(IEventListener *aListener)
1553{
1554 CheckComArgNotNull(aListener);
1555
1556 AutoCaller autoCaller(this);
1557 if (FAILED(autoCaller.rc()))
1558 return autoCaller.rc();
1559
1560 HRESULT rc = S_OK;
1561
1562 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1563
1564 ComPtr<IEventListener> proxy;
1565 rc = getProxyListener(aListener, proxy.asOutParam());
1566 if (FAILED(rc))
1567 return rc;
1568
1569 for (EventSourceList::const_iterator it = mEventSources.begin(); it != mEventSources.end();
1570 ++it)
1571 {
1572 ComPtr<IEventSource> es = *it;
1573 rc = es->UnregisterListener(proxy);
1574 }
1575 rc = mSource->UnregisterListener(aListener);
1576
1577 return removeProxyListener(aListener);
1578
1579}
1580
1581STDMETHODIMP EventSourceAggregator::FireEvent(IEvent *aEvent,
1582 LONG aTimeout,
1583 BOOL *aProcessed)
1584{
1585 CheckComArgNotNull(aEvent);
1586 CheckComArgOutPointerValid(aProcessed);
1587
1588 AutoCaller autoCaller(this);
1589 if (FAILED(autoCaller.rc()))
1590 return autoCaller.rc();
1591
1592 HRESULT rc = S_OK;
1593 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1594 /* Aggregator event source shall not have direct event firing, but we may
1595 wish to support aggregation chains */
1596 for (EventSourceList::const_iterator it = mEventSources.begin(); it != mEventSources.end();
1597 ++it)
1598 {
1599 ComPtr<IEventSource> es = *it;
1600 rc = es->FireEvent(aEvent, aTimeout, aProcessed);
1601 /* Current behavior is that aggregator's FireEvent() always succeeds,
1602 so that multiple event sources don't affect each other. */
1603 NOREF(rc);
1604 }
1605
1606 return S_OK;
1607}
1608
1609STDMETHODIMP EventSourceAggregator::GetEvent(IEventListener *aListener,
1610 LONG aTimeout,
1611 IEvent **aEvent)
1612{
1613 return mSource->GetEvent(aListener, aTimeout, aEvent);
1614}
1615
1616STDMETHODIMP EventSourceAggregator::EventProcessed(IEventListener *aListener,
1617 IEvent *aEvent)
1618{
1619 return mSource->EventProcessed(aListener, aEvent);
1620}
1621
1622HRESULT EventSourceAggregator::createProxyListener(IEventListener *aListener,
1623 IEventListener **aProxy)
1624{
1625 ComObjPtr<ProxyEventListener> proxy;
1626
1627 HRESULT rc = proxy.createObject();
1628 ComAssertMsgRet(SUCCEEDED(rc), ("Could not create proxy (%Rhrc)", rc),
1629 E_FAIL);
1630
1631 rc = proxy->init(mSource);
1632 if (FAILED(rc))
1633 return rc;
1634
1635 ProxyListenerMap::const_iterator it = mListenerProxies.find(aListener);
1636 if (it != mListenerProxies.end())
1637 return setError(E_INVALIDARG,
1638 tr("This listener already registered"));
1639
1640 mListenerProxies.insert(ProxyListenerMap::value_type(aListener, proxy));
1641
1642 proxy.queryInterfaceTo(aProxy);
1643 return S_OK;
1644}
1645
1646HRESULT EventSourceAggregator::getProxyListener(IEventListener *aListener,
1647 IEventListener **aProxy)
1648{
1649 ProxyListenerMap::const_iterator it = mListenerProxies.find(aListener);
1650 if (it == mListenerProxies.end())
1651 return setError(E_INVALIDARG,
1652 tr("This listener never registered"));
1653
1654 (*it).second.queryInterfaceTo(aProxy);
1655 return S_OK;
1656}
1657
1658HRESULT EventSourceAggregator::removeProxyListener(IEventListener *aListener)
1659{
1660 ProxyListenerMap::iterator it = mListenerProxies.find(aListener);
1661 if (it == mListenerProxies.end())
1662 return setError(E_INVALIDARG,
1663 tr("This listener never registered"));
1664
1665 mListenerProxies.erase(it);
1666 return S_OK;
1667}
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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