VirtualBox

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

Last change on this file since 34079 was 33540, checked in by vboxsync, 14 years ago

*: spelling fixes, thanks Timeless!

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