VirtualBox

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

Last change on this file since 62514 was 61774, checked in by vboxsync, 9 years ago

ListenerRecord::enqueue: Don't try signal semaphore or even queue the event if we've been shut down already. Hope the latter is a good idea...
Attempted to deal with event sem lifetime races between shutdown and enqueue, and between shutdown and wait.

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