VirtualBox

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

Last change on this file was 103977, checked in by vboxsync, 8 weeks ago

Apply RT_OVERRIDE/NS_OVERRIDE where required to shut up clang.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use