VirtualBox

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

Last change on this file since 92154 was 91503, checked in by vboxsync, 3 years ago

Main: bugref:1909: Added missed translation marks, removed redundant ones. Expanded one macro to make the lupdate get string correctly. Removed GuestBase::setErrorExternal and changed calls from it to setErrorBoth to handle translation correctly.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use