VirtualBox

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

Last change on this file since 44824 was 43898, checked in by vboxsync, 12 years ago

Missing return value.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 41.5 KB
Line 
1/* $Id: EventImpl.cpp 43898 2012-11-16 14:14:48Z 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 return FALSE; /* Never reached. */
713}
714
715ListenerRecord::ListenerRecord(IEventListener* aListener,
716 com::SafeArray<VBoxEventType_T>& aInterested,
717 BOOL aActive,
718 EventSource* aOwner)
719 :
720 mActive(aActive),
721 mOwner(aOwner),
722 mRefCnt(0)
723{
724 mListener = aListener;
725 EventMap* aEvMap = &aOwner->m->mEvMap;
726
727 for (size_t i = 0; i < aInterested.size(); ++i)
728 {
729 VBoxEventType_T interested = aInterested[i];
730 for (int j = FirstEvent; j < LastEvent; j++)
731 {
732 VBoxEventType_T candidate = (VBoxEventType_T)j;
733 if (implies(interested, candidate))
734 {
735 (*aEvMap)[j - FirstEvent].add(this);
736 }
737 }
738 }
739
740 if (!mActive)
741 {
742 ::RTCritSectInit(&mcsQLock);
743 ::RTSemEventCreate (&mQEvent);
744 mLastRead = RTTimeMilliTS();
745 }
746 else
747 {
748 mQEvent =NIL_RTSEMEVENT;
749 RT_ZERO(mcsQLock);
750 mLastRead = 0;
751 }
752}
753
754ListenerRecord::~ListenerRecord()
755{
756 /* Remove references to us from the event map */
757 EventMap* aEvMap = &mOwner->m->mEvMap;
758 for (int j = FirstEvent; j < LastEvent; j++)
759 {
760 (*aEvMap)[j - FirstEvent].remove(this);
761 }
762
763 if (!mActive)
764 {
765 // at this moment nobody could add elements to our queue, so we can safely
766 // clean it up, otherwise there will be pending events map elements
767 PendingEventsMap* aPem = &mOwner->m->mPendingMap;
768 while (true)
769 {
770 ComPtr<IEvent> aEvent;
771
772 if (mQueue.empty())
773 break;
774
775 mQueue.front().queryInterfaceTo(aEvent.asOutParam());
776 mQueue.pop_front();
777
778 BOOL aWaitable = FALSE;
779 aEvent->COMGETTER(Waitable)(&aWaitable);
780 if (aWaitable)
781 {
782 PendingEventsMap::iterator pit = aPem->find(aEvent);
783 if (pit != aPem->end())
784 eventProcessed(aEvent, pit);
785 }
786 }
787
788 ::RTCritSectDelete(&mcsQLock);
789 ::RTSemEventDestroy(mQEvent);
790 }
791}
792
793HRESULT ListenerRecord::process(IEvent* aEvent,
794 BOOL aWaitable,
795 PendingEventsMap::iterator& pit,
796 AutoLockBase& aAlock)
797{
798 if (mActive)
799 {
800 /*
801 * We release lock here to allow modifying ops on EventSource inside callback.
802 */
803 HRESULT rc = S_OK;
804 if (mListener)
805 {
806 aAlock.release();
807 rc = mListener->HandleEvent(aEvent);
808#ifdef RT_OS_WINDOWS
809 Assert(rc != RPC_E_WRONG_THREAD);
810#endif
811 aAlock.acquire();
812 }
813 if (aWaitable)
814 eventProcessed(aEvent, pit);
815 return rc;
816 }
817 return enqueue(aEvent);
818}
819
820
821HRESULT ListenerRecord::enqueue (IEvent* aEvent)
822{
823 AssertMsg(!mActive, ("must be passive\n"));
824
825 // put an event the queue
826 ::RTCritSectEnter(&mcsQLock);
827
828 // If there was no events reading from the listener for the long time,
829 // and events keep coming, or queue is oversized we shall unregister this listener.
830 uint64_t sinceRead = RTTimeMilliTS() - mLastRead;
831 size_t queueSize = mQueue.size();
832 if ( (queueSize > 1000) || ((queueSize > 500) && (sinceRead > 60 * 1000)))
833 {
834 ::RTCritSectLeave(&mcsQLock);
835 return E_ABORT;
836 }
837
838
839 if (queueSize != 0 && mQueue.back() == aEvent)
840 /* if same event is being pushed multiple times - it's reusable event and
841 we don't really need multiple instances of it in the queue */
842 (void)aEvent;
843 else
844 mQueue.push_back(aEvent);
845
846 ::RTCritSectLeave(&mcsQLock);
847
848 // notify waiters
849 ::RTSemEventSignal(mQEvent);
850
851 return S_OK;
852}
853
854HRESULT ListenerRecord::dequeue (IEvent* *aEvent,
855 LONG aTimeout,
856 AutoLockBase& aAlock)
857{
858 if (mActive)
859 return VBOX_E_INVALID_OBJECT_STATE;
860
861 // retain listener record
862 RecordHolder<ListenerRecord> holder(this);
863
864 ::RTCritSectEnter(&mcsQLock);
865
866 mLastRead = RTTimeMilliTS();
867
868 if (mQueue.empty()) {
869 ::RTCritSectLeave(&mcsQLock);
870 // Speed up common case
871 if (aTimeout == 0)
872 {
873 *aEvent = NULL;
874 return S_OK;
875 }
876 // release lock while waiting, listener will not go away due to above holder
877 aAlock.release();
878 ::RTSemEventWait(mQEvent, aTimeout);
879 // reacquire lock
880 aAlock.acquire();
881 ::RTCritSectEnter(&mcsQLock);
882 }
883 if (mQueue.empty())
884 {
885 *aEvent = NULL;
886 }
887 else
888 {
889 mQueue.front().queryInterfaceTo(aEvent);
890 mQueue.pop_front();
891 }
892 ::RTCritSectLeave(&mcsQLock);
893 return S_OK;
894}
895
896HRESULT ListenerRecord::eventProcessed (IEvent* aEvent, PendingEventsMap::iterator& pit)
897{
898 if (--pit->second == 0)
899 {
900 Assert(pit->first == aEvent);
901 aEvent->SetProcessed();
902 mOwner->m->mPendingMap.erase(pit);
903 }
904
905 return S_OK;
906}
907
908EventSource::EventSource()
909{}
910
911EventSource::~EventSource()
912{}
913
914HRESULT EventSource::FinalConstruct()
915{
916 m = new Data;
917 return BaseFinalConstruct();
918}
919
920void EventSource::FinalRelease()
921{
922 uninit();
923 delete m;
924 BaseFinalRelease();
925}
926
927HRESULT EventSource::init(IUnknown *)
928{
929 HRESULT rc = S_OK;
930
931 AutoInitSpan autoInitSpan(this);
932 AssertReturn(autoInitSpan.isOk(), E_FAIL);
933
934 /* Confirm a successful initialization */
935 autoInitSpan.setSucceeded();
936 return rc;
937}
938
939void EventSource::uninit()
940{
941 AutoUninitSpan autoUninitSpan(this);
942 if (autoUninitSpan.uninitDone())
943 return;
944 m->mListeners.clear();
945 // m->mEvMap shall be cleared at this point too by destructors, assert?
946}
947
948STDMETHODIMP EventSource::RegisterListener(IEventListener * aListener,
949 ComSafeArrayIn(VBoxEventType_T, aInterested),
950 BOOL aActive)
951{
952 CheckComArgNotNull(aListener);
953 CheckComArgSafeArrayNotNull(aInterested);
954
955 AutoCaller autoCaller(this);
956 if (FAILED(autoCaller.rc())) return autoCaller.rc();
957
958 {
959 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
960
961 Listeners::const_iterator it = m->mListeners.find(aListener);
962 if (it != m->mListeners.end())
963 return setError(E_INVALIDARG,
964 tr("This listener already registered"));
965
966 com::SafeArray<VBoxEventType_T> interested(ComSafeArrayInArg (aInterested));
967 RecordHolder<ListenerRecord> lrh(new ListenerRecord(aListener, interested, aActive, this));
968 m->mListeners.insert(Listeners::value_type(aListener, lrh));
969 }
970
971 VBoxEventDesc evDesc;
972 evDesc.init(this, VBoxEventType_OnEventSourceChanged, aListener, TRUE);
973 evDesc.fire(0);
974
975 return S_OK;
976}
977
978STDMETHODIMP EventSource::UnregisterListener(IEventListener * aListener)
979{
980 CheckComArgNotNull(aListener);
981
982 AutoCaller autoCaller(this);
983 if (FAILED(autoCaller.rc())) 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())) return autoCaller.rc();
1023
1024 HRESULT hrc;
1025 BOOL aWaitable = FALSE;
1026 aEvent->COMGETTER(Waitable)(&aWaitable);
1027
1028 do {
1029 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1030
1031 VBoxEventType_T evType;
1032 hrc = aEvent->COMGETTER(Type)(&evType);
1033 AssertComRCReturn(hrc, hrc);
1034
1035 EventMapList &listeners = m->mEvMap[(int)evType - FirstEvent];
1036
1037 /* Anyone interested in this event? */
1038 uint32_t cListeners = listeners.size();
1039 if (cListeners == 0)
1040 {
1041 aEvent->SetProcessed();
1042 break; // just leave the lock and update event object state
1043 }
1044
1045 PendingEventsMap::iterator pit;
1046
1047 if (aWaitable)
1048 {
1049 m->mPendingMap.insert(PendingEventsMap::value_type(aEvent, cListeners));
1050 // we keep iterator here to allow processing active listeners without
1051 // pending events lookup
1052 pit = m->mPendingMap.find(aEvent);
1053 }
1054 for (EventMapList::iterator it = listeners.begin();
1055 it != listeners.end();
1056 ++it)
1057 {
1058 HRESULT cbRc;
1059 // keep listener record reference, in case someone will remove it while in callback
1060 RecordHolder<ListenerRecord> record(*it);
1061
1062 /*
1063 * We pass lock here to allow modifying ops on EventSource inside callback
1064 * in active mode. Note that we expect list iterator stability as 'alock'
1065 * could be temporary released when calling event handler.
1066 */
1067 cbRc = record.obj()->process(aEvent, aWaitable, pit, alock);
1068
1069 /* Note that E_ABORT is used above to signal that a passive
1070 * listener was unregistered due to not picking up its event.
1071 * This overlaps with XPCOM specific use of E_ABORT to signal
1072 * death of an active listener, but that's irrelevant here. */
1073 if (FAILED_DEAD_INTERFACE(cbRc) || cbRc == E_ABORT)
1074 {
1075 Listeners::iterator lit = m->mListeners.find(record.obj()->mListener);
1076 if (lit != m->mListeners.end())
1077 m->mListeners.erase(lit);
1078 }
1079 // anything else to do with cbRc?
1080 }
1081 } while (0);
1082 /* We leave the lock here */
1083
1084 if (aWaitable)
1085 hrc = aEvent->WaitProcessed(aTimeout, aProcessed);
1086 else
1087 *aProcessed = TRUE;
1088
1089 return hrc;
1090}
1091
1092
1093STDMETHODIMP EventSource::GetEvent(IEventListener * aListener,
1094 LONG aTimeout,
1095 IEvent ** aEvent)
1096{
1097
1098 CheckComArgNotNull(aListener);
1099
1100 AutoCaller autoCaller(this);
1101 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1102
1103 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1104
1105 Listeners::iterator it = m->mListeners.find(aListener);
1106 HRESULT rc;
1107
1108 if (it != m->mListeners.end())
1109 rc = it->second.obj()->dequeue(aEvent, aTimeout, alock);
1110 else
1111 rc = setError(VBOX_E_OBJECT_NOT_FOUND,
1112 tr("Listener was never registered"));
1113
1114 if (rc == VBOX_E_INVALID_OBJECT_STATE)
1115 return setError(rc, tr("Listener must be passive"));
1116
1117 return rc;
1118}
1119
1120STDMETHODIMP EventSource::EventProcessed(IEventListener * aListener,
1121 IEvent * aEvent)
1122{
1123 CheckComArgNotNull(aListener);
1124 CheckComArgNotNull(aEvent);
1125
1126 AutoCaller autoCaller(this);
1127 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1128
1129 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1130
1131 Listeners::iterator it = m->mListeners.find(aListener);
1132 HRESULT rc;
1133
1134 BOOL aWaitable = FALSE;
1135 aEvent->COMGETTER(Waitable)(&aWaitable);
1136
1137 if (it != m->mListeners.end())
1138 {
1139 ListenerRecord* aRecord = it->second.obj();
1140
1141 if (aRecord->isActive())
1142 return setError(E_INVALIDARG,
1143 tr("Only applicable to passive listeners"));
1144
1145 if (aWaitable)
1146 {
1147 PendingEventsMap::iterator pit = m->mPendingMap.find(aEvent);
1148
1149 if (pit == m->mPendingMap.end())
1150 {
1151 AssertFailed();
1152 rc = setError(VBOX_E_OBJECT_NOT_FOUND,
1153 tr("Unknown event"));
1154 }
1155 else
1156 rc = aRecord->eventProcessed(aEvent, pit);
1157 }
1158 else
1159 {
1160 // for non-waitable events we're done
1161 rc = S_OK;
1162 }
1163 }
1164 else
1165 {
1166 rc = setError(VBOX_E_OBJECT_NOT_FOUND,
1167 tr("Listener was never registered"));
1168 }
1169
1170 return rc;
1171}
1172
1173/**
1174 * This class serves as feasible listener implementation
1175 * which could be used by clients not able to create local
1176 * COM objects, but still willing to receive event
1177 * notifications in passive mode, such as webservices.
1178 */
1179class ATL_NO_VTABLE PassiveEventListener :
1180 public VirtualBoxBase,
1181 VBOX_SCRIPTABLE_IMPL(IEventListener)
1182{
1183public:
1184
1185 VIRTUALBOXBASE_ADD_ERRORINFO_SUPPORT(PassiveEventListener, IEventListener)
1186
1187 DECLARE_NOT_AGGREGATABLE(PassiveEventListener)
1188
1189 DECLARE_PROTECT_FINAL_CONSTRUCT()
1190
1191 BEGIN_COM_MAP(PassiveEventListener)
1192 VBOX_DEFAULT_INTERFACE_ENTRIES(IEventListener)
1193 END_COM_MAP()
1194
1195 PassiveEventListener()
1196 {}
1197 ~PassiveEventListener()
1198 {}
1199
1200 HRESULT FinalConstruct()
1201 {
1202 return BaseFinalConstruct();
1203 }
1204 void FinalRelease()
1205 {
1206 BaseFinalRelease();
1207 }
1208
1209 // IEventListener methods
1210 STDMETHOD(HandleEvent)(IEvent *)
1211 {
1212 ComAssertMsgRet(false, ("HandleEvent() of wrapper shall never be called"),
1213 E_FAIL);
1214 }
1215};
1216
1217/* Proxy listener class, used to aggregate multiple event sources into one */
1218class ATL_NO_VTABLE ProxyEventListener :
1219 public VirtualBoxBase,
1220 VBOX_SCRIPTABLE_IMPL(IEventListener)
1221{
1222 ComPtr<IEventSource> mSource;
1223public:
1224
1225 VIRTUALBOXBASE_ADD_ERRORINFO_SUPPORT(ProxyEventListener, IEventListener)
1226
1227 DECLARE_NOT_AGGREGATABLE(ProxyEventListener)
1228
1229 DECLARE_PROTECT_FINAL_CONSTRUCT()
1230
1231 BEGIN_COM_MAP(ProxyEventListener)
1232 VBOX_DEFAULT_INTERFACE_ENTRIES(IEventListener)
1233 END_COM_MAP()
1234
1235 ProxyEventListener()
1236 {}
1237 ~ProxyEventListener()
1238 {}
1239
1240 HRESULT FinalConstruct()
1241 {
1242 return BaseFinalConstruct();
1243 }
1244 void FinalRelease()
1245 {
1246 BaseFinalRelease();
1247 }
1248
1249 HRESULT init(IEventSource* aSource)
1250 {
1251 mSource = aSource;
1252 return S_OK;
1253 }
1254
1255 // IEventListener methods
1256 STDMETHOD(HandleEvent)(IEvent * aEvent)
1257 {
1258 BOOL fProcessed = FALSE;
1259 if (mSource)
1260 return mSource->FireEvent(aEvent, 0, &fProcessed);
1261 else
1262 return S_OK;
1263 }
1264};
1265
1266class ATL_NO_VTABLE EventSourceAggregator :
1267 public VirtualBoxBase,
1268 VBOX_SCRIPTABLE_IMPL(IEventSource)
1269{
1270 typedef std::list <ComPtr<IEventSource> > EventSourceList;
1271 /* key is weak reference */
1272 typedef std::map<IEventListener*, ComPtr<IEventListener> > ProxyListenerMap;
1273
1274 EventSourceList mEventSources;
1275 ProxyListenerMap mListenerProxies;
1276 ComObjPtr<EventSource> mSource;
1277
1278public:
1279
1280 VIRTUALBOXBASE_ADD_ERRORINFO_SUPPORT(EventSourceAggregator, IEventSource)
1281
1282 DECLARE_NOT_AGGREGATABLE(EventSourceAggregator)
1283
1284 DECLARE_PROTECT_FINAL_CONSTRUCT()
1285
1286 BEGIN_COM_MAP(EventSourceAggregator)
1287 VBOX_DEFAULT_INTERFACE_ENTRIES(IEventSource)
1288 END_COM_MAP()
1289
1290 EventSourceAggregator()
1291 {}
1292 ~EventSourceAggregator()
1293 {}
1294
1295 HRESULT FinalConstruct()
1296 {
1297 return BaseFinalConstruct();
1298 }
1299 void FinalRelease()
1300 {
1301 mEventSources.clear();
1302 mListenerProxies.clear();
1303 mSource->uninit();
1304 BaseFinalRelease();
1305 }
1306
1307 // internal public
1308 HRESULT init(ComSafeArrayIn(IEventSource *, aSources));
1309
1310 // IEventSource methods
1311 STDMETHOD(CreateListener)(IEventListener ** aListener);
1312 STDMETHOD(CreateAggregator)(ComSafeArrayIn(IEventSource*, aSubordinates),
1313 IEventSource ** aAggregator);
1314 STDMETHOD(RegisterListener)(IEventListener * aListener,
1315 ComSafeArrayIn(VBoxEventType_T, aInterested),
1316 BOOL aActive);
1317 STDMETHOD(UnregisterListener)(IEventListener * aListener);
1318 STDMETHOD(FireEvent)(IEvent * aEvent,
1319 LONG aTimeout,
1320 BOOL *aProcessed);
1321 STDMETHOD(GetEvent)(IEventListener * aListener,
1322 LONG aTimeout,
1323 IEvent * *aEvent);
1324 STDMETHOD(EventProcessed)(IEventListener * aListener,
1325 IEvent * aEvent);
1326
1327 protected:
1328 HRESULT createProxyListener(IEventListener * aListener,
1329 IEventListener * *aProxy);
1330 HRESULT getProxyListener (IEventListener * aListener,
1331 IEventListener * *aProxy);
1332 HRESULT removeProxyListener(IEventListener * aListener);
1333};
1334
1335#ifdef VBOX_WITH_XPCOM
1336NS_DECL_CLASSINFO(ProxyEventListener)
1337NS_IMPL_THREADSAFE_ISUPPORTS1_CI(ProxyEventListener, IEventListener)
1338NS_DECL_CLASSINFO(PassiveEventListener)
1339NS_IMPL_THREADSAFE_ISUPPORTS1_CI(PassiveEventListener, IEventListener)
1340NS_DECL_CLASSINFO(VBoxEvent)
1341NS_IMPL_THREADSAFE_ISUPPORTS1_CI(VBoxEvent, IEvent)
1342NS_DECL_CLASSINFO(VBoxVetoEvent)
1343NS_IMPL_ISUPPORTS_INHERITED1(VBoxVetoEvent, VBoxEvent, IVetoEvent)
1344NS_DECL_CLASSINFO(EventSource)
1345NS_IMPL_THREADSAFE_ISUPPORTS1_CI(EventSource, IEventSource)
1346NS_DECL_CLASSINFO(EventSourceAggregator)
1347NS_IMPL_THREADSAFE_ISUPPORTS1_CI(EventSourceAggregator, IEventSource)
1348#endif
1349
1350
1351STDMETHODIMP EventSource::CreateListener(IEventListener ** aListener)
1352{
1353 CheckComArgOutPointerValid(aListener);
1354
1355 AutoCaller autoCaller(this);
1356 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1357
1358 ComObjPtr<PassiveEventListener> listener;
1359
1360 HRESULT rc = listener.createObject();
1361 ComAssertMsgRet(SUCCEEDED(rc), ("Could not create wrapper object (%Rrc)", rc),
1362 E_FAIL);
1363 listener.queryInterfaceTo(aListener);
1364 return S_OK;
1365}
1366
1367
1368STDMETHODIMP EventSource::CreateAggregator(ComSafeArrayIn(IEventSource*, aSubordinates),
1369 IEventSource ** aResult)
1370{
1371 CheckComArgOutPointerValid(aResult);
1372
1373 AutoCaller autoCaller(this);
1374 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1375
1376 ComObjPtr<EventSourceAggregator> agg;
1377
1378 HRESULT rc = agg.createObject();
1379 ComAssertMsgRet(SUCCEEDED(rc), ("Could not create aggregator (%Rrc)", rc),
1380 E_FAIL);
1381
1382 rc = agg->init(ComSafeArrayInArg(aSubordinates));
1383 if (FAILED(rc))
1384 return rc;
1385
1386
1387 agg.queryInterfaceTo(aResult);
1388 return S_OK;
1389}
1390
1391HRESULT EventSourceAggregator::init(ComSafeArrayIn(IEventSource*, aSourcesIn))
1392{
1393 HRESULT rc;
1394
1395 AutoInitSpan autoInitSpan(this);
1396 AssertReturn(autoInitSpan.isOk(), E_FAIL);
1397
1398 rc = mSource.createObject();
1399 ComAssertMsgRet(SUCCEEDED(rc), ("Could not create source (%Rrc)", rc),
1400 E_FAIL);
1401 rc = mSource->init((IEventSource*)this);
1402 ComAssertMsgRet(SUCCEEDED(rc), ("Could not init source (%Rrc)", rc),
1403 E_FAIL);
1404
1405 com::SafeIfaceArray<IEventSource> aSources(ComSafeArrayInArg (aSourcesIn));
1406
1407 size_t cSize = aSources.size();
1408
1409 for (size_t i = 0; i < cSize; i++)
1410 {
1411 if (aSources[i] != NULL)
1412 mEventSources.push_back(aSources[i]);
1413 }
1414
1415 /* Confirm a successful initialization */
1416 autoInitSpan.setSucceeded();
1417
1418 return rc;
1419}
1420
1421STDMETHODIMP EventSourceAggregator::CreateListener(IEventListener ** aListener)
1422{
1423 return mSource->CreateListener(aListener);
1424}
1425
1426STDMETHODIMP EventSourceAggregator::CreateAggregator(ComSafeArrayIn(IEventSource*, aSubordinates),
1427 IEventSource ** aResult)
1428{
1429 return mSource->CreateAggregator(ComSafeArrayInArg(aSubordinates), aResult);
1430}
1431
1432STDMETHODIMP EventSourceAggregator::RegisterListener(IEventListener * aListener,
1433 ComSafeArrayIn(VBoxEventType_T, aInterested),
1434 BOOL aActive)
1435{
1436 CheckComArgNotNull(aListener);
1437 CheckComArgSafeArrayNotNull(aInterested);
1438
1439 AutoCaller autoCaller(this);
1440 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1441
1442 HRESULT rc;
1443
1444 ComPtr<IEventListener> proxy;
1445 rc = createProxyListener(aListener, proxy.asOutParam());
1446 if (FAILED(rc))
1447 return rc;
1448
1449 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1450 for (EventSourceList::const_iterator it = mEventSources.begin(); it != mEventSources.end();
1451 ++it)
1452 {
1453 ComPtr<IEventSource> es = *it;
1454 /* Register active proxy listener on real event source */
1455 rc = es->RegisterListener(proxy, ComSafeArrayInArg(aInterested), TRUE);
1456 }
1457 /* And add real listener on our event source */
1458 rc = mSource->RegisterListener(aListener, ComSafeArrayInArg(aInterested), aActive);
1459
1460 rc = S_OK;
1461
1462 return rc;
1463}
1464
1465STDMETHODIMP EventSourceAggregator::UnregisterListener(IEventListener * aListener)
1466{
1467 CheckComArgNotNull(aListener);
1468
1469 AutoCaller autoCaller(this);
1470 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1471
1472 HRESULT rc = S_OK;
1473
1474 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1475
1476 ComPtr<IEventListener> proxy;
1477 rc = getProxyListener(aListener, proxy.asOutParam());
1478 if (FAILED(rc))
1479 return rc;
1480
1481 for (EventSourceList::const_iterator it = mEventSources.begin(); it != mEventSources.end();
1482 ++it)
1483 {
1484 ComPtr<IEventSource> es = *it;
1485 rc = es->UnregisterListener(proxy);
1486 }
1487 rc = mSource->UnregisterListener(aListener);
1488
1489 return removeProxyListener(aListener);
1490
1491}
1492
1493STDMETHODIMP EventSourceAggregator::FireEvent(IEvent * aEvent,
1494 LONG aTimeout,
1495 BOOL *aProcessed)
1496{
1497 CheckComArgNotNull(aEvent);
1498 CheckComArgOutPointerValid(aProcessed);
1499
1500 AutoCaller autoCaller(this);
1501 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1502
1503 HRESULT rc = S_OK;
1504 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1505 /* Aggresgator event source shalln't have direct event firing, but we may
1506 wish to support aggregation chains */
1507 for (EventSourceList::const_iterator it = mEventSources.begin(); it != mEventSources.end();
1508 ++it)
1509 {
1510 ComPtr<IEventSource> es = *it;
1511 rc = es->FireEvent(aEvent, aTimeout, aProcessed);
1512 /* Current behavior is that aggregator's FireEvent() always succeeds,
1513 so that multiple event sources don't affect each other. */
1514 NOREF(rc);
1515 }
1516
1517 return S_OK;
1518}
1519
1520STDMETHODIMP EventSourceAggregator::GetEvent(IEventListener * aListener,
1521 LONG aTimeout,
1522 IEvent ** aEvent)
1523{
1524 return mSource->GetEvent(aListener, aTimeout, aEvent);
1525}
1526
1527STDMETHODIMP EventSourceAggregator::EventProcessed(IEventListener * aListener,
1528 IEvent * aEvent)
1529{
1530 return mSource->EventProcessed(aListener, aEvent);
1531}
1532
1533HRESULT EventSourceAggregator::createProxyListener(IEventListener * aListener,
1534 IEventListener * *aProxy)
1535{
1536 ComObjPtr<ProxyEventListener> proxy;
1537
1538 HRESULT rc = proxy.createObject();
1539 ComAssertMsgRet(SUCCEEDED(rc), ("Could not create proxy (%Rrc)", rc),
1540 E_FAIL);
1541
1542 rc = proxy->init(mSource);
1543 if (FAILED(rc))
1544 return rc;
1545
1546 ProxyListenerMap::const_iterator it = mListenerProxies.find(aListener);
1547 if (it != mListenerProxies.end())
1548 return setError(E_INVALIDARG,
1549 tr("This listener already registered"));
1550
1551 mListenerProxies.insert(ProxyListenerMap::value_type(aListener, proxy));
1552
1553 proxy.queryInterfaceTo(aProxy);
1554 return S_OK;
1555}
1556
1557HRESULT EventSourceAggregator::getProxyListener(IEventListener * aListener,
1558 IEventListener * *aProxy)
1559{
1560 ProxyListenerMap::const_iterator it = mListenerProxies.find(aListener);
1561 if (it == mListenerProxies.end())
1562 return setError(E_INVALIDARG,
1563 tr("This listener never registered"));
1564
1565 (*it).second.queryInterfaceTo(aProxy);
1566 return S_OK;
1567}
1568
1569HRESULT EventSourceAggregator::removeProxyListener(IEventListener * aListener)
1570{
1571 ProxyListenerMap::iterator it = mListenerProxies.find(aListener);
1572 if (it == mListenerProxies.end())
1573 return setError(E_INVALIDARG,
1574 tr("This listener never registered"));
1575
1576 mListenerProxies.erase(it);
1577 return S_OK;
1578}
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