VirtualBox

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

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

Main: event names now in past tense

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