VirtualBox

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

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

Main. QT/FE: fix long standing COM issue

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