VirtualBox

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

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

events: vacation-time idea of reusable events (for perfomance-critical operations)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.5 KB
Line 
1/* $Id: EventImpl.cpp 31572 2010-08-11 14:08:41Z 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 addded 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 uint32_t queueSize = mQueue.size();
827 if ( (queueSize > 200) || ((queueSize > 100) && (sinceRead > 60 * 1000)))
828 {
829 ::RTCritSectLeave(&mcsQLock);
830 return E_ABORT;
831 }
832
833 mQueue.push_back(aEvent);
834 ::RTCritSectLeave(&mcsQLock);
835
836 // notify waiters
837 ::RTSemEventSignal(mQEvent);
838
839 return S_OK;
840}
841
842HRESULT ListenerRecord::dequeue (IEvent* *aEvent,
843 LONG aTimeout,
844 AutoLockBase& aAlock)
845{
846 AssertMsg(!mActive, ("must be passive\n"));
847
848 // retain listener record
849 RecordHolder<ListenerRecord> holder(this);
850
851 ::RTCritSectEnter(&mcsQLock);
852
853 mLastRead = RTTimeMilliTS();
854
855 if (mQueue.empty()) {
856 ::RTCritSectLeave(&mcsQLock);
857 // Speed up common case
858 if (aTimeout == 0)
859 {
860 *aEvent = NULL;
861 return S_OK;
862 }
863 // release lock while waiting, listener will not go away due to above holder
864 aAlock.release();
865 ::RTSemEventWait(mQEvent, aTimeout);
866 // reacquire lock
867 aAlock.acquire();
868 ::RTCritSectEnter(&mcsQLock);
869 }
870 if (mQueue.empty())
871 {
872 *aEvent = NULL;
873 }
874 else
875 {
876 mQueue.front().queryInterfaceTo(aEvent);
877 mQueue.pop_front();
878 }
879 ::RTCritSectLeave(&mcsQLock);
880 return S_OK;
881}
882
883HRESULT ListenerRecord::eventProcessed (IEvent* aEvent, PendingEventsMap::iterator& pit)
884{
885 if (--pit->second == 0)
886 {
887 Assert(pit->first == aEvent);
888 aEvent->SetProcessed();
889 mOwner->m->mPendingMap.erase(pit);
890 }
891
892 Assert(pit->second >= 0);
893 return S_OK;
894}
895
896EventSource::EventSource()
897{}
898
899EventSource::~EventSource()
900{}
901
902HRESULT EventSource::FinalConstruct()
903{
904 m = new Data;
905 return S_OK;
906}
907
908void EventSource::FinalRelease()
909{
910 uninit();
911 delete m;
912}
913
914HRESULT EventSource::init(IUnknown *)
915{
916 HRESULT rc = S_OK;
917
918 AutoInitSpan autoInitSpan(this);
919 AssertReturn(autoInitSpan.isOk(), E_FAIL);
920
921 /* Confirm a successful initialization */
922 autoInitSpan.setSucceeded();
923 return rc;
924}
925
926void EventSource::uninit()
927{
928 AutoUninitSpan autoUninitSpan(this);
929 if (autoUninitSpan.uninitDone())
930 return;
931 m->mListeners.clear();
932 // m->mEvMap shall be cleared at this point too by destructors, assert?
933}
934
935STDMETHODIMP EventSource::RegisterListener(IEventListener * aListener,
936 ComSafeArrayIn(VBoxEventType_T, aInterested),
937 BOOL aActive)
938{
939 CheckComArgNotNull(aListener);
940 CheckComArgSafeArrayNotNull(aInterested);
941
942 AutoCaller autoCaller(this);
943 if (FAILED(autoCaller.rc())) return autoCaller.rc();
944
945 {
946 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
947
948 Listeners::const_iterator it = m->mListeners.find(aListener);
949 if (it != m->mListeners.end())
950 return setError(E_INVALIDARG,
951 tr("This listener already registered"));
952
953 com::SafeArray<VBoxEventType_T> interested(ComSafeArrayInArg (aInterested));
954 RecordHolder<ListenerRecord> lrh(new ListenerRecord(aListener, interested, aActive, this));
955 m->mListeners.insert(Listeners::value_type(aListener, lrh));
956 }
957
958 VBoxEventDesc evDesc;
959 evDesc.init(this, VBoxEventType_OnEventSourceChanged, aListener, TRUE);
960 evDesc.fire(0);
961
962 return S_OK;
963}
964
965STDMETHODIMP EventSource::UnregisterListener(IEventListener * aListener)
966{
967 CheckComArgNotNull(aListener);
968
969 AutoCaller autoCaller(this);
970 if (FAILED(autoCaller.rc())) return autoCaller.rc();
971
972 HRESULT rc;
973 {
974 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
975
976 Listeners::iterator it = m->mListeners.find(aListener);
977
978 if (it != m->mListeners.end())
979 {
980 m->mListeners.erase(it);
981 // destructor removes refs from the event map
982 rc = S_OK;
983 }
984 else
985 {
986 rc = setError(VBOX_E_OBJECT_NOT_FOUND,
987 tr("Listener was never registered"));
988 }
989 }
990
991 if (SUCCEEDED(rc))
992 {
993 VBoxEventDesc evDesc;
994 evDesc.init(this, VBoxEventType_OnEventSourceChanged, aListener, FALSE);
995 evDesc.fire(0);
996 }
997
998 return rc;
999}
1000
1001STDMETHODIMP EventSource::FireEvent(IEvent * aEvent,
1002 LONG aTimeout,
1003 BOOL *aProcessed)
1004{
1005 CheckComArgNotNull(aEvent);
1006 CheckComArgOutPointerValid(aProcessed);
1007
1008 AutoCaller autoCaller(this);
1009 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1010
1011 HRESULT hrc;
1012 BOOL aWaitable = FALSE;
1013 aEvent->COMGETTER(Waitable)(&aWaitable);
1014
1015 do {
1016 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1017
1018 VBoxEventType_T evType;
1019 hrc = aEvent->COMGETTER(Type)(&evType);
1020 AssertComRCReturn(hrc, hrc);
1021
1022 EventMapList& listeners = m->mEvMap[(int)evType-FirstEvent];
1023
1024 /* Anyone interested in this event? */
1025 uint32_t cListeners = listeners.size();
1026 if (cListeners == 0)
1027 {
1028 aEvent->SetProcessed();
1029 break; // just leave the lock and update event object state
1030 }
1031
1032 PendingEventsMap::iterator pit;
1033
1034 if (aWaitable)
1035 {
1036 m->mPendingMap.insert(PendingEventsMap::value_type(aEvent, cListeners));
1037 // we keep iterator here to allow processing active listeners without
1038 // pending events lookup
1039 pit = m->mPendingMap.find(aEvent);
1040 }
1041 for(EventMapList::iterator it = listeners.begin();
1042 it != listeners.end(); ++it)
1043 {
1044 HRESULT cbRc;
1045 // keep listener record reference, in case someone will remove it while in callback
1046 RecordHolder<ListenerRecord> record(*it);
1047
1048 /**
1049 * We pass lock here to allow modifying ops on EventSource inside callback
1050 * in active mode. Note that we expect list iterator stability as 'alock'
1051 * could be temporary released when calling event handler.
1052 */
1053 cbRc = record.obj()->process(aEvent, aWaitable, pit, alock);
1054
1055 if (FAILED_DEAD_INTERFACE(cbRc) || (cbRc == E_ABORT))
1056 {
1057 Listeners::iterator lit = m->mListeners.find(record.obj()->mListener);
1058 if (lit != m->mListeners.end())
1059 m->mListeners.erase(lit);
1060 }
1061 // anything else to do with cbRc?
1062 }
1063 } while (0);
1064 /* We leave the lock here */
1065
1066 if (aWaitable)
1067 hrc = aEvent->WaitProcessed(aTimeout, aProcessed);
1068 else
1069 *aProcessed = TRUE;
1070
1071 return hrc;
1072}
1073
1074
1075STDMETHODIMP EventSource::GetEvent(IEventListener * aListener,
1076 LONG aTimeout,
1077 IEvent ** aEvent)
1078{
1079
1080 CheckComArgNotNull(aListener);
1081
1082 AutoCaller autoCaller(this);
1083 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1084
1085 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1086
1087 Listeners::iterator it = m->mListeners.find(aListener);
1088 HRESULT rc;
1089
1090 if (it != m->mListeners.end())
1091 rc = it->second.obj()->dequeue(aEvent, aTimeout, alock);
1092 else
1093 rc = setError(VBOX_E_OBJECT_NOT_FOUND,
1094 tr("Listener was never registered"));
1095
1096 return rc;
1097}
1098
1099STDMETHODIMP EventSource::EventProcessed(IEventListener * aListener,
1100 IEvent * aEvent)
1101{
1102 CheckComArgNotNull(aListener);
1103 CheckComArgNotNull(aEvent);
1104
1105 AutoCaller autoCaller(this);
1106 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1107
1108 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1109
1110 Listeners::iterator it = m->mListeners.find(aListener);
1111 HRESULT rc;
1112
1113 BOOL aWaitable = FALSE;
1114 aEvent->COMGETTER(Waitable)(&aWaitable);
1115
1116 if (it != m->mListeners.end())
1117 {
1118 ListenerRecord* aRecord = it->second.obj();
1119
1120 if (aRecord->isActive())
1121 return setError(E_INVALIDARG,
1122 tr("Only applicable to passive listeners"));
1123
1124 if (aWaitable)
1125 {
1126 PendingEventsMap::iterator pit = m->mPendingMap.find(aEvent);
1127
1128 if (pit == m->mPendingMap.end())
1129 {
1130 AssertFailed();
1131 rc = setError(VBOX_E_OBJECT_NOT_FOUND,
1132 tr("Unknown event"));
1133 }
1134 else
1135 rc = aRecord->eventProcessed(aEvent, pit);
1136 }
1137 else
1138 {
1139 // for non-waitable events we're done
1140 rc = S_OK;
1141 }
1142 }
1143 else
1144 {
1145 rc = setError(VBOX_E_OBJECT_NOT_FOUND,
1146 tr("Listener was never registered"));
1147 }
1148
1149 return rc;
1150}
1151
1152/**
1153 * This class serves as feasible listener implementation
1154 * which could be used by clients not able to create local
1155 * COM objects, but still willing to receive event
1156 * notifications in passive mode, such as webservices.
1157 */
1158class ATL_NO_VTABLE PassiveEventListener :
1159 public VirtualBoxBase,
1160 VBOX_SCRIPTABLE_IMPL(IEventListener)
1161{
1162public:
1163
1164 VIRTUALBOXBASE_ADD_ERRORINFO_SUPPORT(PassiveEventListener, IEventListener)
1165
1166 DECLARE_NOT_AGGREGATABLE(PassiveEventListener)
1167
1168 DECLARE_PROTECT_FINAL_CONSTRUCT()
1169
1170 BEGIN_COM_MAP(PassiveEventListener)
1171 COM_INTERFACE_ENTRY(ISupportErrorInfo)
1172 COM_INTERFACE_ENTRY(IEventListener)
1173 COM_INTERFACE_ENTRY(IDispatch)
1174 END_COM_MAP()
1175
1176 PassiveEventListener()
1177 {}
1178 ~PassiveEventListener()
1179 {}
1180
1181 HRESULT FinalConstruct()
1182 {
1183 return S_OK;
1184 }
1185 void FinalRelease()
1186 {}
1187
1188 // IEventListener methods
1189 STDMETHOD(HandleEvent)(IEvent *)
1190 {
1191 ComAssertMsgRet(false, ("HandleEvent() of wrapper shall never be called"),
1192 E_FAIL);
1193 }
1194};
1195
1196#ifdef VBOX_WITH_XPCOM
1197NS_DECL_CLASSINFO(PassiveEventListener)
1198NS_IMPL_THREADSAFE_ISUPPORTS1_CI(PassiveEventListener, IEventListener)
1199NS_DECL_CLASSINFO(VBoxEvent)
1200NS_IMPL_THREADSAFE_ISUPPORTS1_CI(VBoxEvent, IEvent)
1201NS_DECL_CLASSINFO(VBoxVetoEvent)
1202NS_IMPL_ISUPPORTS_INHERITED1(VBoxVetoEvent, VBoxEvent, IVetoEvent)
1203NS_DECL_CLASSINFO(EventSource)
1204NS_IMPL_THREADSAFE_ISUPPORTS1_CI(EventSource, IEventSource)
1205#endif
1206
1207STDMETHODIMP EventSource::CreateListener(IEventListener ** aListener)
1208{
1209 CheckComArgOutPointerValid(aListener);
1210
1211 AutoCaller autoCaller(this);
1212 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1213
1214 ComObjPtr<PassiveEventListener> listener;
1215
1216 HRESULT rc = listener.createObject();
1217 ComAssertMsgRet(SUCCEEDED(rc), ("Could not create wrapper object (%Rrc)", rc),
1218 E_FAIL);
1219 listener.queryInterfaceTo(aListener);
1220 return S_OK;
1221}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use