VirtualBox

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

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

Main: don't return an IPRT error code here

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.4 KB
Line 
1/* $Id: EventImpl.cpp 31269 2010-08-02 08:05:57Z 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_MachineEvent:
687 return (what == VBoxEventType_OnMachineStateChanged)
688 || (what == VBoxEventType_OnMachineDataChanged)
689 || (what == VBoxEventType_OnMachineRegistered)
690 || (what == VBoxEventType_OnSessionStateChanged)
691 || (what == VBoxEventType_OnGuestPropertyChanged);
692 case VBoxEventType_SnapshotEvent:
693 return (what == VBoxEventType_OnSnapshotTaken)
694 || (what == VBoxEventType_OnSnapshotDeleted)
695 || (what == VBoxEventType_OnSnapshotChanged)
696 ;
697 case VBoxEventType_InputEvent:
698 return (what == VBoxEventType_OnKeyboardLedsChanged)
699 || (what == VBoxEventType_OnMousePointerShapeChanged)
700 || (what == VBoxEventType_OnMouseCapabilityChanged)
701 ;
702 case VBoxEventType_Invalid:
703 return FALSE;
704 default:
705 return who == what;
706 }
707}
708
709ListenerRecord::ListenerRecord(IEventListener* aListener,
710 com::SafeArray<VBoxEventType_T>& aInterested,
711 BOOL aActive,
712 EventSource* aOwner)
713 :
714 mActive(aActive),
715 mOwner(aOwner),
716 mRefCnt(0)
717{
718 mListener = aListener;
719 EventMap* aEvMap = &aOwner->m->mEvMap;
720
721 for (size_t i = 0; i < aInterested.size(); ++i)
722 {
723 VBoxEventType_T interested = aInterested[i];
724 for (int j = FirstEvent; j < LastEvent; j++)
725 {
726 VBoxEventType_T candidate = (VBoxEventType_T)j;
727 if (implies(interested, candidate))
728 {
729 (*aEvMap)[j - FirstEvent].add(this);
730 }
731 }
732 }
733
734 if (!mActive)
735 {
736 ::RTCritSectInit(&mcsQLock);
737 ::RTSemEventCreate (&mQEvent);
738 mLastRead = RTTimeMilliTS();
739 }
740 else
741 {
742 mQEvent =NIL_RTSEMEVENT;
743 RT_ZERO(mcsQLock);
744 mLastRead = 0;
745 }
746}
747
748ListenerRecord::~ListenerRecord()
749{
750 /* Remove references to us from the event map */
751 EventMap* aEvMap = &mOwner->m->mEvMap;
752 for (int j = FirstEvent; j < LastEvent; j++)
753 {
754 (*aEvMap)[j - FirstEvent].remove(this);
755 }
756
757 if (!mActive)
758 {
759 // at this moment nobody could add elements to our queue, so we can safely
760 // clean it up, otherwise there will be pending events map elements
761 PendingEventsMap* aPem = &mOwner->m->mPendingMap;
762 while (true)
763 {
764 ComPtr<IEvent> aEvent;
765
766 if (mQueue.empty())
767 break;
768
769 mQueue.front().queryInterfaceTo(aEvent.asOutParam());
770 mQueue.pop_front();
771
772 BOOL aWaitable = FALSE;
773 aEvent->COMGETTER(Waitable)(&aWaitable);
774 if (aWaitable)
775 {
776 PendingEventsMap::iterator pit = aPem->find(aEvent);
777 if (pit != aPem->end())
778 eventProcessed(aEvent, pit);
779 }
780 }
781
782 ::RTCritSectDelete(&mcsQLock);
783 ::RTSemEventDestroy(mQEvent);
784 }
785}
786
787HRESULT ListenerRecord::process(IEvent* aEvent,
788 BOOL aWaitable,
789 PendingEventsMap::iterator& pit,
790 AutoLockBase& aAlock)
791{
792 if (mActive)
793 {
794 /*
795 * We release lock here to allow modifying ops on EventSource inside callback.
796 */
797 HRESULT rc = S_OK;
798 if (mListener)
799 {
800 aAlock.release();
801 rc = mListener->HandleEvent(aEvent);
802 aAlock.acquire();
803 }
804 if (aWaitable)
805 eventProcessed(aEvent, pit);
806 return rc;
807 }
808 else
809 return enqueue(aEvent);
810}
811
812
813HRESULT ListenerRecord::enqueue (IEvent* aEvent)
814{
815 AssertMsg(!mActive, ("must be passive\n"));
816
817 // put an event the queue
818 ::RTCritSectEnter(&mcsQLock);
819
820 // If there was no events reading from the listener for the long time,
821 // and events keep coming, or queue is oversized we shall unregister this listener.
822 uint64_t sinceRead = RTTimeMilliTS() - mLastRead;
823 uint32_t queueSize = mQueue.size();
824 if ( (queueSize > 200) || ((queueSize > 100) && (sinceRead > 60 * 1000)))
825 {
826 ::RTCritSectLeave(&mcsQLock);
827 return E_ABORT;
828 }
829
830 mQueue.push_back(aEvent);
831 ::RTCritSectLeave(&mcsQLock);
832
833 // notify waiters
834 ::RTSemEventSignal(mQEvent);
835
836 return S_OK;
837}
838
839HRESULT ListenerRecord::dequeue (IEvent* *aEvent,
840 LONG aTimeout,
841 AutoLockBase& aAlock)
842{
843 AssertMsg(!mActive, ("must be passive\n"));
844
845 // retain listener record
846 RecordHolder<ListenerRecord> holder(this);
847
848 ::RTCritSectEnter(&mcsQLock);
849
850 mLastRead = RTTimeMilliTS();
851
852 if (mQueue.empty()) {
853 ::RTCritSectLeave(&mcsQLock);
854 // Speed up common case
855 if (aTimeout == 0)
856 {
857 *aEvent = NULL;
858 return S_OK;
859 }
860 // release lock while waiting, listener will not go away due to above holder
861 aAlock.release();
862 ::RTSemEventWait(mQEvent, aTimeout);
863 // reacquire lock
864 aAlock.acquire();
865 ::RTCritSectEnter(&mcsQLock);
866 }
867 if (mQueue.empty())
868 {
869 *aEvent = NULL;
870 }
871 else
872 {
873 mQueue.front().queryInterfaceTo(aEvent);
874 mQueue.pop_front();
875 }
876 ::RTCritSectLeave(&mcsQLock);
877 return S_OK;
878}
879
880HRESULT ListenerRecord::eventProcessed (IEvent* aEvent, PendingEventsMap::iterator& pit)
881{
882 if (--pit->second == 0)
883 {
884 Assert(pit->first == aEvent);
885 aEvent->SetProcessed();
886 mOwner->m->mPendingMap.erase(pit);
887 }
888
889 Assert(pit->second >= 0);
890 return S_OK;
891}
892
893EventSource::EventSource()
894{}
895
896EventSource::~EventSource()
897{}
898
899HRESULT EventSource::FinalConstruct()
900{
901 m = new Data;
902 return S_OK;
903}
904
905void EventSource::FinalRelease()
906{
907 uninit();
908 delete m;
909}
910
911HRESULT EventSource::init(IUnknown *)
912{
913 HRESULT rc = S_OK;
914
915 AutoInitSpan autoInitSpan(this);
916 AssertReturn(autoInitSpan.isOk(), E_FAIL);
917
918 /* Confirm a successful initialization */
919 autoInitSpan.setSucceeded();
920 return rc;
921}
922
923void EventSource::uninit()
924{
925 AutoUninitSpan autoUninitSpan(this);
926 if (autoUninitSpan.uninitDone())
927 return;
928 m->mListeners.clear();
929 // m->mEvMap shall be cleared at this point too by destructors, assert?
930}
931
932STDMETHODIMP EventSource::RegisterListener(IEventListener * aListener,
933 ComSafeArrayIn(VBoxEventType_T, aInterested),
934 BOOL aActive)
935{
936 CheckComArgNotNull(aListener);
937 CheckComArgSafeArrayNotNull(aInterested);
938
939 AutoCaller autoCaller(this);
940 if (FAILED(autoCaller.rc())) return autoCaller.rc();
941
942 {
943 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
944
945 Listeners::const_iterator it = m->mListeners.find(aListener);
946 if (it != m->mListeners.end())
947 return setError(E_INVALIDARG,
948 tr("This listener already registered"));
949
950 com::SafeArray<VBoxEventType_T> interested(ComSafeArrayInArg (aInterested));
951 RecordHolder<ListenerRecord> lrh(new ListenerRecord(aListener, interested, aActive, this));
952 m->mListeners.insert(Listeners::value_type(aListener, lrh));
953 }
954
955 VBoxEventDesc evDesc;
956 evDesc.init(this, VBoxEventType_OnEventSourceChanged, aListener, TRUE);
957 evDesc.fire(0);
958
959 return S_OK;
960}
961
962STDMETHODIMP EventSource::UnregisterListener(IEventListener * aListener)
963{
964 CheckComArgNotNull(aListener);
965
966 AutoCaller autoCaller(this);
967 if (FAILED(autoCaller.rc())) return autoCaller.rc();
968
969 HRESULT rc;
970 {
971 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
972
973 Listeners::iterator it = m->mListeners.find(aListener);
974
975 if (it != m->mListeners.end())
976 {
977 m->mListeners.erase(it);
978 // destructor removes refs from the event map
979 rc = S_OK;
980 }
981 else
982 {
983 rc = setError(VBOX_E_OBJECT_NOT_FOUND,
984 tr("Listener was never registered"));
985 }
986 }
987
988 if (SUCCEEDED(rc))
989 {
990 VBoxEventDesc evDesc;
991 evDesc.init(this, VBoxEventType_OnEventSourceChanged, aListener, FALSE);
992 evDesc.fire(0);
993 }
994
995 return rc;
996}
997
998STDMETHODIMP EventSource::FireEvent(IEvent * aEvent,
999 LONG aTimeout,
1000 BOOL *aProcessed)
1001{
1002 CheckComArgNotNull(aEvent);
1003 CheckComArgOutPointerValid(aProcessed);
1004
1005 AutoCaller autoCaller(this);
1006 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1007
1008 HRESULT hrc;
1009 BOOL aWaitable = FALSE;
1010 aEvent->COMGETTER(Waitable)(&aWaitable);
1011
1012 do {
1013 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1014
1015 VBoxEventType_T evType;
1016 hrc = aEvent->COMGETTER(Type)(&evType);
1017 AssertComRCReturn(hrc, hrc);
1018
1019 EventMapList& listeners = m->mEvMap[(int)evType-FirstEvent];
1020
1021 /* Anyone interested in this event? */
1022 uint32_t cListeners = listeners.size();
1023 if (cListeners == 0)
1024 {
1025 aEvent->SetProcessed();
1026 break; // just leave the lock and update event object state
1027 }
1028
1029 PendingEventsMap::iterator pit;
1030
1031 if (aWaitable)
1032 {
1033 m->mPendingMap.insert(PendingEventsMap::value_type(aEvent, cListeners));
1034 // we keep iterator here to allow processing active listeners without
1035 // pending events lookup
1036 pit = m->mPendingMap.find(aEvent);
1037 }
1038 for(EventMapList::iterator it = listeners.begin();
1039 it != listeners.end(); ++it)
1040 {
1041 HRESULT cbRc;
1042 // keep listener record reference, in case someone will remove it while in callback
1043 RecordHolder<ListenerRecord> record(*it);
1044
1045 /**
1046 * We pass lock here to allow modifying ops on EventSource inside callback
1047 * in active mode. Note that we expect list iterator stability as 'alock'
1048 * could be temporary released when calling event handler.
1049 */
1050 cbRc = record.obj()->process(aEvent, aWaitable, pit, alock);
1051
1052 if (FAILED_DEAD_INTERFACE(cbRc) || (cbRc == E_ABORT))
1053 {
1054 Listeners::iterator lit = m->mListeners.find(record.obj()->mListener);
1055 if (lit != m->mListeners.end())
1056 m->mListeners.erase(lit);
1057 }
1058 // anything else to do with cbRc?
1059 }
1060 } while (0);
1061 /* We leave the lock here */
1062
1063 if (aWaitable)
1064 hrc = aEvent->WaitProcessed(aTimeout, aProcessed);
1065 else
1066 *aProcessed = TRUE;
1067
1068 return hrc;
1069}
1070
1071
1072STDMETHODIMP EventSource::GetEvent(IEventListener * aListener,
1073 LONG aTimeout,
1074 IEvent ** aEvent)
1075{
1076
1077 CheckComArgNotNull(aListener);
1078
1079 AutoCaller autoCaller(this);
1080 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1081
1082 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1083
1084 Listeners::iterator it = m->mListeners.find(aListener);
1085 HRESULT rc;
1086
1087 if (it != m->mListeners.end())
1088 rc = it->second.obj()->dequeue(aEvent, aTimeout, alock);
1089 else
1090 rc = setError(VBOX_E_OBJECT_NOT_FOUND,
1091 tr("Listener was never registered"));
1092
1093 return rc;
1094}
1095
1096STDMETHODIMP EventSource::EventProcessed(IEventListener * aListener,
1097 IEvent * aEvent)
1098{
1099 CheckComArgNotNull(aListener);
1100 CheckComArgNotNull(aEvent);
1101
1102 AutoCaller autoCaller(this);
1103 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1104
1105 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1106
1107 Listeners::iterator it = m->mListeners.find(aListener);
1108 HRESULT rc;
1109
1110 BOOL aWaitable = FALSE;
1111 aEvent->COMGETTER(Waitable)(&aWaitable);
1112
1113 if (it != m->mListeners.end())
1114 {
1115 ListenerRecord* aRecord = it->second.obj();
1116
1117 if (aRecord->isActive())
1118 return setError(E_INVALIDARG,
1119 tr("Only applicable to passive listeners"));
1120
1121 if (aWaitable)
1122 {
1123 PendingEventsMap::iterator pit = m->mPendingMap.find(aEvent);
1124
1125 if (pit == m->mPendingMap.end())
1126 {
1127 AssertFailed();
1128 rc = setError(VBOX_E_OBJECT_NOT_FOUND,
1129 tr("Unknown event"));
1130 }
1131 else
1132 rc = aRecord->eventProcessed(aEvent, pit);
1133 }
1134 else
1135 {
1136 // for non-waitable events we're done
1137 rc = S_OK;
1138 }
1139 }
1140 else
1141 {
1142 rc = setError(VBOX_E_OBJECT_NOT_FOUND,
1143 tr("Listener was never registered"));
1144 }
1145
1146 return rc;
1147}
1148
1149/**
1150 * This class serves as feasible listener implementation
1151 * which could be used by clients not able to create local
1152 * COM objects, but still willing to receive event
1153 * notifications in passive mode, such as webservices.
1154 */
1155class ATL_NO_VTABLE PassiveEventListener :
1156 public VirtualBoxBase,
1157 VBOX_SCRIPTABLE_IMPL(IEventListener)
1158{
1159public:
1160
1161 VIRTUALBOXBASE_ADD_ERRORINFO_SUPPORT(PassiveEventListener, IEventListener)
1162
1163 DECLARE_NOT_AGGREGATABLE(PassiveEventListener)
1164
1165 DECLARE_PROTECT_FINAL_CONSTRUCT()
1166
1167 BEGIN_COM_MAP(PassiveEventListener)
1168 COM_INTERFACE_ENTRY(ISupportErrorInfo)
1169 COM_INTERFACE_ENTRY(IEventListener)
1170 COM_INTERFACE_ENTRY(IDispatch)
1171 END_COM_MAP()
1172
1173 PassiveEventListener()
1174 {}
1175 ~PassiveEventListener()
1176 {}
1177
1178 HRESULT FinalConstruct()
1179 {
1180 return S_OK;
1181 }
1182 void FinalRelease()
1183 {}
1184
1185 // IEventListener methods
1186 STDMETHOD(HandleEvent)(IEvent *)
1187 {
1188 ComAssertMsgRet(false, ("HandleEvent() of wrapper shall never be called"),
1189 E_FAIL);
1190 }
1191};
1192
1193#ifdef VBOX_WITH_XPCOM
1194NS_DECL_CLASSINFO(PassiveEventListener)
1195NS_IMPL_THREADSAFE_ISUPPORTS1_CI(PassiveEventListener, IEventListener)
1196NS_DECL_CLASSINFO(VBoxEvent)
1197NS_IMPL_THREADSAFE_ISUPPORTS1_CI(VBoxEvent, IEvent)
1198NS_DECL_CLASSINFO(VBoxVetoEvent)
1199NS_IMPL_ISUPPORTS_INHERITED1(VBoxVetoEvent, VBoxEvent, IVetoEvent)
1200NS_DECL_CLASSINFO(EventSource)
1201NS_IMPL_THREADSAFE_ISUPPORTS1_CI(EventSource, IEventSource)
1202#endif
1203
1204STDMETHODIMP EventSource::CreateListener(IEventListener ** aListener)
1205{
1206 CheckComArgOutPointerValid(aListener);
1207
1208 AutoCaller autoCaller(this);
1209 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1210
1211 ComObjPtr<PassiveEventListener> listener;
1212
1213 HRESULT rc = listener.createObject();
1214 ComAssertMsgRet(SUCCEEDED(rc), ("Could not create wrapper object (%Rrc)", rc),
1215 E_FAIL);
1216 listener.queryInterfaceTo(aListener);
1217 return S_OK;
1218}
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