VirtualBox

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

Last change on this file since 50538 was 50538, checked in by vboxsync, 11 years ago

Main/EventImpl.cpp: coding style cleanup

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

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