VirtualBox

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

Last change on this file since 59083 was 55523, checked in by vboxsync, 10 years ago

Main: extend IVetoEvent interface to support approvals too.

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