VirtualBox

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

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

Main: remove SupportErrorInfo template magic

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