VirtualBox

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

Last change on this file since 30955 was 30913, checked in by vboxsync, 15 years ago

doxygen

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 29.7 KB
Line 
1/* $Id: EventImpl.cpp 30913 2010-07-19 15:44:35Z 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
64#include <VBox/com/array.h>
65
66class ListenerRecord;
67
68struct VBoxEvent::Data
69{
70 Data()
71 : mType(VBoxEventType_Invalid),
72 mWaitEvent(NIL_RTSEMEVENT),
73 mWaitable(FALSE),
74 mProcessed(FALSE)
75 {}
76
77 VBoxEventType_T mType;
78 RTSEMEVENT mWaitEvent;
79 BOOL mWaitable;
80 BOOL mProcessed;
81 ComPtr<IEventSource> mSource;
82};
83
84HRESULT VBoxEvent::FinalConstruct()
85{
86 m = new Data;
87 return S_OK;
88}
89
90void VBoxEvent::FinalRelease()
91{
92 if (m)
93 {
94 uninit();
95 delete m;
96 m = 0;
97 }
98}
99
100HRESULT VBoxEvent::init(IEventSource *aSource, VBoxEventType_T aType, BOOL aWaitable)
101{
102 HRESULT rc = S_OK;
103
104 AssertReturn(aSource != NULL, E_INVALIDARG);
105
106 AutoInitSpan autoInitSpan(this);
107 AssertReturn(autoInitSpan.isOk(), E_FAIL);
108
109 m->mSource = aSource;
110 m->mType = aType;
111 m->mWaitable = aWaitable;
112 m->mProcessed = !aWaitable;
113
114 do {
115 if (aWaitable)
116 {
117 int vrc = ::RTSemEventCreate(&m->mWaitEvent);
118
119 if (RT_FAILURE(vrc))
120 {
121 AssertFailed ();
122 return setError(E_FAIL,
123 tr("Internal error (%Rrc)"), vrc);
124 }
125 }
126 } while (0);
127
128 /* Confirm a successful initialization */
129 autoInitSpan.setSucceeded();
130
131 return rc;
132}
133
134void VBoxEvent::uninit()
135{
136 if (!m)
137 return;
138
139 m->mProcessed = TRUE;
140 m->mType = VBoxEventType_Invalid;
141 m->mSource.setNull();
142
143 if (m->mWaitEvent != NIL_RTSEMEVENT)
144 {
145 Assert(m->mWaitable);
146 ::RTSemEventDestroy(m->mWaitEvent);
147 m->mWaitEvent = NIL_RTSEMEVENT;
148 }
149}
150
151STDMETHODIMP VBoxEvent::COMGETTER(Type)(VBoxEventType_T *aType)
152{
153 CheckComArgNotNull(aType);
154
155 AutoCaller autoCaller(this);
156 if (FAILED(autoCaller.rc())) return autoCaller.rc();
157
158 // never changes till event alive, no locking?
159 *aType = m->mType;
160 return S_OK;
161}
162
163STDMETHODIMP VBoxEvent::COMGETTER(Source)(IEventSource* *aSource)
164{
165 CheckComArgOutPointerValid(aSource);
166
167 AutoCaller autoCaller(this);
168 if (FAILED(autoCaller.rc())) return autoCaller.rc();
169
170 m->mSource.queryInterfaceTo(aSource);
171 return S_OK;
172}
173
174STDMETHODIMP VBoxEvent::COMGETTER(Waitable)(BOOL *aWaitable)
175{
176 CheckComArgNotNull(aWaitable);
177
178 AutoCaller autoCaller(this);
179 if (FAILED(autoCaller.rc())) return autoCaller.rc();
180
181 // never changes till event alive, no locking?
182 *aWaitable = m->mWaitable;
183 return S_OK;
184}
185
186
187STDMETHODIMP VBoxEvent::SetProcessed()
188{
189 AutoCaller autoCaller(this);
190 if (FAILED(autoCaller.rc())) return autoCaller.rc();
191
192 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
193
194 if (m->mProcessed)
195 return S_OK;
196
197 m->mProcessed = TRUE;
198
199 // notify waiters
200 ::RTSemEventSignal(m->mWaitEvent);
201
202 return S_OK;
203}
204
205STDMETHODIMP VBoxEvent::WaitProcessed(LONG aTimeout, BOOL *aResult)
206{
207 CheckComArgNotNull(aResult);
208
209 AutoCaller autoCaller(this);
210 if (FAILED(autoCaller.rc())) return autoCaller.rc();
211
212 {
213 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
214
215 if (m->mProcessed)
216 {
217 *aResult = TRUE;
218 return S_OK;
219 }
220
221 if (aTimeout == 0)
222 {
223 *aResult = m->mProcessed;
224 return S_OK;
225 }
226 }
227
228 /* @todo: maybe while loop for spurious wakeups? */
229 int vrc = ::RTSemEventWait(m->mWaitEvent, aTimeout);
230 AssertMsg(RT_SUCCESS(vrc) || vrc == VERR_TIMEOUT || vrc == VERR_INTERRUPTED,
231 ("RTSemEventWait returned %Rrc\n", vrc));
232
233 if (RT_SUCCESS(vrc))
234 {
235 AssertMsg(m->mProcessed,
236 ("mProcessed must be set here\n"));
237 *aResult = m->mProcessed;
238 }
239 else
240 {
241 *aResult = FALSE;
242 }
243
244 return S_OK;
245}
246
247typedef std::list<Bstr> VetoList;
248struct VBoxVetoEvent::Data
249{
250 Data()
251 :
252 mVetoed(FALSE)
253 {}
254 BOOL mVetoed;
255 VetoList mVetoList;
256};
257
258HRESULT VBoxVetoEvent::FinalConstruct()
259{
260 VBoxEvent::FinalConstruct();
261 m = new Data;
262 return S_OK;
263}
264
265void VBoxVetoEvent::FinalRelease()
266{
267 if (m)
268 {
269 uninit();
270 delete m;
271 m = 0;
272 }
273 VBoxEvent::FinalRelease();
274}
275
276
277HRESULT VBoxVetoEvent::init(IEventSource *aSource, VBoxEventType_T aType)
278{
279 HRESULT rc = S_OK;
280 // all veto events are waitable
281 rc = VBoxEvent::init(aSource, aType, TRUE);
282 if (FAILED(rc)) return rc;
283
284 m->mVetoed = FALSE;
285 m->mVetoList.clear();
286
287 return rc;
288}
289
290void VBoxVetoEvent::uninit()
291{
292 VBoxEvent::uninit();
293 if (!m)
294 return;
295 m->mVetoed = FALSE;
296}
297
298STDMETHODIMP VBoxVetoEvent::AddVeto(IN_BSTR aVeto)
299{
300 AutoCaller autoCaller(this);
301 if (FAILED(autoCaller.rc())) return autoCaller.rc();
302
303 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
304
305 if (aVeto)
306 m->mVetoList.push_back(aVeto);
307
308 m->mVetoed = TRUE;
309
310 return S_OK;
311}
312
313STDMETHODIMP VBoxVetoEvent::IsVetoed(BOOL * aResult)
314{
315 CheckComArgOutPointerValid(aResult);
316
317 AutoCaller autoCaller(this);
318 if (FAILED(autoCaller.rc())) return autoCaller.rc();
319
320 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
321
322 *aResult = m->mVetoed;
323
324 return S_OK;
325}
326
327STDMETHODIMP VBoxVetoEvent::GetVetos(ComSafeArrayOut(BSTR, aVetos))
328{
329 if (ComSafeArrayOutIsNull(aVetos))
330 return E_POINTER;
331
332 AutoCaller autoCaller(this);
333 if (FAILED(autoCaller.rc())) return autoCaller.rc();
334
335 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
336 com::SafeArray<BSTR> vetos(m->mVetoList.size());
337 int i = 0;
338 for (VetoList::const_iterator it = m->mVetoList.begin();
339 it != m->mVetoList.end();
340 ++it, ++i)
341 {
342 const Bstr &str = *it;
343 str.cloneTo(&vetos[i]);
344 }
345 vetos.detachTo(ComSafeArrayOutArg(aVetos));
346
347 return S_OK;
348
349}
350
351static const int FirstEvent = (int)VBoxEventType_LastWildcard + 1;
352static const int LastEvent = (int)VBoxEventType_Last;
353static const int NumEvents = LastEvent - FirstEvent;
354
355/**
356 * Class replacing std::list and able to provide required stability
357 * during iteration. It's acheived by delaying structural modifications
358 * to the list till the moment particular element is no longer used by
359 * current iterators.
360 */
361class EventMapRecord
362{
363public:
364 /**
365 * We have to be double linked, as structural modifications in list are delayed
366 * till element removed, so we have to know our previous one to update its next
367 */
368 EventMapRecord* mNext;
369 bool mAlive;
370private:
371 EventMapRecord* mPrev;
372 ListenerRecord* mRef; /* must be weak reference */
373 int32_t mRefCnt;
374
375public:
376 EventMapRecord(ListenerRecord* aRef)
377 :
378 mNext(0),
379 mAlive(true),
380 mPrev(0),
381 mRef(aRef),
382 mRefCnt(1)
383 {}
384
385 EventMapRecord(EventMapRecord& aOther)
386 {
387 mNext = aOther.mNext;
388 mPrev = aOther.mPrev;
389 mRef = aOther.mRef;
390 mRefCnt = aOther.mRefCnt;
391 mAlive = aOther.mAlive;
392 }
393
394 ~EventMapRecord()
395 {
396 if (mNext)
397 mNext->mPrev = mPrev;
398 if (mPrev)
399 mPrev->mNext = mNext;
400 }
401
402 void addRef()
403 {
404 ASMAtomicIncS32(&mRefCnt);
405 }
406
407 void release()
408 {
409 if (ASMAtomicDecS32(&mRefCnt) <= 0) delete this;
410 }
411
412 // Called when an element is no longer needed
413 void kill()
414 {
415 mAlive = false;
416 release();
417 }
418
419 ListenerRecord* ref()
420 {
421 return mAlive ? mRef : 0;
422 }
423
424 friend class EventMapList;
425};
426
427
428class EventMapList
429{
430 EventMapRecord* mHead;
431 uint32_t mSize;
432public:
433 EventMapList()
434 :
435 mHead(0),
436 mSize(0)
437 {}
438 ~EventMapList()
439 {
440 EventMapRecord* aCur = mHead;
441 while (aCur)
442 {
443 EventMapRecord* aNext = aCur->mNext;
444 aCur->release();
445 aCur = aNext;
446 }
447 }
448
449 /*
450 * Elements have to be added to the front of the list, to make sure
451 * that iterators doesn't see newly added listeners, and iteration
452 * will always complete.
453 */
454 void add(ListenerRecord* aRec)
455 {
456 EventMapRecord* aNew = new EventMapRecord(aRec);
457 aNew->mNext = mHead;
458 if (mHead)
459 mHead->mPrev = aNew;
460 mHead = aNew;
461 mSize++;
462 }
463
464 /*
465 * Mark element as removed, actual removal could be delayed until
466 * all consumers release it too. This helps to keep list stable
467 * enough for iterators to allow long and probably intrusive callbacks.
468 */
469 void remove(ListenerRecord* aRec)
470 {
471 EventMapRecord* aCur = mHead;
472 while (aCur)
473 {
474 EventMapRecord* aNext = aCur->mNext;
475 if (aCur->ref() == aRec)
476 {
477 if (aCur == mHead)
478 mHead = aNext;
479 aCur->kill();
480 mSize--;
481 // break?
482 }
483 aCur = aNext;
484 }
485 }
486
487 uint32_t size() const
488 {
489 return mSize;
490 }
491
492 struct iterator
493 {
494 EventMapRecord* mCur;
495
496 iterator()
497 : mCur(0)
498 {}
499
500 explicit
501 iterator(EventMapRecord* aCur)
502 : mCur(aCur)
503 {
504 // Prevent element removal, till we're at it
505 if (mCur)
506 mCur->addRef();
507 }
508
509 ~iterator()
510 {
511 if (mCur)
512 mCur->release();
513 }
514
515 ListenerRecord*
516 operator*() const
517 {
518 return mCur->ref();
519 }
520
521 EventMapList::iterator&
522 operator++()
523 {
524 EventMapRecord* aPrev = mCur;
525 do {
526 mCur = mCur->mNext;
527 } while (mCur && !mCur->mAlive);
528
529 // now we can safely release previous element
530 aPrev->release();
531
532 // And grab the new current
533 if (mCur)
534 mCur->addRef();
535
536 return *this;
537 }
538
539 bool
540 operator==(const EventMapList::iterator& aOther) const
541 {
542 return mCur == aOther.mCur;
543 }
544
545 bool
546 operator!=(const EventMapList::iterator& aOther) const
547 {
548 return mCur != aOther.mCur;
549 }
550 };
551
552 iterator begin()
553 {
554 return iterator(mHead);
555 }
556
557 iterator end()
558 {
559 return iterator(0);
560 }
561};
562
563typedef EventMapList EventMap[NumEvents];
564typedef std::map<IEvent*, int32_t> PendingEventsMap;
565typedef std::deque<ComPtr<IEvent> > PassiveQueue;
566
567class ListenerRecord
568{
569private:
570 ComPtr<IEventListener> mListener;
571 BOOL mActive;
572 EventSource* mOwner;
573
574 RTSEMEVENT mQEvent;
575 RTCRITSECT mcsQLock;
576 PassiveQueue mQueue;
577 int32_t mRefCnt;
578
579public:
580 ListenerRecord(IEventListener* aListener,
581 com::SafeArray<VBoxEventType_T>& aInterested,
582 BOOL aActive,
583 EventSource* aOwner);
584 ~ListenerRecord();
585
586 HRESULT process(IEvent* aEvent, BOOL aWaitable, PendingEventsMap::iterator& pit, AutoLockBase& alock);
587 HRESULT enqueue(IEvent* aEvent);
588 HRESULT dequeue(IEvent* *aEvent, LONG aTimeout, AutoLockBase& aAlock);
589 HRESULT eventProcessed(IEvent * aEvent, PendingEventsMap::iterator& pit);
590 void addRef()
591 {
592 ASMAtomicIncS32(&mRefCnt);
593 }
594 void release()
595 {
596 if (ASMAtomicDecS32(&mRefCnt) <= 0) delete this;
597 }
598 BOOL isActive()
599 {
600 return mActive;
601 }
602
603 friend class EventSource;
604};
605
606/* Handy class with semantics close to ComPtr, but for list records */
607template<typename Held>
608class RecordHolder
609{
610public:
611 RecordHolder(Held* lr)
612 :
613 held(lr)
614 {
615 addref();
616 }
617 RecordHolder(const RecordHolder& that)
618 :
619 held(that.held)
620 {
621 addref();
622 }
623 RecordHolder()
624 :
625 held(0)
626 {
627 }
628 ~RecordHolder()
629 {
630 release();
631 }
632
633 Held* obj()
634 {
635 return held;
636 }
637
638 RecordHolder &operator=(const RecordHolder &that)
639 {
640 safe_assign(that.held);
641 return *this;
642 }
643private:
644 Held* held;
645
646 void addref()
647 {
648 if (held)
649 held->addRef();
650 }
651 void release()
652 {
653 if (held)
654 held->release();
655 }
656 void safe_assign (Held *that_p)
657 {
658 if (that_p)
659 that_p->addRef();
660 release();
661 held = that_p;
662 }
663};
664
665typedef std::map<IEventListener*, RecordHolder<ListenerRecord> > Listeners;
666
667struct EventSource::Data
668{
669 Data() {}
670 Listeners mListeners;
671 EventMap mEvMap;
672 PendingEventsMap mPendingMap;
673};
674
675/**
676 * This function defines what wildcard expands to.
677 */
678static BOOL implies(VBoxEventType_T who, VBoxEventType_T what)
679{
680 switch (who)
681 {
682 case VBoxEventType_Any:
683 return TRUE;
684 case VBoxEventType_MachineEvent:
685 return (what == VBoxEventType_OnMachineStateChanged)
686 || (what == VBoxEventType_OnMachineDataChanged)
687 || (what == VBoxEventType_OnMachineRegistered)
688 || (what == VBoxEventType_OnSessionStateChanged)
689 || (what == VBoxEventType_OnGuestPropertyChanged);
690 case VBoxEventType_SnapshotEvent:
691 return (what == VBoxEventType_OnSnapshotTaken)
692 || (what == VBoxEventType_OnSnapshotDeleted)
693 || (what == VBoxEventType_OnSnapshotChanged)
694 ;
695 case VBoxEventType_InputEvent:
696 return (what == VBoxEventType_OnKeyboardLedsChanged)
697 || (what == VBoxEventType_OnMousePointerShapeChanged)
698 || (what == VBoxEventType_OnMouseCapabilityChanged)
699 ;
700 case VBoxEventType_Invalid:
701 return FALSE;
702 default:
703 return who == what;
704 }
705}
706
707ListenerRecord::ListenerRecord(IEventListener* aListener,
708 com::SafeArray<VBoxEventType_T>& aInterested,
709 BOOL aActive,
710 EventSource* aOwner)
711 :
712 mActive(aActive),
713 mOwner(aOwner),
714 mRefCnt(0)
715{
716 mListener = aListener;
717 EventMap* aEvMap = &aOwner->m->mEvMap;
718
719 for (size_t i = 0; i < aInterested.size(); ++i)
720 {
721 VBoxEventType_T interested = aInterested[i];
722 for (int j = FirstEvent; j < LastEvent; j++)
723 {
724 VBoxEventType_T candidate = (VBoxEventType_T)j;
725 if (implies(interested, candidate))
726 {
727 (*aEvMap)[j - FirstEvent].add(this);
728 }
729 }
730 }
731
732 if (!mActive)
733 {
734 ::RTCritSectInit(&mcsQLock);
735 ::RTSemEventCreate (&mQEvent);
736 }
737}
738
739ListenerRecord::~ListenerRecord()
740{
741 /* Remove references to us from the event map */
742 EventMap* aEvMap = &mOwner->m->mEvMap;
743 for (int j = FirstEvent; j < LastEvent; j++)
744 {
745 (*aEvMap)[j - FirstEvent].remove(this);
746 }
747
748 if (!mActive)
749 {
750 // at this moment nobody could add elements to our queue, so we can safely
751 // clean it up, otherwise there will be pending events map elements
752 PendingEventsMap* aPem = &mOwner->m->mPendingMap;
753 while (true)
754 {
755 ComPtr<IEvent> aEvent;
756
757 if (mQueue.empty())
758 break;
759
760 mQueue.front().queryInterfaceTo(aEvent.asOutParam());
761 mQueue.pop_front();
762
763 BOOL aWaitable = FALSE;
764 aEvent->COMGETTER(Waitable)(&aWaitable);
765 if (aWaitable)
766 {
767 PendingEventsMap::iterator pit = aPem->find(aEvent);
768 if (pit != aPem->end())
769 eventProcessed(aEvent, pit);
770 }
771 }
772
773 ::RTCritSectDelete(&mcsQLock);
774 ::RTSemEventDestroy(mQEvent);
775 }
776}
777
778HRESULT ListenerRecord::process(IEvent* aEvent,
779 BOOL aWaitable,
780 PendingEventsMap::iterator& pit,
781 AutoLockBase& aAlock)
782{
783 if (mActive)
784 {
785 /*
786 * We release lock here to allow modifying ops on EventSource inside callback.
787 */
788 HRESULT rc = S_OK;
789 if (mListener)
790 {
791 aAlock.release();
792 rc = mListener->HandleEvent(aEvent);
793 aAlock.acquire();
794 }
795 if (aWaitable)
796 eventProcessed(aEvent, pit);
797 return rc;
798 }
799 else
800 return enqueue(aEvent);
801}
802
803
804HRESULT ListenerRecord::enqueue (IEvent* aEvent)
805{
806 AssertMsg(!mActive, ("must be passive\n"));
807 // put an event the queue
808 ::RTCritSectEnter(&mcsQLock);
809 mQueue.push_back(aEvent);
810 ::RTCritSectLeave(&mcsQLock);
811
812 // notify waiters
813 ::RTSemEventSignal(mQEvent);
814
815 return S_OK;
816}
817
818HRESULT ListenerRecord::dequeue (IEvent* *aEvent,
819 LONG aTimeout,
820 AutoLockBase& aAlock)
821{
822 AssertMsg(!mActive, ("must be passive\n"));
823
824 // retain listener record
825 RecordHolder<ListenerRecord> holder(this);
826
827 ::RTCritSectEnter(&mcsQLock);
828 if (mQueue.empty()) {
829 ::RTCritSectLeave(&mcsQLock);
830 // Speed up common case
831 if (aTimeout == 0)
832 {
833 *aEvent = NULL;
834 return S_OK;
835 }
836 // release lock while waiting, listener will not go away due to above holder
837 aAlock.release();
838 ::RTSemEventWait(mQEvent, aTimeout);
839 // reacquire lock
840 aAlock.acquire();
841 ::RTCritSectEnter(&mcsQLock);
842 }
843 if (mQueue.empty())
844 {
845 *aEvent = NULL;
846 }
847 else
848 {
849 mQueue.front().queryInterfaceTo(aEvent);
850 mQueue.pop_front();
851 }
852 ::RTCritSectLeave(&mcsQLock);
853 return S_OK;
854}
855
856HRESULT ListenerRecord::eventProcessed (IEvent* aEvent, PendingEventsMap::iterator& pit)
857{
858 if (--pit->second == 0)
859 {
860 Assert(pit->first == aEvent);
861 aEvent->SetProcessed();
862 mOwner->m->mPendingMap.erase(pit);
863 }
864
865 Assert(pit->second >= 0);
866 return S_OK;
867}
868
869EventSource::EventSource()
870{}
871
872EventSource::~EventSource()
873{}
874
875HRESULT EventSource::FinalConstruct()
876{
877 m = new Data;
878 return S_OK;
879}
880
881void EventSource::FinalRelease()
882{
883 uninit();
884 delete m;
885}
886
887HRESULT EventSource::init(IUnknown *)
888{
889 HRESULT rc = S_OK;
890
891 AutoInitSpan autoInitSpan(this);
892 AssertReturn(autoInitSpan.isOk(), E_FAIL);
893
894 /* Confirm a successful initialization */
895 autoInitSpan.setSucceeded();
896 return rc;
897}
898
899void EventSource::uninit()
900{
901 AutoUninitSpan autoUninitSpan(this);
902 if (autoUninitSpan.uninitDone())
903 return;
904 m->mListeners.clear();
905 // m->mEvMap shall be cleared at this point too by destructors, assert?
906}
907
908STDMETHODIMP EventSource::RegisterListener(IEventListener * aListener,
909 ComSafeArrayIn(VBoxEventType_T, aInterested),
910 BOOL aActive)
911{
912 CheckComArgNotNull(aListener);
913 CheckComArgSafeArrayNotNull(aInterested);
914
915 AutoCaller autoCaller(this);
916 if (FAILED(autoCaller.rc())) return autoCaller.rc();
917
918 {
919 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
920
921 Listeners::const_iterator it = m->mListeners.find(aListener);
922 if (it != m->mListeners.end())
923 return setError(E_INVALIDARG,
924 tr("This listener already registered"));
925
926 com::SafeArray<VBoxEventType_T> interested(ComSafeArrayInArg (aInterested));
927 RecordHolder<ListenerRecord> lrh(new ListenerRecord(aListener, interested, aActive, this));
928 m->mListeners.insert(Listeners::value_type(aListener, lrh));
929 }
930
931 VBoxEventDesc evDesc;
932 evDesc.init(this, VBoxEventType_OnEventSourceChanged, aListener, TRUE);
933 evDesc.fire(0);
934
935 return S_OK;
936}
937
938STDMETHODIMP EventSource::UnregisterListener(IEventListener * aListener)
939{
940 CheckComArgNotNull(aListener);
941
942 AutoCaller autoCaller(this);
943 if (FAILED(autoCaller.rc())) return autoCaller.rc();
944
945 HRESULT rc;
946 {
947 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
948
949 Listeners::iterator it = m->mListeners.find(aListener);
950
951 if (it != m->mListeners.end())
952 {
953 m->mListeners.erase(it);
954 // destructor removes refs from the event map
955 rc = S_OK;
956 }
957 else
958 {
959 rc = setError(VBOX_E_OBJECT_NOT_FOUND,
960 tr("Listener was never registered"));
961 }
962 }
963
964 if (SUCCEEDED(rc))
965 {
966 VBoxEventDesc evDesc;
967 evDesc.init(this, VBoxEventType_OnEventSourceChanged, aListener, FALSE);
968 evDesc.fire(0);
969 }
970
971 return rc;
972}
973
974STDMETHODIMP EventSource::FireEvent(IEvent * aEvent,
975 LONG aTimeout,
976 BOOL *aProcessed)
977{
978 CheckComArgNotNull(aEvent);
979 CheckComArgOutPointerValid(aProcessed);
980
981 AutoCaller autoCaller(this);
982 if (FAILED(autoCaller.rc())) return autoCaller.rc();
983
984 HRESULT hrc;
985 BOOL aWaitable = FALSE;
986 aEvent->COMGETTER(Waitable)(&aWaitable);
987
988 do {
989 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
990
991 VBoxEventType_T evType;
992 hrc = aEvent->COMGETTER(Type)(&evType);
993 AssertComRCReturn(hrc, VERR_ACCESS_DENIED);
994
995 EventMapList& listeners = m->mEvMap[(int)evType-FirstEvent];
996
997 /* Anyone interested in this event? */
998 uint32_t cListeners = listeners.size();
999 if (cListeners == 0)
1000 {
1001 aEvent->SetProcessed();
1002 break; // just leave the lock and update event object state
1003 }
1004
1005 PendingEventsMap::iterator pit;
1006
1007 if (aWaitable)
1008 {
1009 m->mPendingMap.insert(PendingEventsMap::value_type(aEvent, cListeners));
1010 // we keep iterator here to allow processing active listeners without
1011 // pending events lookup
1012 pit = m->mPendingMap.find(aEvent);
1013 }
1014 for(EventMapList::iterator it = listeners.begin();
1015 it != listeners.end(); ++it)
1016 {
1017 HRESULT cbRc;
1018 // keep listener record reference, in case someone will remove it while in callback
1019 RecordHolder<ListenerRecord> record(*it);
1020
1021 /**
1022 * We pass lock here to allow modifying ops on EventSource inside callback
1023 * in active mode. Note that we expect list iterator stability as 'alock'
1024 * could be temporary released when calling event handler.
1025 */
1026 cbRc = record.obj()->process(aEvent, aWaitable, pit, alock);
1027
1028 if (FAILED_DEAD_INTERFACE(cbRc))
1029 {
1030 Listeners::iterator lit = m->mListeners.find(record.obj()->mListener);
1031 if (lit != m->mListeners.end())
1032 m->mListeners.erase(lit);
1033 }
1034 // anything else to do with cbRc?
1035 }
1036 } while (0);
1037 /* We leave the lock here */
1038
1039 if (aWaitable)
1040 hrc = aEvent->WaitProcessed(aTimeout, aProcessed);
1041 else
1042 *aProcessed = TRUE;
1043
1044 return hrc;
1045}
1046
1047
1048STDMETHODIMP EventSource::GetEvent(IEventListener * aListener,
1049 LONG aTimeout,
1050 IEvent ** aEvent)
1051{
1052
1053 CheckComArgNotNull(aListener);
1054
1055 AutoCaller autoCaller(this);
1056 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1057
1058 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1059
1060 Listeners::iterator it = m->mListeners.find(aListener);
1061 HRESULT rc;
1062
1063 if (it != m->mListeners.end())
1064 rc = it->second.obj()->dequeue(aEvent, aTimeout, alock);
1065 else
1066 rc = setError(VBOX_E_OBJECT_NOT_FOUND,
1067 tr("Listener was never registered"));
1068
1069 return rc;
1070}
1071
1072STDMETHODIMP EventSource::EventProcessed(IEventListener * aListener,
1073 IEvent * aEvent)
1074{
1075 CheckComArgNotNull(aListener);
1076 CheckComArgNotNull(aEvent);
1077
1078 AutoCaller autoCaller(this);
1079 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1080
1081 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1082
1083 Listeners::iterator it = m->mListeners.find(aListener);
1084 HRESULT rc;
1085
1086 BOOL aWaitable = FALSE;
1087 aEvent->COMGETTER(Waitable)(&aWaitable);
1088
1089 if (it != m->mListeners.end())
1090 {
1091 ListenerRecord* aRecord = it->second.obj();
1092
1093 if (aRecord->isActive())
1094 return setError(E_INVALIDARG,
1095 tr("Only applicable to passive listeners"));
1096
1097 if (aWaitable)
1098 {
1099 PendingEventsMap::iterator pit = m->mPendingMap.find(aEvent);
1100
1101 if (pit == m->mPendingMap.end())
1102 {
1103 AssertFailed();
1104 rc = setError(VBOX_E_OBJECT_NOT_FOUND,
1105 tr("Unknown event"));
1106 }
1107 else
1108 rc = aRecord->eventProcessed(aEvent, pit);
1109 }
1110 else
1111 {
1112 // for non-waitable events we're done
1113 rc = S_OK;
1114 }
1115 }
1116 else
1117 {
1118 rc = setError(VBOX_E_OBJECT_NOT_FOUND,
1119 tr("Listener was never registered"));
1120 }
1121
1122 return rc;
1123}
1124
1125/**
1126 * This class serves as feasible listener implementation
1127 * which could be used by clients not able to create local
1128 * COM objects, but still willing to receive event
1129 * notifications in passive mode, such as webservices.
1130 */
1131class ATL_NO_VTABLE PassiveEventListener :
1132 public VirtualBoxBase,
1133 VBOX_SCRIPTABLE_IMPL(IEventListener)
1134{
1135public:
1136
1137 VIRTUALBOXBASE_ADD_ERRORINFO_SUPPORT(PassiveEventListener, IEventListener)
1138
1139 DECLARE_NOT_AGGREGATABLE(PassiveEventListener)
1140
1141 DECLARE_PROTECT_FINAL_CONSTRUCT()
1142
1143 BEGIN_COM_MAP(PassiveEventListener)
1144 COM_INTERFACE_ENTRY(ISupportErrorInfo)
1145 COM_INTERFACE_ENTRY(IEventListener)
1146 COM_INTERFACE_ENTRY(IDispatch)
1147 END_COM_MAP()
1148
1149 PassiveEventListener()
1150 {}
1151 ~PassiveEventListener()
1152 {}
1153
1154 HRESULT FinalConstruct()
1155 {
1156 return S_OK;
1157 }
1158 void FinalRelease()
1159 {}
1160
1161 // IEventListener methods
1162 STDMETHOD(HandleEvent)(IEvent *)
1163 {
1164 ComAssertMsgRet(false, ("HandleEvent() of wrapper shall never be called"),
1165 E_FAIL);
1166 }
1167};
1168
1169#ifdef VBOX_WITH_XPCOM
1170NS_DECL_CLASSINFO(PassiveEventListener)
1171NS_IMPL_THREADSAFE_ISUPPORTS1_CI(PassiveEventListener, IEventListener)
1172NS_DECL_CLASSINFO(VBoxEvent)
1173NS_IMPL_THREADSAFE_ISUPPORTS1_CI(VBoxEvent, IEvent)
1174NS_DECL_CLASSINFO(VBoxVetoEvent)
1175NS_IMPL_ISUPPORTS_INHERITED1(VBoxVetoEvent, VBoxEvent, IVetoEvent)
1176NS_DECL_CLASSINFO(EventSource)
1177NS_IMPL_THREADSAFE_ISUPPORTS1_CI(EventSource, IEventSource)
1178#endif
1179
1180STDMETHODIMP EventSource::CreateListener(IEventListener ** aListener)
1181{
1182 CheckComArgOutPointerValid(aListener);
1183
1184 AutoCaller autoCaller(this);
1185 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1186
1187 ComObjPtr<PassiveEventListener> listener;
1188
1189 HRESULT rc = listener.createObject();
1190 ComAssertMsgRet(SUCCEEDED(rc), ("Could not create wrapper object (%Rrc)", rc),
1191 E_FAIL);
1192 listener.queryInterfaceTo(aListener);
1193 return S_OK;
1194}
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