VirtualBox

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

Last change on this file since 78402 was 76592, checked in by vboxsync, 6 years ago

Main: Don't use Logging.h.

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