VirtualBox

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

Last change on this file since 85239 was 85239, checked in by vboxsync, 4 years ago

Main/EventImpl.cpp: Made VBoxEvent::waitProcessed and ListenerRecord::dequeue interpret all negative timeout values as meaning RT_INDEFINITE_WAIT like we do elsewhere in the API. Stumbled upon this due to a sign conversion issue highlighted by Clang 11. bugref:9790

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette