VirtualBox

source: vbox/trunk/src/VBox/Main/EventImpl.cpp@ 35263

Last change on this file since 35263 was 34316, checked in by vboxsync, 14 years ago

Main/Event: comment the special use of E_ABORT

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

© 2023 Oracle
ContactPrivacy policyTerms of Use