VirtualBox

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

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

Main/Event: eliminate unused parameter

  • 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 50544 2014-02-21 14:47:22Z 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
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)
415 delete this;
416 }
417
418 // Called when an element is no longer needed
419 void kill()
420 {
421 mAlive = false;
422 release();
423 }
424
425 ListenerRecord *ref()
426 {
427 return mAlive ? mRef : 0;
428 }
429
430 friend class EventMapList;
431};
432
433
434class EventMapList
435{
436 EventMapRecord *mHead;
437 uint32_t mSize;
438public:
439 EventMapList()
440 :
441 mHead(0),
442 mSize(0)
443 {}
444 ~EventMapList()
445 {
446 EventMapRecord *pCur = mHead;
447 while (pCur)
448 {
449 EventMapRecord *pNext = pCur->mNext;
450 pCur->release();
451 pCur = pNext;
452 }
453 }
454
455 /*
456 * Elements have to be added to the front of the list, to make sure
457 * that iterators doesn't see newly added listeners, and iteration
458 * will always complete.
459 */
460 void add(ListenerRecord *aRec)
461 {
462 EventMapRecord *pNew = new EventMapRecord(aRec);
463 pNew->mNext = mHead;
464 if (mHead)
465 mHead->mPrev = pNew;
466 mHead = pNew;
467 mSize++;
468 }
469
470 /*
471 * Mark element as removed, actual removal could be delayed until
472 * all consumers release it too. This helps to keep list stable
473 * enough for iterators to allow long and probably intrusive callbacks.
474 */
475 void remove(ListenerRecord *aRec)
476 {
477 EventMapRecord *pCur = mHead;
478 while (pCur)
479 {
480 EventMapRecord *aNext = pCur->mNext;
481 if (pCur->ref() == aRec)
482 {
483 if (pCur == mHead)
484 mHead = aNext;
485 pCur->kill();
486 mSize--;
487 // break?
488 }
489 pCur = aNext;
490 }
491 }
492
493 uint32_t size() const
494 {
495 return mSize;
496 }
497
498 struct iterator
499 {
500 EventMapRecord *mCur;
501
502 iterator() :
503 mCur(0)
504 {}
505
506 explicit
507 iterator(EventMapRecord *aCur) :
508 mCur(aCur)
509 {
510 // Prevent element removal, till we're at it
511 if (mCur)
512 mCur->addRef();
513 }
514
515 ~iterator()
516 {
517 if (mCur)
518 mCur->release();
519 }
520
521 ListenerRecord *
522 operator*() const
523 {
524 return mCur->ref();
525 }
526
527 EventMapList::iterator &
528 operator++()
529 {
530 EventMapRecord *pPrev = mCur;
531 do {
532 mCur = mCur->mNext;
533 } while (mCur && !mCur->mAlive);
534
535 // now we can safely release previous element
536 pPrev->release();
537
538 // And grab the new current
539 if (mCur)
540 mCur->addRef();
541
542 return *this;
543 }
544
545 bool
546 operator==(const EventMapList::iterator &aOther) const
547 {
548 return mCur == aOther.mCur;
549 }
550
551 bool
552 operator!=(const EventMapList::iterator &aOther) const
553 {
554 return mCur != aOther.mCur;
555 }
556 };
557
558 iterator begin()
559 {
560 return iterator(mHead);
561 }
562
563 iterator end()
564 {
565 return iterator(0);
566 }
567};
568
569typedef EventMapList EventMap[NumEvents];
570typedef std::map<IEvent *, int32_t> PendingEventsMap;
571typedef std::deque<ComPtr<IEvent> > PassiveQueue;
572
573class ListenerRecord
574{
575private:
576 ComPtr<IEventListener> mListener;
577 BOOL mActive;
578 EventSource *mOwner;
579
580 RTSEMEVENT mQEvent;
581 RTCRITSECT mcsQLock;
582 PassiveQueue mQueue;
583 int32_t volatile mRefCnt;
584 uint64_t mLastRead;
585
586public:
587 ListenerRecord(IEventListener *aListener,
588 com::SafeArray<VBoxEventType_T> &aInterested,
589 BOOL aActive,
590 EventSource *aOwner);
591 ~ListenerRecord();
592
593 HRESULT process(IEvent *aEvent, BOOL aWaitable, PendingEventsMap::iterator &pit, AutoLockBase &alock);
594 HRESULT enqueue(IEvent *aEvent);
595 HRESULT dequeue(IEvent **aEvent, LONG aTimeout, AutoLockBase &aAlock);
596 HRESULT eventProcessed(IEvent *aEvent, PendingEventsMap::iterator &pit);
597
598 void addRef()
599 {
600 ASMAtomicIncS32(&mRefCnt);
601 }
602
603 void release()
604 {
605 if (ASMAtomicDecS32(&mRefCnt) <= 0)
606 delete this;
607 }
608
609 BOOL isActive()
610 {
611 return mActive;
612 }
613
614 friend class EventSource;
615};
616
617/* Handy class with semantics close to ComPtr, but for list records */
618template<typename Held>
619class RecordHolder
620{
621public:
622 RecordHolder(Held *lr) :
623 held(lr)
624 {
625 addref();
626 }
627 RecordHolder(const RecordHolder &that) :
628 held(that.held)
629 {
630 addref();
631 }
632 RecordHolder()
633 :
634 held(0)
635 {
636 }
637 ~RecordHolder()
638 {
639 release();
640 }
641
642 Held *obj()
643 {
644 return held;
645 }
646
647 RecordHolder &operator=(const RecordHolder &that)
648 {
649 safe_assign(that.held);
650 return *this;
651 }
652private:
653 Held *held;
654
655 void addref()
656 {
657 if (held)
658 held->addRef();
659 }
660 void release()
661 {
662 if (held)
663 held->release();
664 }
665 void safe_assign(Held *that_p)
666 {
667 if (that_p)
668 that_p->addRef();
669 release();
670 held = that_p;
671 }
672};
673
674typedef std::map<IEventListener *, RecordHolder<ListenerRecord> > Listeners;
675
676struct EventSource::Data
677{
678 Data()
679 {}
680
681 Listeners mListeners;
682 EventMap mEvMap;
683 PendingEventsMap mPendingMap;
684};
685
686/**
687 * This function defines what wildcard expands to.
688 */
689static BOOL implies(VBoxEventType_T who, VBoxEventType_T what)
690{
691 switch (who)
692 {
693 case VBoxEventType_Any:
694 return TRUE;
695 case VBoxEventType_Vetoable:
696 return (what == VBoxEventType_OnExtraDataCanChange)
697 || (what == VBoxEventType_OnCanShowWindow);
698 case VBoxEventType_MachineEvent:
699 return (what == VBoxEventType_OnMachineStateChanged)
700 || (what == VBoxEventType_OnMachineDataChanged)
701 || (what == VBoxEventType_OnMachineRegistered)
702 || (what == VBoxEventType_OnSessionStateChanged)
703 || (what == VBoxEventType_OnGuestPropertyChanged);
704 case VBoxEventType_SnapshotEvent:
705 return (what == VBoxEventType_OnSnapshotTaken)
706 || (what == VBoxEventType_OnSnapshotDeleted)
707 || (what == VBoxEventType_OnSnapshotChanged) ;
708 case VBoxEventType_InputEvent:
709 return (what == VBoxEventType_OnKeyboardLedsChanged)
710 || (what == VBoxEventType_OnMousePointerShapeChanged)
711 || (what == VBoxEventType_OnMouseCapabilityChanged);
712 case VBoxEventType_Invalid:
713 return FALSE;
714 default:
715 break;
716 }
717
718 return who == what;
719}
720
721ListenerRecord::ListenerRecord(IEventListener *aListener,
722 com::SafeArray<VBoxEventType_T> &aInterested,
723 BOOL aActive,
724 EventSource *aOwner) :
725 mActive(aActive), mOwner(aOwner), mRefCnt(0)
726{
727 mListener = aListener;
728 EventMap *aEvMap = &aOwner->m->mEvMap;
729
730 for (size_t i = 0; i < aInterested.size(); ++i)
731 {
732 VBoxEventType_T interested = aInterested[i];
733 for (int j = FirstEvent; j < LastEvent; j++)
734 {
735 VBoxEventType_T candidate = (VBoxEventType_T)j;
736 if (implies(interested, candidate))
737 {
738 (*aEvMap)[j - FirstEvent].add(this);
739 }
740 }
741 }
742
743 if (!mActive)
744 {
745 ::RTCritSectInit(&mcsQLock);
746 ::RTSemEventCreate(&mQEvent);
747 mLastRead = RTTimeMilliTS();
748 }
749 else
750 {
751 mQEvent = NIL_RTSEMEVENT;
752 RT_ZERO(mcsQLock);
753 mLastRead = 0;
754 }
755}
756
757ListenerRecord::~ListenerRecord()
758{
759 /* Remove references to us from the event map */
760 EventMap *aEvMap = &mOwner->m->mEvMap;
761 for (int j = FirstEvent; j < LastEvent; j++)
762 {
763 (*aEvMap)[j - FirstEvent].remove(this);
764 }
765
766 if (!mActive)
767 {
768 // at this moment nobody could add elements to our queue, so we can safely
769 // clean it up, otherwise there will be pending events map elements
770 PendingEventsMap *aPem = &mOwner->m->mPendingMap;
771 while (true)
772 {
773 ComPtr<IEvent> aEvent;
774
775 if (mQueue.empty())
776 break;
777
778 mQueue.front().queryInterfaceTo(aEvent.asOutParam());
779 mQueue.pop_front();
780
781 BOOL aWaitable = FALSE;
782 aEvent->COMGETTER(Waitable)(&aWaitable);
783 if (aWaitable)
784 {
785 PendingEventsMap::iterator pit = aPem->find(aEvent);
786 if (pit != aPem->end())
787 eventProcessed(aEvent, pit);
788 }
789 }
790
791 ::RTCritSectDelete(&mcsQLock);
792 ::RTSemEventDestroy(mQEvent);
793 }
794}
795
796HRESULT ListenerRecord::process(IEvent *aEvent,
797 BOOL aWaitable,
798 PendingEventsMap::iterator &pit,
799 AutoLockBase &aAlock)
800{
801 if (mActive)
802 {
803 /*
804 * We release lock here to allow modifying ops on EventSource inside callback.
805 */
806 HRESULT rc = S_OK;
807 if (mListener)
808 {
809 aAlock.release();
810 rc = mListener->HandleEvent(aEvent);
811#ifdef RT_OS_WINDOWS
812 Assert(rc != RPC_E_WRONG_THREAD);
813#endif
814 aAlock.acquire();
815 }
816 if (aWaitable)
817 eventProcessed(aEvent, pit);
818 return rc;
819 }
820 return enqueue(aEvent);
821}
822
823
824HRESULT ListenerRecord::enqueue(IEvent *aEvent)
825{
826 AssertMsg(!mActive, ("must be passive\n"));
827
828 // put an event the queue
829 ::RTCritSectEnter(&mcsQLock);
830
831 // If there was no events reading from the listener for the long time,
832 // and events keep coming, or queue is oversized we shall unregister this listener.
833 uint64_t sinceRead = RTTimeMilliTS() - mLastRead;
834 size_t queueSize = mQueue.size();
835 if ((queueSize > 1000) || ((queueSize > 500) && (sinceRead > 60 * 1000)))
836 {
837 ::RTCritSectLeave(&mcsQLock);
838 return E_ABORT;
839 }
840
841
842 if (queueSize != 0 && mQueue.back() == aEvent)
843 /* if same event is being pushed multiple times - it's reusable event and
844 we don't really need multiple instances of it in the queue */
845 (void)aEvent;
846 else
847 mQueue.push_back(aEvent);
848
849 ::RTCritSectLeave(&mcsQLock);
850
851 // notify waiters
852 ::RTSemEventSignal(mQEvent);
853
854 return S_OK;
855}
856
857HRESULT ListenerRecord::dequeue(IEvent **aEvent,
858 LONG aTimeout,
859 AutoLockBase &aAlock)
860{
861 if (mActive)
862 return VBOX_E_INVALID_OBJECT_STATE;
863
864 // retain listener record
865 RecordHolder<ListenerRecord> holder(this);
866
867 ::RTCritSectEnter(&mcsQLock);
868
869 mLastRead = RTTimeMilliTS();
870
871 if (mQueue.empty())
872 {
873 ::RTCritSectLeave(&mcsQLock);
874 // Speed up common case
875 if (aTimeout == 0)
876 {
877 *aEvent = NULL;
878 return S_OK;
879 }
880 // release lock while waiting, listener will not go away due to above holder
881 aAlock.release();
882 ::RTSemEventWait(mQEvent, aTimeout);
883 // reacquire lock
884 aAlock.acquire();
885 ::RTCritSectEnter(&mcsQLock);
886 }
887 if (mQueue.empty())
888 {
889 *aEvent = NULL;
890 }
891 else
892 {
893 mQueue.front().queryInterfaceTo(aEvent);
894 mQueue.pop_front();
895 }
896 ::RTCritSectLeave(&mcsQLock);
897 return S_OK;
898}
899
900HRESULT ListenerRecord::eventProcessed(IEvent *aEvent, PendingEventsMap::iterator &pit)
901{
902 if (--pit->second == 0)
903 {
904 Assert(pit->first == aEvent);
905 aEvent->SetProcessed();
906 mOwner->m->mPendingMap.erase(pit);
907 }
908
909 return S_OK;
910}
911
912EventSource::EventSource()
913{}
914
915EventSource::~EventSource()
916{}
917
918HRESULT EventSource::FinalConstruct()
919{
920 m = new Data;
921 return BaseFinalConstruct();
922}
923
924void EventSource::FinalRelease()
925{
926 uninit();
927 delete m;
928 BaseFinalRelease();
929}
930
931HRESULT EventSource::init()
932{
933 HRESULT rc = S_OK;
934
935 AutoInitSpan autoInitSpan(this);
936 AssertReturn(autoInitSpan.isOk(), E_FAIL);
937
938 /* Confirm a successful initialization */
939 autoInitSpan.setSucceeded();
940 return rc;
941}
942
943void EventSource::uninit()
944{
945 AutoUninitSpan autoUninitSpan(this);
946 if (autoUninitSpan.uninitDone())
947 return;
948
949 m->mListeners.clear();
950 // m->mEvMap shall be cleared at this point too by destructors, assert?
951}
952
953STDMETHODIMP EventSource::RegisterListener(IEventListener *aListener,
954 ComSafeArrayIn(VBoxEventType_T, aInterested),
955 BOOL aActive)
956{
957 CheckComArgNotNull(aListener);
958 CheckComArgSafeArrayNotNull(aInterested);
959
960 AutoCaller autoCaller(this);
961 if (FAILED(autoCaller.rc()))
962 return autoCaller.rc();
963
964 {
965 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
966
967 Listeners::const_iterator it = m->mListeners.find(aListener);
968 if (it != m->mListeners.end())
969 return setError(E_INVALIDARG,
970 tr("This listener already registered"));
971
972 com::SafeArray<VBoxEventType_T> interested(ComSafeArrayInArg(aInterested));
973 RecordHolder<ListenerRecord> lrh(new ListenerRecord(aListener, interested, aActive, this));
974 m->mListeners.insert(Listeners::value_type(aListener, lrh));
975 }
976
977 VBoxEventDesc evDesc;
978 evDesc.init(this, VBoxEventType_OnEventSourceChanged, aListener, TRUE);
979 evDesc.fire(0);
980
981 return S_OK;
982}
983
984STDMETHODIMP EventSource::UnregisterListener(IEventListener *aListener)
985{
986 CheckComArgNotNull(aListener);
987
988 AutoCaller autoCaller(this);
989 if (FAILED(autoCaller.rc()))
990 return autoCaller.rc();
991
992 HRESULT rc;
993 {
994 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
995
996 Listeners::iterator it = m->mListeners.find(aListener);
997
998 if (it != m->mListeners.end())
999 {
1000 m->mListeners.erase(it);
1001 // destructor removes refs from the event map
1002 rc = S_OK;
1003 }
1004 else
1005 {
1006 rc = setError(VBOX_E_OBJECT_NOT_FOUND,
1007 tr("Listener was never registered"));
1008 }
1009 }
1010
1011 if (SUCCEEDED(rc))
1012 {
1013 VBoxEventDesc evDesc;
1014 evDesc.init(this, VBoxEventType_OnEventSourceChanged, aListener, FALSE);
1015 evDesc.fire(0);
1016 }
1017
1018 return rc;
1019}
1020
1021STDMETHODIMP EventSource::FireEvent(IEvent *aEvent,
1022 LONG aTimeout,
1023 BOOL *aProcessed)
1024{
1025 CheckComArgNotNull(aEvent);
1026 CheckComArgOutPointerValid(aProcessed);
1027
1028 AutoCaller autoCaller(this);
1029 if (FAILED(autoCaller.rc()))
1030 return autoCaller.rc();
1031
1032 HRESULT hrc;
1033 BOOL aWaitable = FALSE;
1034 aEvent->COMGETTER(Waitable)(&aWaitable);
1035
1036 do {
1037 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1038
1039 VBoxEventType_T evType;
1040 hrc = aEvent->COMGETTER(Type)(&evType);
1041 AssertComRCReturn(hrc, hrc);
1042
1043 EventMapList &listeners = m->mEvMap[(int)evType - FirstEvent];
1044
1045 /* Anyone interested in this event? */
1046 uint32_t cListeners = listeners.size();
1047 if (cListeners == 0)
1048 {
1049 aEvent->SetProcessed();
1050 break; // just leave the lock and update event object state
1051 }
1052
1053 PendingEventsMap::iterator pit;
1054
1055 if (aWaitable)
1056 {
1057 m->mPendingMap.insert(PendingEventsMap::value_type(aEvent, cListeners));
1058 // we keep iterator here to allow processing active listeners without
1059 // pending events lookup
1060 pit = m->mPendingMap.find(aEvent);
1061 }
1062 for (EventMapList::iterator it = listeners.begin();
1063 it != listeners.end();
1064 ++it)
1065 {
1066 HRESULT cbRc;
1067 // keep listener record reference, in case someone will remove it while in callback
1068 RecordHolder<ListenerRecord> record(*it);
1069
1070 /*
1071 * We pass lock here to allow modifying ops on EventSource inside callback
1072 * in active mode. Note that we expect list iterator stability as 'alock'
1073 * could be temporary released when calling event handler.
1074 */
1075 cbRc = record.obj()->process(aEvent, aWaitable, pit, alock);
1076
1077 /* Note that E_ABORT is used above to signal that a passive
1078 * listener was unregistered due to not picking up its event.
1079 * This overlaps with XPCOM specific use of E_ABORT to signal
1080 * death of an active listener, but that's irrelevant here. */
1081 if (FAILED_DEAD_INTERFACE(cbRc) || cbRc == E_ABORT)
1082 {
1083 Listeners::iterator lit = m->mListeners.find(record.obj()->mListener);
1084 if (lit != m->mListeners.end())
1085 m->mListeners.erase(lit);
1086 }
1087 // anything else to do with cbRc?
1088 }
1089 } while (0);
1090 /* We leave the lock here */
1091
1092 if (aWaitable)
1093 hrc = aEvent->WaitProcessed(aTimeout, aProcessed);
1094 else
1095 *aProcessed = TRUE;
1096
1097 return hrc;
1098}
1099
1100
1101STDMETHODIMP EventSource::GetEvent(IEventListener *aListener,
1102 LONG aTimeout,
1103 IEvent **aEvent)
1104{
1105
1106 CheckComArgNotNull(aListener);
1107
1108 AutoCaller autoCaller(this);
1109 if (FAILED(autoCaller.rc()))
1110 return autoCaller.rc();
1111
1112 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1113
1114 Listeners::iterator it = m->mListeners.find(aListener);
1115 HRESULT rc;
1116
1117 if (it != m->mListeners.end())
1118 rc = it->second.obj()->dequeue(aEvent, aTimeout, alock);
1119 else
1120 rc = setError(VBOX_E_OBJECT_NOT_FOUND,
1121 tr("Listener was never registered"));
1122
1123 if (rc == VBOX_E_INVALID_OBJECT_STATE)
1124 return setError(rc, tr("Listener must be passive"));
1125
1126 return rc;
1127}
1128
1129STDMETHODIMP EventSource::EventProcessed(IEventListener *aListener,
1130 IEvent *aEvent)
1131{
1132 CheckComArgNotNull(aListener);
1133 CheckComArgNotNull(aEvent);
1134
1135 AutoCaller autoCaller(this);
1136 if (FAILED(autoCaller.rc()))
1137 return autoCaller.rc();
1138
1139 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1140
1141 Listeners::iterator it = m->mListeners.find(aListener);
1142 HRESULT rc;
1143
1144 BOOL aWaitable = FALSE;
1145 aEvent->COMGETTER(Waitable)(&aWaitable);
1146
1147 if (it != m->mListeners.end())
1148 {
1149 ListenerRecord *aRecord = it->second.obj();
1150
1151 if (aRecord->isActive())
1152 return setError(E_INVALIDARG,
1153 tr("Only applicable to passive listeners"));
1154
1155 if (aWaitable)
1156 {
1157 PendingEventsMap::iterator pit = m->mPendingMap.find(aEvent);
1158
1159 if (pit == m->mPendingMap.end())
1160 {
1161 AssertFailed();
1162 rc = setError(VBOX_E_OBJECT_NOT_FOUND,
1163 tr("Unknown event"));
1164 }
1165 else
1166 rc = aRecord->eventProcessed(aEvent, pit);
1167 }
1168 else
1169 {
1170 // for non-waitable events we're done
1171 rc = S_OK;
1172 }
1173 }
1174 else
1175 {
1176 rc = setError(VBOX_E_OBJECT_NOT_FOUND,
1177 tr("Listener was never registered"));
1178 }
1179
1180 return rc;
1181}
1182
1183/**
1184 * This class serves as feasible listener implementation
1185 * which could be used by clients not able to create local
1186 * COM objects, but still willing to receive event
1187 * notifications in passive mode, such as webservices.
1188 */
1189class ATL_NO_VTABLE PassiveEventListener :
1190 public VirtualBoxBase,
1191 VBOX_SCRIPTABLE_IMPL(IEventListener)
1192{
1193public:
1194
1195 VIRTUALBOXBASE_ADD_ERRORINFO_SUPPORT(PassiveEventListener, IEventListener)
1196
1197 DECLARE_NOT_AGGREGATABLE(PassiveEventListener)
1198
1199 DECLARE_PROTECT_FINAL_CONSTRUCT()
1200
1201 BEGIN_COM_MAP(PassiveEventListener)
1202 VBOX_DEFAULT_INTERFACE_ENTRIES(IEventListener)
1203 END_COM_MAP()
1204
1205 PassiveEventListener()
1206 {}
1207 ~PassiveEventListener()
1208 {}
1209
1210 HRESULT FinalConstruct()
1211 {
1212 return BaseFinalConstruct();
1213 }
1214 void FinalRelease()
1215 {
1216 BaseFinalRelease();
1217 }
1218
1219 // IEventListener methods
1220 STDMETHOD(HandleEvent)(IEvent *)
1221 {
1222 ComAssertMsgRet(false, ("HandleEvent() of wrapper shall never be called"),
1223 E_FAIL);
1224 }
1225};
1226
1227/* Proxy listener class, used to aggregate multiple event sources into one */
1228class ATL_NO_VTABLE ProxyEventListener :
1229 public VirtualBoxBase,
1230 VBOX_SCRIPTABLE_IMPL(IEventListener)
1231{
1232 ComPtr<IEventSource> mSource;
1233public:
1234
1235 VIRTUALBOXBASE_ADD_ERRORINFO_SUPPORT(ProxyEventListener, IEventListener)
1236
1237 DECLARE_NOT_AGGREGATABLE(ProxyEventListener)
1238
1239 DECLARE_PROTECT_FINAL_CONSTRUCT()
1240
1241 BEGIN_COM_MAP(ProxyEventListener)
1242 VBOX_DEFAULT_INTERFACE_ENTRIES(IEventListener)
1243 END_COM_MAP()
1244
1245 ProxyEventListener()
1246 {}
1247 ~ProxyEventListener()
1248 {}
1249
1250 HRESULT FinalConstruct()
1251 {
1252 return BaseFinalConstruct();
1253 }
1254 void FinalRelease()
1255 {
1256 BaseFinalRelease();
1257 }
1258
1259 HRESULT init(IEventSource *aSource)
1260 {
1261 mSource = aSource;
1262 return S_OK;
1263 }
1264
1265 // IEventListener methods
1266 STDMETHOD(HandleEvent)(IEvent *aEvent)
1267 {
1268 BOOL fProcessed = FALSE;
1269 if (mSource)
1270 return mSource->FireEvent(aEvent, 0, &fProcessed);
1271 else
1272 return S_OK;
1273 }
1274};
1275
1276class ATL_NO_VTABLE EventSourceAggregator :
1277 public VirtualBoxBase,
1278 VBOX_SCRIPTABLE_IMPL(IEventSource)
1279{
1280 typedef std::list <ComPtr<IEventSource> > EventSourceList;
1281 /* key is weak reference */
1282 typedef std::map<IEventListener *, ComPtr<IEventListener> > ProxyListenerMap;
1283
1284 EventSourceList mEventSources;
1285 ProxyListenerMap mListenerProxies;
1286 ComObjPtr<EventSource> mSource;
1287
1288public:
1289
1290 VIRTUALBOXBASE_ADD_ERRORINFO_SUPPORT(EventSourceAggregator, IEventSource)
1291
1292 DECLARE_NOT_AGGREGATABLE(EventSourceAggregator)
1293
1294 DECLARE_PROTECT_FINAL_CONSTRUCT()
1295
1296 BEGIN_COM_MAP(EventSourceAggregator)
1297 VBOX_DEFAULT_INTERFACE_ENTRIES(IEventSource)
1298 END_COM_MAP()
1299
1300 EventSourceAggregator()
1301 {}
1302 ~EventSourceAggregator()
1303 {}
1304
1305 HRESULT FinalConstruct()
1306 {
1307 return BaseFinalConstruct();
1308 }
1309 void FinalRelease()
1310 {
1311 mEventSources.clear();
1312 mListenerProxies.clear();
1313 mSource->uninit();
1314 BaseFinalRelease();
1315 }
1316
1317 // internal public
1318 HRESULT init(ComSafeArrayIn(IEventSource *, aSources));
1319
1320 // IEventSource methods
1321 STDMETHOD(CreateListener)(IEventListener **aListener);
1322 STDMETHOD(CreateAggregator)(ComSafeArrayIn(IEventSource *, aSubordinates),
1323 IEventSource **aAggregator);
1324 STDMETHOD(RegisterListener)(IEventListener *aListener,
1325 ComSafeArrayIn(VBoxEventType_T, aInterested),
1326 BOOL aActive);
1327 STDMETHOD(UnregisterListener)(IEventListener *aListener);
1328 STDMETHOD(FireEvent)(IEvent *aEvent,
1329 LONG aTimeout,
1330 BOOL *aProcessed);
1331 STDMETHOD(GetEvent)(IEventListener *aListener,
1332 LONG aTimeout,
1333 IEvent **aEvent);
1334 STDMETHOD(EventProcessed)(IEventListener *aListener,
1335 IEvent *aEvent);
1336
1337 protected:
1338 HRESULT createProxyListener(IEventListener *aListener,
1339 IEventListener **aProxy);
1340 HRESULT getProxyListener(IEventListener *aListener,
1341 IEventListener **aProxy);
1342 HRESULT removeProxyListener(IEventListener *aListener);
1343};
1344
1345#ifdef VBOX_WITH_XPCOM
1346NS_DECL_CLASSINFO(ProxyEventListener)
1347NS_IMPL_THREADSAFE_ISUPPORTS1_CI(ProxyEventListener, IEventListener)
1348NS_DECL_CLASSINFO(PassiveEventListener)
1349NS_IMPL_THREADSAFE_ISUPPORTS1_CI(PassiveEventListener, IEventListener)
1350NS_DECL_CLASSINFO(VBoxEvent)
1351NS_IMPL_THREADSAFE_ISUPPORTS1_CI(VBoxEvent, IEvent)
1352NS_DECL_CLASSINFO(VBoxVetoEvent)
1353NS_IMPL_ISUPPORTS_INHERITED1(VBoxVetoEvent, VBoxEvent, IVetoEvent)
1354NS_DECL_CLASSINFO(EventSource)
1355NS_IMPL_THREADSAFE_ISUPPORTS1_CI(EventSource, IEventSource)
1356NS_DECL_CLASSINFO(EventSourceAggregator)
1357NS_IMPL_THREADSAFE_ISUPPORTS1_CI(EventSourceAggregator, IEventSource)
1358#endif
1359
1360
1361STDMETHODIMP EventSource::CreateListener(IEventListener **aListener)
1362{
1363 CheckComArgOutPointerValid(aListener);
1364
1365 AutoCaller autoCaller(this);
1366 if (FAILED(autoCaller.rc()))
1367 return autoCaller.rc();
1368
1369 ComObjPtr<PassiveEventListener> listener;
1370
1371 HRESULT rc = listener.createObject();
1372 ComAssertMsgRet(SUCCEEDED(rc), ("Could not create wrapper object (%Rhrc)", rc),
1373 E_FAIL);
1374 listener.queryInterfaceTo(aListener);
1375 return S_OK;
1376}
1377
1378
1379STDMETHODIMP EventSource::CreateAggregator(ComSafeArrayIn(IEventSource *, aSubordinates),
1380 IEventSource **aResult)
1381{
1382 CheckComArgOutPointerValid(aResult);
1383
1384 AutoCaller autoCaller(this);
1385 if (FAILED(autoCaller.rc()))
1386 return autoCaller.rc();
1387
1388 ComObjPtr<EventSourceAggregator> agg;
1389
1390 HRESULT rc = agg.createObject();
1391 ComAssertMsgRet(SUCCEEDED(rc), ("Could not create aggregator (%Rhrc)", rc),
1392 E_FAIL);
1393
1394 rc = agg->init(ComSafeArrayInArg(aSubordinates));
1395 if (FAILED(rc))
1396 return rc;
1397
1398 agg.queryInterfaceTo(aResult);
1399 return S_OK;
1400}
1401
1402HRESULT EventSourceAggregator::init(ComSafeArrayIn(IEventSource *, aSourcesIn))
1403{
1404 HRESULT rc;
1405
1406 AutoInitSpan autoInitSpan(this);
1407 AssertReturn(autoInitSpan.isOk(), E_FAIL);
1408
1409 rc = mSource.createObject();
1410 ComAssertMsgRet(SUCCEEDED(rc), ("Could not create source (%Rhrc)", rc),
1411 E_FAIL);
1412 rc = mSource->init();
1413 ComAssertMsgRet(SUCCEEDED(rc), ("Could not init source (%Rhrc)", rc),
1414 E_FAIL);
1415
1416 com::SafeIfaceArray<IEventSource> aSources(ComSafeArrayInArg (aSourcesIn));
1417
1418 size_t cSize = aSources.size();
1419
1420 for (size_t i = 0; i < cSize; i++)
1421 {
1422 if (aSources[i] != NULL)
1423 mEventSources.push_back(aSources[i]);
1424 }
1425
1426 /* Confirm a successful initialization */
1427 autoInitSpan.setSucceeded();
1428
1429 return rc;
1430}
1431
1432STDMETHODIMP EventSourceAggregator::CreateListener(IEventListener **aListener)
1433{
1434 return mSource->CreateListener(aListener);
1435}
1436
1437STDMETHODIMP EventSourceAggregator::CreateAggregator(ComSafeArrayIn(IEventSource *, aSubordinates),
1438 IEventSource **aResult)
1439{
1440 return mSource->CreateAggregator(ComSafeArrayInArg(aSubordinates), aResult);
1441}
1442
1443STDMETHODIMP EventSourceAggregator::RegisterListener(IEventListener *aListener,
1444 ComSafeArrayIn(VBoxEventType_T, aInterested),
1445 BOOL aActive)
1446{
1447 CheckComArgNotNull(aListener);
1448 CheckComArgSafeArrayNotNull(aInterested);
1449
1450 AutoCaller autoCaller(this);
1451 if (FAILED(autoCaller.rc()))
1452 return autoCaller.rc();
1453
1454 HRESULT rc;
1455
1456 ComPtr<IEventListener> proxy;
1457 rc = createProxyListener(aListener, proxy.asOutParam());
1458 if (FAILED(rc))
1459 return rc;
1460
1461 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1462 for (EventSourceList::const_iterator it = mEventSources.begin(); it != mEventSources.end();
1463 ++it)
1464 {
1465 ComPtr<IEventSource> es = *it;
1466 /* Register active proxy listener on real event source */
1467 rc = es->RegisterListener(proxy, ComSafeArrayInArg(aInterested), TRUE);
1468 }
1469 /* And add real listener on our event source */
1470 rc = mSource->RegisterListener(aListener, ComSafeArrayInArg(aInterested), aActive);
1471
1472 rc = S_OK;
1473
1474 return rc;
1475}
1476
1477STDMETHODIMP EventSourceAggregator::UnregisterListener(IEventListener *aListener)
1478{
1479 CheckComArgNotNull(aListener);
1480
1481 AutoCaller autoCaller(this);
1482 if (FAILED(autoCaller.rc()))
1483 return autoCaller.rc();
1484
1485 HRESULT rc = S_OK;
1486
1487 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1488
1489 ComPtr<IEventListener> proxy;
1490 rc = getProxyListener(aListener, proxy.asOutParam());
1491 if (FAILED(rc))
1492 return rc;
1493
1494 for (EventSourceList::const_iterator it = mEventSources.begin(); it != mEventSources.end();
1495 ++it)
1496 {
1497 ComPtr<IEventSource> es = *it;
1498 rc = es->UnregisterListener(proxy);
1499 }
1500 rc = mSource->UnregisterListener(aListener);
1501
1502 return removeProxyListener(aListener);
1503
1504}
1505
1506STDMETHODIMP EventSourceAggregator::FireEvent(IEvent *aEvent,
1507 LONG aTimeout,
1508 BOOL *aProcessed)
1509{
1510 CheckComArgNotNull(aEvent);
1511 CheckComArgOutPointerValid(aProcessed);
1512
1513 AutoCaller autoCaller(this);
1514 if (FAILED(autoCaller.rc()))
1515 return autoCaller.rc();
1516
1517 HRESULT rc = S_OK;
1518 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1519 /* Aggregator event source shall not have direct event firing, but we may
1520 wish to support aggregation chains */
1521 for (EventSourceList::const_iterator it = mEventSources.begin(); it != mEventSources.end();
1522 ++it)
1523 {
1524 ComPtr<IEventSource> es = *it;
1525 rc = es->FireEvent(aEvent, aTimeout, aProcessed);
1526 /* Current behavior is that aggregator's FireEvent() always succeeds,
1527 so that multiple event sources don't affect each other. */
1528 NOREF(rc);
1529 }
1530
1531 return S_OK;
1532}
1533
1534STDMETHODIMP EventSourceAggregator::GetEvent(IEventListener *aListener,
1535 LONG aTimeout,
1536 IEvent **aEvent)
1537{
1538 return mSource->GetEvent(aListener, aTimeout, aEvent);
1539}
1540
1541STDMETHODIMP EventSourceAggregator::EventProcessed(IEventListener *aListener,
1542 IEvent *aEvent)
1543{
1544 return mSource->EventProcessed(aListener, aEvent);
1545}
1546
1547HRESULT EventSourceAggregator::createProxyListener(IEventListener *aListener,
1548 IEventListener **aProxy)
1549{
1550 ComObjPtr<ProxyEventListener> proxy;
1551
1552 HRESULT rc = proxy.createObject();
1553 ComAssertMsgRet(SUCCEEDED(rc), ("Could not create proxy (%Rhrc)", rc),
1554 E_FAIL);
1555
1556 rc = proxy->init(mSource);
1557 if (FAILED(rc))
1558 return rc;
1559
1560 ProxyListenerMap::const_iterator it = mListenerProxies.find(aListener);
1561 if (it != mListenerProxies.end())
1562 return setError(E_INVALIDARG,
1563 tr("This listener already registered"));
1564
1565 mListenerProxies.insert(ProxyListenerMap::value_type(aListener, proxy));
1566
1567 proxy.queryInterfaceTo(aProxy);
1568 return S_OK;
1569}
1570
1571HRESULT EventSourceAggregator::getProxyListener(IEventListener *aListener,
1572 IEventListener **aProxy)
1573{
1574 ProxyListenerMap::const_iterator it = mListenerProxies.find(aListener);
1575 if (it == mListenerProxies.end())
1576 return setError(E_INVALIDARG,
1577 tr("This listener never registered"));
1578
1579 (*it).second.queryInterfaceTo(aProxy);
1580 return S_OK;
1581}
1582
1583HRESULT EventSourceAggregator::removeProxyListener(IEventListener *aListener)
1584{
1585 ProxyListenerMap::iterator it = mListenerProxies.find(aListener);
1586 if (it == mListenerProxies.end())
1587 return setError(E_INVALIDARG,
1588 tr("This listener never registered"));
1589
1590 mListenerProxies.erase(it);
1591 return S_OK;
1592}
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