VirtualBox

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

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

Main: support simple reusable events coalescing

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette