VirtualBox

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

Last change on this file since 43394 was 39714, checked in by vboxsync, 13 years ago

nits.

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