VirtualBox

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

Last change on this file since 50945 was 50545, checked in by vboxsync, 11 years ago

Main/EventSource+VirtualBox: better event source cleanup (for now used only in iVirtualBox/VBoxSVC), to prevent sabotage by passive event listener calls hanging around

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 42.8 KB
Line 
1/* $Id: EventImpl.cpp 50545 2014-02-21 14:50:41Z 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.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/** @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 RTCRITSECT mcsQLock;
582 PassiveQueue mQueue;
583 int32_t volatile mRefCnt;
584 uint64_t mLastRead;
585
586public:
587 ListenerRecord(IEventListener *aListener,
588 com::SafeArray<VBoxEventType_T> &aInterested,
589 BOOL aActive,
590 EventSource *aOwner);
591 ~ListenerRecord();
592
593 HRESULT process(IEvent *aEvent, BOOL aWaitable, PendingEventsMap::iterator &pit, AutoLockBase &alock);
594 HRESULT enqueue(IEvent *aEvent);
595 HRESULT dequeue(IEvent **aEvent, LONG aTimeout, AutoLockBase &aAlock);
596 HRESULT eventProcessed(IEvent *aEvent, PendingEventsMap::iterator &pit);
597 void shutdown();
598
599 void addRef()
600 {
601 ASMAtomicIncS32(&mRefCnt);
602 }
603
604 void release()
605 {
606 if (ASMAtomicDecS32(&mRefCnt) <= 0)
607 delete this;
608 }
609
610 BOOL isActive()
611 {
612 return mActive;
613 }
614
615 friend class EventSource;
616};
617
618/* Handy class with semantics close to ComPtr, but for list records */
619template<typename Held>
620class RecordHolder
621{
622public:
623 RecordHolder(Held *lr) :
624 held(lr)
625 {
626 addref();
627 }
628 RecordHolder(const RecordHolder &that) :
629 held(that.held)
630 {
631 addref();
632 }
633 RecordHolder()
634 :
635 held(0)
636 {
637 }
638 ~RecordHolder()
639 {
640 release();
641 }
642
643 Held *obj()
644 {
645 return held;
646 }
647
648 RecordHolder &operator=(const RecordHolder &that)
649 {
650 safe_assign(that.held);
651 return *this;
652 }
653private:
654 Held *held;
655
656 void addref()
657 {
658 if (held)
659 held->addRef();
660 }
661 void release()
662 {
663 if (held)
664 held->release();
665 }
666 void safe_assign(Held *that_p)
667 {
668 if (that_p)
669 that_p->addRef();
670 release();
671 held = that_p;
672 }
673};
674
675typedef std::map<IEventListener *, RecordHolder<ListenerRecord> > Listeners;
676
677struct EventSource::Data
678{
679 Data() : fShutdown(false)
680 {}
681
682 Listeners mListeners;
683 EventMap mEvMap;
684 PendingEventsMap mPendingMap;
685 bool fShutdown;
686};
687
688/**
689 * This function defines what wildcard expands to.
690 */
691static BOOL implies(VBoxEventType_T who, VBoxEventType_T what)
692{
693 switch (who)
694 {
695 case VBoxEventType_Any:
696 return TRUE;
697 case VBoxEventType_Vetoable:
698 return (what == VBoxEventType_OnExtraDataCanChange)
699 || (what == VBoxEventType_OnCanShowWindow);
700 case VBoxEventType_MachineEvent:
701 return (what == VBoxEventType_OnMachineStateChanged)
702 || (what == VBoxEventType_OnMachineDataChanged)
703 || (what == VBoxEventType_OnMachineRegistered)
704 || (what == VBoxEventType_OnSessionStateChanged)
705 || (what == VBoxEventType_OnGuestPropertyChanged);
706 case VBoxEventType_SnapshotEvent:
707 return (what == VBoxEventType_OnSnapshotTaken)
708 || (what == VBoxEventType_OnSnapshotDeleted)
709 || (what == VBoxEventType_OnSnapshotChanged) ;
710 case VBoxEventType_InputEvent:
711 return (what == VBoxEventType_OnKeyboardLedsChanged)
712 || (what == VBoxEventType_OnMousePointerShapeChanged)
713 || (what == VBoxEventType_OnMouseCapabilityChanged);
714 case VBoxEventType_Invalid:
715 return FALSE;
716 default:
717 break;
718 }
719
720 return who == what;
721}
722
723ListenerRecord::ListenerRecord(IEventListener *aListener,
724 com::SafeArray<VBoxEventType_T> &aInterested,
725 BOOL aActive,
726 EventSource *aOwner) :
727 mActive(aActive), mOwner(aOwner), mRefCnt(0)
728{
729 mListener = aListener;
730 EventMap *aEvMap = &aOwner->m->mEvMap;
731
732 for (size_t i = 0; i < aInterested.size(); ++i)
733 {
734 VBoxEventType_T interested = aInterested[i];
735 for (int j = FirstEvent; j < LastEvent; j++)
736 {
737 VBoxEventType_T candidate = (VBoxEventType_T)j;
738 if (implies(interested, candidate))
739 {
740 (*aEvMap)[j - FirstEvent].add(this);
741 }
742 }
743 }
744
745 if (!mActive)
746 {
747 ::RTCritSectInit(&mcsQLock);
748 ::RTSemEventCreate(&mQEvent);
749 mLastRead = RTTimeMilliTS();
750 }
751 else
752 {
753 mQEvent = NIL_RTSEMEVENT;
754 RT_ZERO(mcsQLock);
755 mLastRead = 0;
756 }
757}
758
759ListenerRecord::~ListenerRecord()
760{
761 /* Remove references to us from the event map */
762 EventMap *aEvMap = &mOwner->m->mEvMap;
763 for (int j = FirstEvent; j < LastEvent; j++)
764 {
765 (*aEvMap)[j - FirstEvent].remove(this);
766 }
767
768 if (!mActive)
769 {
770 // at this moment nobody could add elements to our queue, so we can safely
771 // clean it up, otherwise there will be pending events map elements
772 PendingEventsMap *aPem = &mOwner->m->mPendingMap;
773 while (true)
774 {
775 ComPtr<IEvent> aEvent;
776
777 if (mQueue.empty())
778 break;
779
780 mQueue.front().queryInterfaceTo(aEvent.asOutParam());
781 mQueue.pop_front();
782
783 BOOL aWaitable = FALSE;
784 aEvent->COMGETTER(Waitable)(&aWaitable);
785 if (aWaitable)
786 {
787 PendingEventsMap::iterator pit = aPem->find(aEvent);
788 if (pit != aPem->end())
789 eventProcessed(aEvent, pit);
790 }
791 }
792
793 ::RTCritSectDelete(&mcsQLock);
794 }
795 shutdown();
796}
797
798HRESULT ListenerRecord::process(IEvent *aEvent,
799 BOOL aWaitable,
800 PendingEventsMap::iterator &pit,
801 AutoLockBase &aAlock)
802{
803 if (mActive)
804 {
805 /*
806 * We release lock here to allow modifying ops on EventSource inside callback.
807 */
808 HRESULT rc = S_OK;
809 if (mListener)
810 {
811 aAlock.release();
812 rc = mListener->HandleEvent(aEvent);
813#ifdef RT_OS_WINDOWS
814 Assert(rc != RPC_E_WRONG_THREAD);
815#endif
816 aAlock.acquire();
817 }
818 if (aWaitable)
819 eventProcessed(aEvent, pit);
820 return rc;
821 }
822 return enqueue(aEvent);
823}
824
825
826HRESULT ListenerRecord::enqueue(IEvent *aEvent)
827{
828 AssertMsg(!mActive, ("must be passive\n"));
829
830 // put an event the queue
831 ::RTCritSectEnter(&mcsQLock);
832
833 // If there was no events reading from the listener for the long time,
834 // and events keep coming, or queue is oversized we shall unregister this listener.
835 uint64_t sinceRead = RTTimeMilliTS() - mLastRead;
836 size_t queueSize = mQueue.size();
837 if ((queueSize > 1000) || ((queueSize > 500) && (sinceRead > 60 * 1000)))
838 {
839 ::RTCritSectLeave(&mcsQLock);
840 return E_ABORT;
841 }
842
843
844 if (queueSize != 0 && mQueue.back() == aEvent)
845 /* if same event is being pushed multiple times - it's reusable event and
846 we don't really need multiple instances of it in the queue */
847 (void)aEvent;
848 else
849 mQueue.push_back(aEvent);
850
851 ::RTCritSectLeave(&mcsQLock);
852
853 // notify waiters
854 ::RTSemEventSignal(mQEvent);
855
856 return S_OK;
857}
858
859HRESULT ListenerRecord::dequeue(IEvent **aEvent,
860 LONG aTimeout,
861 AutoLockBase &aAlock)
862{
863 if (mActive)
864 return VBOX_E_INVALID_OBJECT_STATE;
865
866 // retain listener record
867 RecordHolder<ListenerRecord> holder(this);
868
869 ::RTCritSectEnter(&mcsQLock);
870
871 mLastRead = RTTimeMilliTS();
872
873 if (mQueue.empty())
874 {
875 ::RTCritSectLeave(&mcsQLock);
876 // Speed up common case
877 if (aTimeout == 0)
878 {
879 *aEvent = NULL;
880 return S_OK;
881 }
882 // release lock while waiting, listener will not go away due to above holder
883 aAlock.release();
884 ::RTSemEventWait(mQEvent, aTimeout);
885 // reacquire lock
886 aAlock.acquire();
887 ::RTCritSectEnter(&mcsQLock);
888 }
889 if (mQueue.empty())
890 {
891 *aEvent = NULL;
892 }
893 else
894 {
895 mQueue.front().queryInterfaceTo(aEvent);
896 mQueue.pop_front();
897 }
898 ::RTCritSectLeave(&mcsQLock);
899 return S_OK;
900}
901
902HRESULT ListenerRecord::eventProcessed(IEvent *aEvent, PendingEventsMap::iterator &pit)
903{
904 if (--pit->second == 0)
905 {
906 Assert(pit->first == aEvent);
907 aEvent->SetProcessed();
908 mOwner->m->mPendingMap.erase(pit);
909 }
910
911 return S_OK;
912}
913
914void ListenerRecord::shutdown()
915{
916 if (mQEvent != NIL_RTSEMEVENT)
917 {
918 RTSEMEVENT tmp = mQEvent;
919 mQEvent = NIL_RTSEMEVENT;
920 ::RTSemEventDestroy(tmp);
921 }
922}
923
924EventSource::EventSource()
925{}
926
927EventSource::~EventSource()
928{}
929
930HRESULT EventSource::FinalConstruct()
931{
932 m = new Data;
933 return BaseFinalConstruct();
934}
935
936void EventSource::FinalRelease()
937{
938 uninit();
939 delete m;
940 BaseFinalRelease();
941}
942
943HRESULT EventSource::init()
944{
945 HRESULT rc = S_OK;
946
947 AutoInitSpan autoInitSpan(this);
948 AssertReturn(autoInitSpan.isOk(), E_FAIL);
949
950 /* Confirm a successful initialization */
951 autoInitSpan.setSucceeded();
952 return rc;
953}
954
955void EventSource::uninit()
956{
957 {
958 // First of all (before even thinking about entering the uninit span):
959 // make sure that all listeners are are shut down (no pending events or
960 // wait calls), because they cannot be alive without the associated
961 // event source. Otherwise API clients which use long-term (or
962 // indefinite) waits will block VBoxSVC termination (just one example)
963 // for a long time or even infinitely long.
964 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
965 if (!m->fShutdown)
966 {
967 m->fShutdown = true;
968 for (Listeners::iterator it = m->mListeners.begin();
969 it != m->mListeners.end();
970 ++it)
971 {
972 it->second.obj()->shutdown();
973 }
974 }
975 }
976
977 AutoUninitSpan autoUninitSpan(this);
978 if (autoUninitSpan.uninitDone())
979 return;
980
981 m->mListeners.clear();
982 // m->mEvMap shall be cleared at this point too by destructors, assert?
983}
984
985STDMETHODIMP EventSource::RegisterListener(IEventListener *aListener,
986 ComSafeArrayIn(VBoxEventType_T, aInterested),
987 BOOL aActive)
988{
989 CheckComArgNotNull(aListener);
990 CheckComArgSafeArrayNotNull(aInterested);
991
992 AutoCaller autoCaller(this);
993 if (FAILED(autoCaller.rc()))
994 return autoCaller.rc();
995
996 {
997 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
998
999 if (m->fShutdown)
1000 return setError(VBOX_E_INVALID_OBJECT_STATE,
1001 tr("This event source is already shut down"));
1002
1003 Listeners::const_iterator it = m->mListeners.find(aListener);
1004 if (it != m->mListeners.end())
1005 return setError(E_INVALIDARG,
1006 tr("This listener already registered"));
1007
1008 com::SafeArray<VBoxEventType_T> interested(ComSafeArrayInArg(aInterested));
1009 RecordHolder<ListenerRecord> lrh(new ListenerRecord(aListener, interested, aActive, this));
1010 m->mListeners.insert(Listeners::value_type(aListener, lrh));
1011 }
1012
1013 VBoxEventDesc evDesc;
1014 evDesc.init(this, VBoxEventType_OnEventSourceChanged, aListener, TRUE);
1015 evDesc.fire(0);
1016
1017 return S_OK;
1018}
1019
1020STDMETHODIMP EventSource::UnregisterListener(IEventListener *aListener)
1021{
1022 CheckComArgNotNull(aListener);
1023
1024 AutoCaller autoCaller(this);
1025 if (FAILED(autoCaller.rc()))
1026 return autoCaller.rc();
1027
1028 HRESULT rc;
1029 {
1030 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1031
1032 Listeners::iterator it = m->mListeners.find(aListener);
1033
1034 if (it != m->mListeners.end())
1035 {
1036 it->second.obj()->shutdown();
1037 m->mListeners.erase(it);
1038 // destructor removes refs from the event map
1039 rc = S_OK;
1040 }
1041 else
1042 {
1043 rc = setError(VBOX_E_OBJECT_NOT_FOUND,
1044 tr("Listener was never registered"));
1045 }
1046 }
1047
1048 if (SUCCEEDED(rc))
1049 {
1050 VBoxEventDesc evDesc;
1051 evDesc.init(this, VBoxEventType_OnEventSourceChanged, aListener, FALSE);
1052 evDesc.fire(0);
1053 }
1054
1055 return rc;
1056}
1057
1058STDMETHODIMP EventSource::FireEvent(IEvent *aEvent,
1059 LONG aTimeout,
1060 BOOL *aProcessed)
1061{
1062 CheckComArgNotNull(aEvent);
1063 CheckComArgOutPointerValid(aProcessed);
1064
1065 AutoCaller autoCaller(this);
1066 if (FAILED(autoCaller.rc()))
1067 return autoCaller.rc();
1068
1069 HRESULT hrc;
1070 BOOL aWaitable = FALSE;
1071 aEvent->COMGETTER(Waitable)(&aWaitable);
1072
1073 do {
1074 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1075
1076 if (m->fShutdown)
1077 return setError(VBOX_E_INVALID_OBJECT_STATE,
1078 tr("This event source is already shut down"));
1079
1080 VBoxEventType_T evType;
1081 hrc = aEvent->COMGETTER(Type)(&evType);
1082 AssertComRCReturn(hrc, hrc);
1083
1084 EventMapList &listeners = m->mEvMap[(int)evType - FirstEvent];
1085
1086 /* Anyone interested in this event? */
1087 uint32_t cListeners = listeners.size();
1088 if (cListeners == 0)
1089 {
1090 aEvent->SetProcessed();
1091 break; // just leave the lock and update event object state
1092 }
1093
1094 PendingEventsMap::iterator pit;
1095
1096 if (aWaitable)
1097 {
1098 m->mPendingMap.insert(PendingEventsMap::value_type(aEvent, cListeners));
1099 // we keep iterator here to allow processing active listeners without
1100 // pending events lookup
1101 pit = m->mPendingMap.find(aEvent);
1102 }
1103 for (EventMapList::iterator it = listeners.begin();
1104 it != listeners.end();
1105 ++it)
1106 {
1107 HRESULT cbRc;
1108 // keep listener record reference, in case someone will remove it while in callback
1109 RecordHolder<ListenerRecord> record(*it);
1110
1111 /*
1112 * We pass lock here to allow modifying ops on EventSource inside callback
1113 * in active mode. Note that we expect list iterator stability as 'alock'
1114 * could be temporary released when calling event handler.
1115 */
1116 cbRc = record.obj()->process(aEvent, aWaitable, pit, alock);
1117
1118 /* Note that E_ABORT is used above to signal that a passive
1119 * listener was unregistered due to not picking up its event.
1120 * This overlaps with XPCOM specific use of E_ABORT to signal
1121 * death of an active listener, but that's irrelevant here. */
1122 if (FAILED_DEAD_INTERFACE(cbRc) || cbRc == E_ABORT)
1123 {
1124 Listeners::iterator lit = m->mListeners.find(record.obj()->mListener);
1125 if (lit != m->mListeners.end())
1126 {
1127 lit->second.obj()->shutdown();
1128 m->mListeners.erase(lit);
1129 }
1130 }
1131 // anything else to do with cbRc?
1132 }
1133 } while (0);
1134 /* We leave the lock here */
1135
1136 if (aWaitable)
1137 hrc = aEvent->WaitProcessed(aTimeout, aProcessed);
1138 else
1139 *aProcessed = TRUE;
1140
1141 return hrc;
1142}
1143
1144
1145STDMETHODIMP EventSource::GetEvent(IEventListener *aListener,
1146 LONG aTimeout,
1147 IEvent **aEvent)
1148{
1149
1150 CheckComArgNotNull(aListener);
1151
1152 AutoCaller autoCaller(this);
1153 if (FAILED(autoCaller.rc()))
1154 return autoCaller.rc();
1155
1156 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1157
1158 if (m->fShutdown)
1159 return setError(VBOX_E_INVALID_OBJECT_STATE,
1160 tr("This event source is already shut down"));
1161
1162 Listeners::iterator it = m->mListeners.find(aListener);
1163 HRESULT rc;
1164
1165 if (it != m->mListeners.end())
1166 rc = it->second.obj()->dequeue(aEvent, aTimeout, alock);
1167 else
1168 rc = setError(VBOX_E_OBJECT_NOT_FOUND,
1169 tr("Listener was never registered"));
1170
1171 if (rc == VBOX_E_INVALID_OBJECT_STATE)
1172 return setError(rc, tr("Listener must be passive"));
1173
1174 return rc;
1175}
1176
1177STDMETHODIMP EventSource::EventProcessed(IEventListener *aListener,
1178 IEvent *aEvent)
1179{
1180 CheckComArgNotNull(aListener);
1181 CheckComArgNotNull(aEvent);
1182
1183 AutoCaller autoCaller(this);
1184 if (FAILED(autoCaller.rc()))
1185 return autoCaller.rc();
1186
1187 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1188
1189 if (m->fShutdown)
1190 return setError(VBOX_E_INVALID_OBJECT_STATE,
1191 tr("This event source is already shut down"));
1192
1193 Listeners::iterator it = m->mListeners.find(aListener);
1194 HRESULT rc;
1195
1196 BOOL aWaitable = FALSE;
1197 aEvent->COMGETTER(Waitable)(&aWaitable);
1198
1199 if (it != m->mListeners.end())
1200 {
1201 ListenerRecord *aRecord = it->second.obj();
1202
1203 if (aRecord->isActive())
1204 return setError(E_INVALIDARG,
1205 tr("Only applicable to passive listeners"));
1206
1207 if (aWaitable)
1208 {
1209 PendingEventsMap::iterator pit = m->mPendingMap.find(aEvent);
1210
1211 if (pit == m->mPendingMap.end())
1212 {
1213 AssertFailed();
1214 rc = setError(VBOX_E_OBJECT_NOT_FOUND,
1215 tr("Unknown event"));
1216 }
1217 else
1218 rc = aRecord->eventProcessed(aEvent, pit);
1219 }
1220 else
1221 {
1222 // for non-waitable events we're done
1223 rc = S_OK;
1224 }
1225 }
1226 else
1227 {
1228 rc = setError(VBOX_E_OBJECT_NOT_FOUND,
1229 tr("Listener was never registered"));
1230 }
1231
1232 return rc;
1233}
1234
1235/**
1236 * This class serves as feasible listener implementation
1237 * which could be used by clients not able to create local
1238 * COM objects, but still willing to receive event
1239 * notifications in passive mode, such as webservices.
1240 */
1241class ATL_NO_VTABLE PassiveEventListener :
1242 public VirtualBoxBase,
1243 VBOX_SCRIPTABLE_IMPL(IEventListener)
1244{
1245public:
1246
1247 VIRTUALBOXBASE_ADD_ERRORINFO_SUPPORT(PassiveEventListener, IEventListener)
1248
1249 DECLARE_NOT_AGGREGATABLE(PassiveEventListener)
1250
1251 DECLARE_PROTECT_FINAL_CONSTRUCT()
1252
1253 BEGIN_COM_MAP(PassiveEventListener)
1254 VBOX_DEFAULT_INTERFACE_ENTRIES(IEventListener)
1255 END_COM_MAP()
1256
1257 PassiveEventListener()
1258 {}
1259 ~PassiveEventListener()
1260 {}
1261
1262 HRESULT FinalConstruct()
1263 {
1264 return BaseFinalConstruct();
1265 }
1266 void FinalRelease()
1267 {
1268 BaseFinalRelease();
1269 }
1270
1271 // IEventListener methods
1272 STDMETHOD(HandleEvent)(IEvent *)
1273 {
1274 ComAssertMsgRet(false, ("HandleEvent() of wrapper shall never be called"),
1275 E_FAIL);
1276 }
1277};
1278
1279/* Proxy listener class, used to aggregate multiple event sources into one */
1280class ATL_NO_VTABLE ProxyEventListener :
1281 public VirtualBoxBase,
1282 VBOX_SCRIPTABLE_IMPL(IEventListener)
1283{
1284 ComPtr<IEventSource> mSource;
1285public:
1286
1287 VIRTUALBOXBASE_ADD_ERRORINFO_SUPPORT(ProxyEventListener, IEventListener)
1288
1289 DECLARE_NOT_AGGREGATABLE(ProxyEventListener)
1290
1291 DECLARE_PROTECT_FINAL_CONSTRUCT()
1292
1293 BEGIN_COM_MAP(ProxyEventListener)
1294 VBOX_DEFAULT_INTERFACE_ENTRIES(IEventListener)
1295 END_COM_MAP()
1296
1297 ProxyEventListener()
1298 {}
1299 ~ProxyEventListener()
1300 {}
1301
1302 HRESULT FinalConstruct()
1303 {
1304 return BaseFinalConstruct();
1305 }
1306 void FinalRelease()
1307 {
1308 BaseFinalRelease();
1309 }
1310
1311 HRESULT init(IEventSource *aSource)
1312 {
1313 mSource = aSource;
1314 return S_OK;
1315 }
1316
1317 // IEventListener methods
1318 STDMETHOD(HandleEvent)(IEvent *aEvent)
1319 {
1320 BOOL fProcessed = FALSE;
1321 if (mSource)
1322 return mSource->FireEvent(aEvent, 0, &fProcessed);
1323 else
1324 return S_OK;
1325 }
1326};
1327
1328class ATL_NO_VTABLE EventSourceAggregator :
1329 public VirtualBoxBase,
1330 VBOX_SCRIPTABLE_IMPL(IEventSource)
1331{
1332 typedef std::list <ComPtr<IEventSource> > EventSourceList;
1333 /* key is weak reference */
1334 typedef std::map<IEventListener *, ComPtr<IEventListener> > ProxyListenerMap;
1335
1336 EventSourceList mEventSources;
1337 ProxyListenerMap mListenerProxies;
1338 ComObjPtr<EventSource> mSource;
1339
1340public:
1341
1342 VIRTUALBOXBASE_ADD_ERRORINFO_SUPPORT(EventSourceAggregator, IEventSource)
1343
1344 DECLARE_NOT_AGGREGATABLE(EventSourceAggregator)
1345
1346 DECLARE_PROTECT_FINAL_CONSTRUCT()
1347
1348 BEGIN_COM_MAP(EventSourceAggregator)
1349 VBOX_DEFAULT_INTERFACE_ENTRIES(IEventSource)
1350 END_COM_MAP()
1351
1352 EventSourceAggregator()
1353 {}
1354 ~EventSourceAggregator()
1355 {}
1356
1357 HRESULT FinalConstruct()
1358 {
1359 return BaseFinalConstruct();
1360 }
1361 void FinalRelease()
1362 {
1363 mEventSources.clear();
1364 mListenerProxies.clear();
1365 mSource->uninit();
1366 BaseFinalRelease();
1367 }
1368
1369 // internal public
1370 HRESULT init(ComSafeArrayIn(IEventSource *, aSources));
1371
1372 // IEventSource methods
1373 STDMETHOD(CreateListener)(IEventListener **aListener);
1374 STDMETHOD(CreateAggregator)(ComSafeArrayIn(IEventSource *, aSubordinates),
1375 IEventSource **aAggregator);
1376 STDMETHOD(RegisterListener)(IEventListener *aListener,
1377 ComSafeArrayIn(VBoxEventType_T, aInterested),
1378 BOOL aActive);
1379 STDMETHOD(UnregisterListener)(IEventListener *aListener);
1380 STDMETHOD(FireEvent)(IEvent *aEvent,
1381 LONG aTimeout,
1382 BOOL *aProcessed);
1383 STDMETHOD(GetEvent)(IEventListener *aListener,
1384 LONG aTimeout,
1385 IEvent **aEvent);
1386 STDMETHOD(EventProcessed)(IEventListener *aListener,
1387 IEvent *aEvent);
1388
1389 protected:
1390 HRESULT createProxyListener(IEventListener *aListener,
1391 IEventListener **aProxy);
1392 HRESULT getProxyListener(IEventListener *aListener,
1393 IEventListener **aProxy);
1394 HRESULT removeProxyListener(IEventListener *aListener);
1395};
1396
1397#ifdef VBOX_WITH_XPCOM
1398NS_DECL_CLASSINFO(ProxyEventListener)
1399NS_IMPL_THREADSAFE_ISUPPORTS1_CI(ProxyEventListener, IEventListener)
1400NS_DECL_CLASSINFO(PassiveEventListener)
1401NS_IMPL_THREADSAFE_ISUPPORTS1_CI(PassiveEventListener, IEventListener)
1402NS_DECL_CLASSINFO(VBoxEvent)
1403NS_IMPL_THREADSAFE_ISUPPORTS1_CI(VBoxEvent, IEvent)
1404NS_DECL_CLASSINFO(VBoxVetoEvent)
1405NS_IMPL_ISUPPORTS_INHERITED1(VBoxVetoEvent, VBoxEvent, IVetoEvent)
1406NS_DECL_CLASSINFO(EventSource)
1407NS_IMPL_THREADSAFE_ISUPPORTS1_CI(EventSource, IEventSource)
1408NS_DECL_CLASSINFO(EventSourceAggregator)
1409NS_IMPL_THREADSAFE_ISUPPORTS1_CI(EventSourceAggregator, IEventSource)
1410#endif
1411
1412
1413STDMETHODIMP EventSource::CreateListener(IEventListener **aListener)
1414{
1415 CheckComArgOutPointerValid(aListener);
1416
1417 AutoCaller autoCaller(this);
1418 if (FAILED(autoCaller.rc()))
1419 return autoCaller.rc();
1420
1421 ComObjPtr<PassiveEventListener> listener;
1422
1423 HRESULT rc = listener.createObject();
1424 ComAssertMsgRet(SUCCEEDED(rc), ("Could not create wrapper object (%Rhrc)", rc),
1425 E_FAIL);
1426 listener.queryInterfaceTo(aListener);
1427 return S_OK;
1428}
1429
1430
1431STDMETHODIMP EventSource::CreateAggregator(ComSafeArrayIn(IEventSource *, aSubordinates),
1432 IEventSource **aResult)
1433{
1434 CheckComArgOutPointerValid(aResult);
1435
1436 AutoCaller autoCaller(this);
1437 if (FAILED(autoCaller.rc()))
1438 return autoCaller.rc();
1439
1440 ComObjPtr<EventSourceAggregator> agg;
1441
1442 HRESULT rc = agg.createObject();
1443 ComAssertMsgRet(SUCCEEDED(rc), ("Could not create aggregator (%Rhrc)", rc),
1444 E_FAIL);
1445
1446 rc = agg->init(ComSafeArrayInArg(aSubordinates));
1447 if (FAILED(rc))
1448 return rc;
1449
1450 agg.queryInterfaceTo(aResult);
1451 return S_OK;
1452}
1453
1454HRESULT EventSourceAggregator::init(ComSafeArrayIn(IEventSource *, aSourcesIn))
1455{
1456 HRESULT rc;
1457
1458 AutoInitSpan autoInitSpan(this);
1459 AssertReturn(autoInitSpan.isOk(), E_FAIL);
1460
1461 rc = mSource.createObject();
1462 ComAssertMsgRet(SUCCEEDED(rc), ("Could not create source (%Rhrc)", rc),
1463 E_FAIL);
1464 rc = mSource->init();
1465 ComAssertMsgRet(SUCCEEDED(rc), ("Could not init source (%Rhrc)", rc),
1466 E_FAIL);
1467
1468 com::SafeIfaceArray<IEventSource> aSources(ComSafeArrayInArg (aSourcesIn));
1469
1470 size_t cSize = aSources.size();
1471
1472 for (size_t i = 0; i < cSize; i++)
1473 {
1474 if (aSources[i] != NULL)
1475 mEventSources.push_back(aSources[i]);
1476 }
1477
1478 /* Confirm a successful initialization */
1479 autoInitSpan.setSucceeded();
1480
1481 return rc;
1482}
1483
1484STDMETHODIMP EventSourceAggregator::CreateListener(IEventListener **aListener)
1485{
1486 return mSource->CreateListener(aListener);
1487}
1488
1489STDMETHODIMP EventSourceAggregator::CreateAggregator(ComSafeArrayIn(IEventSource *, aSubordinates),
1490 IEventSource **aResult)
1491{
1492 return mSource->CreateAggregator(ComSafeArrayInArg(aSubordinates), aResult);
1493}
1494
1495STDMETHODIMP EventSourceAggregator::RegisterListener(IEventListener *aListener,
1496 ComSafeArrayIn(VBoxEventType_T, aInterested),
1497 BOOL aActive)
1498{
1499 CheckComArgNotNull(aListener);
1500 CheckComArgSafeArrayNotNull(aInterested);
1501
1502 AutoCaller autoCaller(this);
1503 if (FAILED(autoCaller.rc()))
1504 return autoCaller.rc();
1505
1506 HRESULT rc;
1507
1508 ComPtr<IEventListener> proxy;
1509 rc = createProxyListener(aListener, proxy.asOutParam());
1510 if (FAILED(rc))
1511 return rc;
1512
1513 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1514 for (EventSourceList::const_iterator it = mEventSources.begin(); it != mEventSources.end();
1515 ++it)
1516 {
1517 ComPtr<IEventSource> es = *it;
1518 /* Register active proxy listener on real event source */
1519 rc = es->RegisterListener(proxy, ComSafeArrayInArg(aInterested), TRUE);
1520 }
1521 /* And add real listener on our event source */
1522 rc = mSource->RegisterListener(aListener, ComSafeArrayInArg(aInterested), aActive);
1523
1524 rc = S_OK;
1525
1526 return rc;
1527}
1528
1529STDMETHODIMP EventSourceAggregator::UnregisterListener(IEventListener *aListener)
1530{
1531 CheckComArgNotNull(aListener);
1532
1533 AutoCaller autoCaller(this);
1534 if (FAILED(autoCaller.rc()))
1535 return autoCaller.rc();
1536
1537 HRESULT rc = S_OK;
1538
1539 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1540
1541 ComPtr<IEventListener> proxy;
1542 rc = getProxyListener(aListener, proxy.asOutParam());
1543 if (FAILED(rc))
1544 return rc;
1545
1546 for (EventSourceList::const_iterator it = mEventSources.begin(); it != mEventSources.end();
1547 ++it)
1548 {
1549 ComPtr<IEventSource> es = *it;
1550 rc = es->UnregisterListener(proxy);
1551 }
1552 rc = mSource->UnregisterListener(aListener);
1553
1554 return removeProxyListener(aListener);
1555
1556}
1557
1558STDMETHODIMP EventSourceAggregator::FireEvent(IEvent *aEvent,
1559 LONG aTimeout,
1560 BOOL *aProcessed)
1561{
1562 CheckComArgNotNull(aEvent);
1563 CheckComArgOutPointerValid(aProcessed);
1564
1565 AutoCaller autoCaller(this);
1566 if (FAILED(autoCaller.rc()))
1567 return autoCaller.rc();
1568
1569 HRESULT rc = S_OK;
1570 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1571 /* Aggregator event source shall not have direct event firing, but we may
1572 wish to support aggregation chains */
1573 for (EventSourceList::const_iterator it = mEventSources.begin(); it != mEventSources.end();
1574 ++it)
1575 {
1576 ComPtr<IEventSource> es = *it;
1577 rc = es->FireEvent(aEvent, aTimeout, aProcessed);
1578 /* Current behavior is that aggregator's FireEvent() always succeeds,
1579 so that multiple event sources don't affect each other. */
1580 NOREF(rc);
1581 }
1582
1583 return S_OK;
1584}
1585
1586STDMETHODIMP EventSourceAggregator::GetEvent(IEventListener *aListener,
1587 LONG aTimeout,
1588 IEvent **aEvent)
1589{
1590 return mSource->GetEvent(aListener, aTimeout, aEvent);
1591}
1592
1593STDMETHODIMP EventSourceAggregator::EventProcessed(IEventListener *aListener,
1594 IEvent *aEvent)
1595{
1596 return mSource->EventProcessed(aListener, aEvent);
1597}
1598
1599HRESULT EventSourceAggregator::createProxyListener(IEventListener *aListener,
1600 IEventListener **aProxy)
1601{
1602 ComObjPtr<ProxyEventListener> proxy;
1603
1604 HRESULT rc = proxy.createObject();
1605 ComAssertMsgRet(SUCCEEDED(rc), ("Could not create proxy (%Rhrc)", rc),
1606 E_FAIL);
1607
1608 rc = proxy->init(mSource);
1609 if (FAILED(rc))
1610 return rc;
1611
1612 ProxyListenerMap::const_iterator it = mListenerProxies.find(aListener);
1613 if (it != mListenerProxies.end())
1614 return setError(E_INVALIDARG,
1615 tr("This listener already registered"));
1616
1617 mListenerProxies.insert(ProxyListenerMap::value_type(aListener, proxy));
1618
1619 proxy.queryInterfaceTo(aProxy);
1620 return S_OK;
1621}
1622
1623HRESULT EventSourceAggregator::getProxyListener(IEventListener *aListener,
1624 IEventListener **aProxy)
1625{
1626 ProxyListenerMap::const_iterator it = mListenerProxies.find(aListener);
1627 if (it == mListenerProxies.end())
1628 return setError(E_INVALIDARG,
1629 tr("This listener never registered"));
1630
1631 (*it).second.queryInterfaceTo(aProxy);
1632 return S_OK;
1633}
1634
1635HRESULT EventSourceAggregator::removeProxyListener(IEventListener *aListener)
1636{
1637 ProxyListenerMap::iterator it = mListenerProxies.find(aListener);
1638 if (it == mListenerProxies.end())
1639 return setError(E_INVALIDARG,
1640 tr("This listener never registered"));
1641
1642 mListenerProxies.erase(it);
1643 return S_OK;
1644}
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