VirtualBox

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

Last change on this file since 54017 was 53160, checked in by vboxsync, 10 years ago

Main/EventImpl.cpp: fix passive event handling regression from wrapper conversion

  • 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 53160 2014-10-28 16:26:43Z 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 if (it != m->mListeners.end())
1152 rc = it->second.obj()->dequeue(aEvent.asOutParam(), aTimeout, alock);
1153 else
1154 rc = setError(VBOX_E_OBJECT_NOT_FOUND,
1155 tr("Listener was never registered"));
1156
1157 if (rc == VBOX_E_INVALID_OBJECT_STATE)
1158 return setError(rc, tr("Listener must be passive"));
1159
1160 return rc;
1161}
1162
1163HRESULT EventSource::eventProcessed(const ComPtr<IEventListener> &aListener,
1164 const ComPtr<IEvent> &aEvent)
1165{
1166 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1167
1168 if (m->fShutdown)
1169 return setError(VBOX_E_INVALID_OBJECT_STATE,
1170 tr("This event source is already shut down"));
1171
1172 Listeners::iterator it = m->mListeners.find(aListener);
1173 HRESULT rc;
1174
1175 BOOL aWaitable = FALSE;
1176 aEvent->COMGETTER(Waitable)(&aWaitable);
1177
1178 if (it != m->mListeners.end())
1179 {
1180 ListenerRecord *aRecord = it->second.obj();
1181
1182 if (aRecord->isActive())
1183 return setError(E_INVALIDARG,
1184 tr("Only applicable to passive listeners"));
1185
1186 if (aWaitable)
1187 {
1188 PendingEventsMap::iterator pit = m->mPendingMap.find(aEvent);
1189
1190 if (pit == m->mPendingMap.end())
1191 {
1192 AssertFailed();
1193 rc = setError(VBOX_E_OBJECT_NOT_FOUND,
1194 tr("Unknown event"));
1195 }
1196 else
1197 rc = aRecord->eventProcessed(aEvent, pit);
1198 }
1199 else
1200 {
1201 // for non-waitable events we're done
1202 rc = S_OK;
1203 }
1204 }
1205 else
1206 {
1207 rc = setError(VBOX_E_OBJECT_NOT_FOUND,
1208 tr("Listener was never registered"));
1209 }
1210
1211 return rc;
1212}
1213
1214/**
1215 * This class serves as feasible listener implementation
1216 * which could be used by clients not able to create local
1217 * COM objects, but still willing to receive event
1218 * notifications in passive mode, such as webservices.
1219 */
1220class ATL_NO_VTABLE PassiveEventListener :
1221 public VirtualBoxBase,
1222 VBOX_SCRIPTABLE_IMPL(IEventListener)
1223{
1224public:
1225
1226 VIRTUALBOXBASE_ADD_ERRORINFO_SUPPORT(PassiveEventListener, IEventListener)
1227
1228 DECLARE_NOT_AGGREGATABLE(PassiveEventListener)
1229
1230 DECLARE_PROTECT_FINAL_CONSTRUCT()
1231
1232 BEGIN_COM_MAP(PassiveEventListener)
1233 VBOX_DEFAULT_INTERFACE_ENTRIES(IEventListener)
1234 END_COM_MAP()
1235
1236 PassiveEventListener()
1237 {}
1238 ~PassiveEventListener()
1239 {}
1240
1241 HRESULT FinalConstruct()
1242 {
1243 return BaseFinalConstruct();
1244 }
1245 void FinalRelease()
1246 {
1247 BaseFinalRelease();
1248 }
1249
1250 // IEventListener methods
1251 STDMETHOD(HandleEvent)(IEvent *)
1252 {
1253 ComAssertMsgRet(false, ("HandleEvent() of wrapper shall never be called"),
1254 E_FAIL);
1255 }
1256};
1257
1258/* Proxy listener class, used to aggregate multiple event sources into one */
1259class ATL_NO_VTABLE ProxyEventListener :
1260 public VirtualBoxBase,
1261 VBOX_SCRIPTABLE_IMPL(IEventListener)
1262{
1263 ComPtr<IEventSource> mSource;
1264public:
1265
1266 VIRTUALBOXBASE_ADD_ERRORINFO_SUPPORT(ProxyEventListener, IEventListener)
1267
1268 DECLARE_NOT_AGGREGATABLE(ProxyEventListener)
1269
1270 DECLARE_PROTECT_FINAL_CONSTRUCT()
1271
1272 BEGIN_COM_MAP(ProxyEventListener)
1273 VBOX_DEFAULT_INTERFACE_ENTRIES(IEventListener)
1274 END_COM_MAP()
1275
1276 ProxyEventListener()
1277 {}
1278 ~ProxyEventListener()
1279 {}
1280
1281 HRESULT FinalConstruct()
1282 {
1283 return BaseFinalConstruct();
1284 }
1285 void FinalRelease()
1286 {
1287 BaseFinalRelease();
1288 }
1289
1290 HRESULT init(IEventSource *aSource)
1291 {
1292 mSource = aSource;
1293 return S_OK;
1294 }
1295
1296 // IEventListener methods
1297 STDMETHOD(HandleEvent)(IEvent *aEvent)
1298 {
1299 BOOL fProcessed = FALSE;
1300 if (mSource)
1301 return mSource->FireEvent(aEvent, 0, &fProcessed);
1302 else
1303 return S_OK;
1304 }
1305};
1306
1307class ATL_NO_VTABLE EventSourceAggregator :
1308 public VirtualBoxBase,
1309 VBOX_SCRIPTABLE_IMPL(IEventSource)
1310{
1311 typedef std::list <ComPtr<IEventSource> > EventSourceList;
1312 /* key is weak reference */
1313 typedef std::map<IEventListener *, ComPtr<IEventListener> > ProxyListenerMap;
1314
1315 EventSourceList mEventSources;
1316 ProxyListenerMap mListenerProxies;
1317 ComObjPtr<EventSource> mSource;
1318
1319public:
1320
1321 VIRTUALBOXBASE_ADD_ERRORINFO_SUPPORT(EventSourceAggregator, IEventSource)
1322
1323 DECLARE_NOT_AGGREGATABLE(EventSourceAggregator)
1324
1325 DECLARE_PROTECT_FINAL_CONSTRUCT()
1326
1327 BEGIN_COM_MAP(EventSourceAggregator)
1328 VBOX_DEFAULT_INTERFACE_ENTRIES(IEventSource)
1329 END_COM_MAP()
1330
1331 EventSourceAggregator()
1332 {}
1333 ~EventSourceAggregator()
1334 {}
1335
1336 HRESULT FinalConstruct()
1337 {
1338 return BaseFinalConstruct();
1339 }
1340 void FinalRelease()
1341 {
1342 mEventSources.clear();
1343 mListenerProxies.clear();
1344 mSource->uninit();
1345 BaseFinalRelease();
1346 }
1347
1348 // internal public
1349 HRESULT init(const std::vector<ComPtr<IEventSource> > aSourcesIn);
1350
1351 // IEventSource methods
1352 STDMETHOD(CreateListener)(IEventListener **aListener);
1353 STDMETHOD(CreateAggregator)(ComSafeArrayIn(IEventSource *, aSubordinates),
1354 IEventSource **aAggregator);
1355 STDMETHOD(RegisterListener)(IEventListener *aListener,
1356 ComSafeArrayIn(VBoxEventType_T, aInterested),
1357 BOOL aActive);
1358 STDMETHOD(UnregisterListener)(IEventListener *aListener);
1359 STDMETHOD(FireEvent)(IEvent *aEvent,
1360 LONG aTimeout,
1361 BOOL *aProcessed);
1362 STDMETHOD(GetEvent)(IEventListener *aListener,
1363 LONG aTimeout,
1364 IEvent **aEvent);
1365 STDMETHOD(EventProcessed)(IEventListener *aListener,
1366 IEvent *aEvent);
1367
1368 protected:
1369 HRESULT createProxyListener(IEventListener *aListener,
1370 IEventListener **aProxy);
1371 HRESULT getProxyListener(IEventListener *aListener,
1372 IEventListener **aProxy);
1373 HRESULT removeProxyListener(IEventListener *aListener);
1374};
1375
1376#ifdef VBOX_WITH_XPCOM
1377NS_DECL_CLASSINFO(ProxyEventListener)
1378NS_IMPL_THREADSAFE_ISUPPORTS1_CI(ProxyEventListener, IEventListener)
1379NS_DECL_CLASSINFO(PassiveEventListener)
1380NS_IMPL_THREADSAFE_ISUPPORTS1_CI(PassiveEventListener, IEventListener)
1381NS_DECL_CLASSINFO(EventSourceAggregator)
1382NS_IMPL_THREADSAFE_ISUPPORTS1_CI(EventSourceAggregator, IEventSource)
1383#endif
1384
1385
1386HRESULT EventSource::createListener(ComPtr<IEventListener> &aListener)
1387{
1388 ComObjPtr<PassiveEventListener> listener;
1389
1390 HRESULT rc = listener.createObject();
1391 ComAssertMsgRet(SUCCEEDED(rc), ("Could not create wrapper object (%Rhrc)", rc),
1392 E_FAIL);
1393 listener.queryInterfaceTo(aListener.asOutParam());
1394 return S_OK;
1395}
1396
1397HRESULT EventSource::createAggregator(const std::vector<ComPtr<IEventSource> > &aSubordinates,
1398 ComPtr<IEventSource> &aResult)
1399{
1400 ComObjPtr<EventSourceAggregator> agg;
1401
1402 HRESULT rc = agg.createObject();
1403 ComAssertMsgRet(SUCCEEDED(rc), ("Could not create aggregator (%Rhrc)", rc),
1404 E_FAIL);
1405
1406 rc = agg->init(aSubordinates);
1407 if (FAILED(rc))
1408 return rc;
1409
1410 agg.queryInterfaceTo(aResult.asOutParam());
1411 return S_OK;
1412}
1413
1414HRESULT EventSourceAggregator::init(const std::vector<ComPtr<IEventSource> > aSourcesIn)
1415{
1416 HRESULT rc;
1417
1418 AutoInitSpan autoInitSpan(this);
1419 AssertReturn(autoInitSpan.isOk(), E_FAIL);
1420
1421 rc = mSource.createObject();
1422 ComAssertMsgRet(SUCCEEDED(rc), ("Could not create source (%Rhrc)", rc),
1423 E_FAIL);
1424 rc = mSource->init();
1425 ComAssertMsgRet(SUCCEEDED(rc), ("Could not init source (%Rhrc)", rc),
1426 E_FAIL);
1427
1428 for (size_t i = 0; i < aSourcesIn.size(); i++)
1429 {
1430 if (aSourcesIn[i] != NULL)
1431 mEventSources.push_back(aSourcesIn[i]);
1432 }
1433
1434 /* Confirm a successful initialization */
1435 autoInitSpan.setSucceeded();
1436
1437 return rc;
1438}
1439
1440STDMETHODIMP EventSourceAggregator::CreateListener(IEventListener **aListener)
1441{
1442 return mSource->CreateListener(aListener);
1443}
1444
1445STDMETHODIMP EventSourceAggregator::CreateAggregator(ComSafeArrayIn(IEventSource *, aSubordinates),
1446 IEventSource **aResult)
1447{
1448 return mSource->CreateAggregator(ComSafeArrayInArg(aSubordinates), aResult);
1449}
1450
1451STDMETHODIMP EventSourceAggregator::RegisterListener(IEventListener *aListener,
1452 ComSafeArrayIn(VBoxEventType_T, aInterested),
1453 BOOL aActive)
1454{
1455 CheckComArgNotNull(aListener);
1456 CheckComArgSafeArrayNotNull(aInterested);
1457
1458 AutoCaller autoCaller(this);
1459 if (FAILED(autoCaller.rc()))
1460 return autoCaller.rc();
1461
1462 HRESULT rc;
1463
1464 ComPtr<IEventListener> proxy;
1465 rc = createProxyListener(aListener, proxy.asOutParam());
1466 if (FAILED(rc))
1467 return rc;
1468
1469 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1470 for (EventSourceList::const_iterator it = mEventSources.begin(); it != mEventSources.end();
1471 ++it)
1472 {
1473 ComPtr<IEventSource> es = *it;
1474 /* Register active proxy listener on real event source */
1475 rc = es->RegisterListener(proxy, ComSafeArrayInArg(aInterested), TRUE);
1476 }
1477 /* And add real listener on our event source */
1478 rc = mSource->RegisterListener(aListener, ComSafeArrayInArg(aInterested), aActive);
1479
1480 rc = S_OK;
1481
1482 return rc;
1483}
1484
1485STDMETHODIMP EventSourceAggregator::UnregisterListener(IEventListener *aListener)
1486{
1487 CheckComArgNotNull(aListener);
1488
1489 AutoCaller autoCaller(this);
1490 if (FAILED(autoCaller.rc()))
1491 return autoCaller.rc();
1492
1493 HRESULT rc = S_OK;
1494
1495 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1496
1497 ComPtr<IEventListener> proxy;
1498 rc = getProxyListener(aListener, proxy.asOutParam());
1499 if (FAILED(rc))
1500 return rc;
1501
1502 for (EventSourceList::const_iterator it = mEventSources.begin(); it != mEventSources.end();
1503 ++it)
1504 {
1505 ComPtr<IEventSource> es = *it;
1506 rc = es->UnregisterListener(proxy);
1507 }
1508 rc = mSource->UnregisterListener(aListener);
1509
1510 return removeProxyListener(aListener);
1511
1512}
1513
1514STDMETHODIMP EventSourceAggregator::FireEvent(IEvent *aEvent,
1515 LONG aTimeout,
1516 BOOL *aProcessed)
1517{
1518 CheckComArgNotNull(aEvent);
1519 CheckComArgOutPointerValid(aProcessed);
1520
1521 AutoCaller autoCaller(this);
1522 if (FAILED(autoCaller.rc()))
1523 return autoCaller.rc();
1524
1525 HRESULT rc = S_OK;
1526 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1527 /* Aggregator event source shall not have direct event firing, but we may
1528 wish to support aggregation chains */
1529 for (EventSourceList::const_iterator it = mEventSources.begin(); it != mEventSources.end();
1530 ++it)
1531 {
1532 ComPtr<IEventSource> es = *it;
1533 rc = es->FireEvent(aEvent, aTimeout, aProcessed);
1534 /* Current behavior is that aggregator's FireEvent() always succeeds,
1535 so that multiple event sources don't affect each other. */
1536 NOREF(rc);
1537 }
1538
1539 return S_OK;
1540}
1541
1542STDMETHODIMP EventSourceAggregator::GetEvent(IEventListener *aListener,
1543 LONG aTimeout,
1544 IEvent **aEvent)
1545{
1546 return mSource->GetEvent(aListener, aTimeout, aEvent);
1547}
1548
1549STDMETHODIMP EventSourceAggregator::EventProcessed(IEventListener *aListener,
1550 IEvent *aEvent)
1551{
1552 return mSource->EventProcessed(aListener, aEvent);
1553}
1554
1555HRESULT EventSourceAggregator::createProxyListener(IEventListener *aListener,
1556 IEventListener **aProxy)
1557{
1558 ComObjPtr<ProxyEventListener> proxy;
1559
1560 HRESULT rc = proxy.createObject();
1561 ComAssertMsgRet(SUCCEEDED(rc), ("Could not create proxy (%Rhrc)", rc),
1562 E_FAIL);
1563
1564 rc = proxy->init(mSource);
1565 if (FAILED(rc))
1566 return rc;
1567
1568 ProxyListenerMap::const_iterator it = mListenerProxies.find(aListener);
1569 if (it != mListenerProxies.end())
1570 return setError(E_INVALIDARG,
1571 tr("This listener already registered"));
1572
1573 mListenerProxies.insert(ProxyListenerMap::value_type(aListener, proxy));
1574
1575 proxy.queryInterfaceTo(aProxy);
1576 return S_OK;
1577}
1578
1579HRESULT EventSourceAggregator::getProxyListener(IEventListener *aListener,
1580 IEventListener **aProxy)
1581{
1582 ProxyListenerMap::const_iterator it = mListenerProxies.find(aListener);
1583 if (it == mListenerProxies.end())
1584 return setError(E_INVALIDARG,
1585 tr("This listener never registered"));
1586
1587 (*it).second.queryInterfaceTo(aProxy);
1588 return S_OK;
1589}
1590
1591HRESULT EventSourceAggregator::removeProxyListener(IEventListener *aListener)
1592{
1593 ProxyListenerMap::iterator it = mListenerProxies.find(aListener);
1594 if (it == mListenerProxies.end())
1595 return setError(E_INVALIDARG,
1596 tr("This listener never registered"));
1597
1598 mListenerProxies.erase(it);
1599 return S_OK;
1600}
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