VirtualBox

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

Last change on this file since 95433 was 95428, checked in by vboxsync, 2 years ago

Main: Resolved a @todo: Renamed VBoxEventType_Last -> VBoxEventType_End.

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