VirtualBox

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

Last change on this file since 94521 was 93188, checked in by vboxsync, 2 years ago

Main/Event: defunct passive event listeners prevent subsequent vetoable
events from being processed bugref:10000

If a passive event listener has registered an interest in a vetoable
event and subsequently disappears then any later passive event listeners
which are interested in the same vetoable event won't ever see
IEvent::setProcessed() called. The most common scenario where this is
seen is when the VBox GUI crashes and restarts during the same VBoxSVC
instance after which any time an OnExtraDataCanChange event is
triggered, such as by clicking a VM's name, it incurs a three second
timeout where nothing can be done in the GUI during that time. The fix
removes OnExtraDataCanChange from the unresponsive passive event
listener's list of interested events after it times out the first time
which means that the annoying three seconds of GUI inactivity will only
be seen once.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use