VirtualBox

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

Last change on this file since 52881 was 52734, checked in by vboxsync, 10 years ago

Main/Event: add uninit state handling for VBoxEvent, and the full init/uninit state handling for VBoxVetoEvent, the wrappers insist on proper state

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