VirtualBox

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

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

Main: generic events (disabled)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.9 KB
Line 
1/* $Id: EventImpl.cpp 30381 2010-06-22 22:06:00Z 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#include <list>
19#include <map>
20#include <deque>
21
22#include "EventImpl.h"
23#include "AutoCaller.h"
24#include "Logging.h"
25
26#include <iprt/semaphore.h>
27#include <iprt/critsect.h>
28#include <iprt/asm.h>
29
30#include <VBox/com/array.h>
31
32struct VBoxEvent::Data
33{
34 Data()
35 :
36 mType(VBoxEventType_Invalid),
37 mWaitEvent(NIL_RTSEMEVENT),
38 mWaitable(FALSE),
39 mProcessed(FALSE)
40 {}
41 VBoxEventType_T mType;
42 RTSEMEVENT mWaitEvent;
43 BOOL mWaitable;
44 BOOL mProcessed;
45 ComPtr<IEventSource> mSource;
46};
47
48HRESULT VBoxEvent::FinalConstruct()
49{
50 m = new Data;
51 return S_OK;
52}
53
54void VBoxEvent::FinalRelease()
55{
56 if (m)
57 {
58 uninit();
59 delete m;
60 m = 0;
61 }
62}
63
64
65HRESULT VBoxEvent::init(IEventSource *aSource, VBoxEventType_T aType, BOOL aWaitable)
66{
67 HRESULT rc = S_OK;
68
69 AssertReturn(aSource != NULL, E_INVALIDARG);
70
71 AutoInitSpan autoInitSpan(this);
72 AssertReturn(autoInitSpan.isOk(), E_FAIL);
73
74 m->mSource = aSource;
75 m->mType = aType;
76 m->mWaitable = aWaitable;
77 m->mProcessed = !aWaitable;
78
79 do {
80 if (aWaitable)
81 {
82 int vrc = ::RTSemEventCreate (&m->mWaitEvent);
83
84 if (RT_FAILURE(vrc))
85 {
86 AssertFailed ();
87 return setError(E_FAIL,
88 tr("Internal error (%Rrc)"), vrc);
89 }
90 }
91 } while (0);
92
93 /* Confirm a successful initialization */
94 autoInitSpan.setSucceeded();
95
96 return rc;
97}
98
99void VBoxEvent::uninit()
100{
101 if (!m)
102 return;
103
104 m->mProcessed = TRUE;
105 m->mType = VBoxEventType_Invalid;
106 m->mSource.setNull();
107
108 if (m->mWaitEvent != NIL_RTSEMEVENT)
109 {
110 Assert(m->mWaitable);
111 ::RTSemEventDestroy(m->mWaitEvent);
112 m->mWaitEvent = NIL_RTSEMEVENT;
113 }
114}
115
116STDMETHODIMP VBoxEvent::COMGETTER(Type)(VBoxEventType_T *aType)
117{
118 CheckComArgNotNull(aType);
119
120 AutoCaller autoCaller(this);
121 if (FAILED(autoCaller.rc())) return autoCaller.rc();
122
123 // never changes till event alive, no locking?
124 *aType = m->mType;
125 return S_OK;
126}
127
128STDMETHODIMP VBoxEvent::COMGETTER(Source)(IEventSource* *aSource)
129{
130 CheckComArgOutPointerValid(aSource);
131
132 AutoCaller autoCaller(this);
133 if (FAILED(autoCaller.rc())) return autoCaller.rc();
134
135 m->mSource.queryInterfaceTo(aSource);
136 return S_OK;
137}
138
139STDMETHODIMP VBoxEvent::COMGETTER(Waitable)(BOOL *aWaitable)
140{
141 CheckComArgNotNull(aWaitable);
142
143 AutoCaller autoCaller(this);
144 if (FAILED(autoCaller.rc())) return autoCaller.rc();
145
146 // never changes till event alive, no locking?
147 *aWaitable = m->mWaitable;
148 return S_OK;
149}
150
151
152STDMETHODIMP VBoxEvent::SetProcessed()
153{
154 AutoCaller autoCaller(this);
155 if (FAILED(autoCaller.rc())) return autoCaller.rc();
156
157 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
158
159 if (m->mProcessed)
160 return S_OK;
161
162 m->mProcessed = TRUE;
163
164 // notify waiters
165 ::RTSemEventSignal(m->mWaitEvent);
166
167 return S_OK;
168}
169
170STDMETHODIMP VBoxEvent::WaitProcessed(LONG aTimeout, BOOL *aResult)
171{
172 CheckComArgNotNull(aResult);
173
174 AutoCaller autoCaller(this);
175 if (FAILED(autoCaller.rc())) return autoCaller.rc();
176
177 {
178 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
179
180 if (m->mProcessed)
181 {
182 *aResult = TRUE;
183 return S_OK;
184 }
185
186 if (aTimeout == 0)
187 {
188 *aResult = m->mProcessed;
189 return S_OK;
190 }
191 }
192
193 int vrc = ::RTSemEventWait(m->mWaitEvent, aTimeout);
194 AssertMsg(RT_SUCCESS(vrc) || vrc == VERR_TIMEOUT || vrc == VERR_INTERRUPTED,
195 ("RTSemEventWait returned %Rrc\n", vrc));
196
197 if (RT_SUCCESS(vrc))
198 {
199 AssertMsg(m->mProcessed,
200 ("mProcessed must be set here\n"));
201 *aResult = m->mProcessed;
202 }
203 else
204 {
205 *aResult = FALSE;
206 }
207
208 return S_OK;
209}
210
211static const int FirstEvent = (int)VBoxEventType_LastWildcard + 1;
212static const int LastEvent = (int)VBoxEventType_Last;
213static const int NumEvents = LastEvent - FirstEvent;
214
215class ListenerRecord;
216typedef std::list<ListenerRecord*> EventMap[NumEvents];
217typedef std::map<IEvent*, int32_t> PendingEventsMap;
218typedef std::deque<ComPtr<IEvent> > PassiveQueue;
219
220class ListenerRecord
221{
222private:
223 ComPtr<IEventListener> mListener;
224 BOOL mActive;
225 EventSource* mOwner;
226
227 RTSEMEVENT mQEvent;
228 RTCRITSECT mcsQLock;
229 PassiveQueue mQueue;
230 int32_t mRefCnt;
231
232public:
233 ListenerRecord(IEventListener* aListener,
234 com::SafeArray<VBoxEventType_T>& aInterested,
235 BOOL aActive,
236 EventSource* aOwner);
237 ~ListenerRecord();
238
239 HRESULT process(IEvent* aEvent, BOOL aWaitable, PendingEventsMap::iterator& pit);
240 HRESULT enqueue(IEvent* aEvent);
241 HRESULT dequeue(IEvent* *aEvent, LONG aTimeout);
242 HRESULT eventProcessed(IEvent * aEvent, PendingEventsMap::iterator& pit);
243 void addRef()
244 {
245 ASMAtomicIncS32(&mRefCnt);
246 }
247 void release()
248 {
249 if (ASMAtomicDecS32(&mRefCnt) <= 0) delete this;
250 }
251 BOOL isActive()
252 {
253 return mActive;
254 }
255};
256
257/* Handy class with semantics close to ComPtr, but for ListenerRecord */
258class ListenerRecordHolder
259{
260public:
261 ListenerRecordHolder(ListenerRecord* lr)
262 :
263 held(lr)
264 {
265 addref();
266 }
267 ListenerRecordHolder(const ListenerRecordHolder& that)
268 :
269 held(that.held)
270 {
271 addref();
272 }
273 ListenerRecordHolder()
274 :
275 held(0)
276 {
277 }
278 ~ListenerRecordHolder()
279 {
280 release();
281 }
282
283 ListenerRecord* obj()
284 {
285 return held;
286 }
287
288 ListenerRecordHolder &operator=(const ListenerRecordHolder &that)
289 {
290 safe_assign(that.held);
291 return *this;
292 }
293private:
294 ListenerRecord* held;
295
296 void addref()
297 {
298 if (held)
299 held->addRef();
300 }
301 void release()
302 {
303 if (held)
304 held->release();
305 }
306 void safe_assign (ListenerRecord *that_p)
307 {
308 if (that_p)
309 that_p->addRef();
310 release();
311 held = that_p;
312 }
313};
314
315typedef std::map<IEventListener*, ListenerRecordHolder> Listeners;
316
317struct EventSource::Data
318{
319 Data() {}
320 Listeners mListeners;
321 EventMap mEvMap;
322 PendingEventsMap mPendingMap;
323};
324
325/**
326 * This function defines what wildcard expands to.
327 */
328static BOOL implies(VBoxEventType_T who, VBoxEventType_T what)
329{
330 switch (who)
331 {
332 case VBoxEventType_Any:
333 return TRUE;
334 case VBoxEventType_MachineEvent:
335 return (what == VBoxEventType_OnMachineStateChange)
336 || (what == VBoxEventType_OnMachineDataChange)
337 || (what == VBoxEventType_OnMachineRegistered)
338 || (what == VBoxEventType_OnSessionStateChange)
339 || (what == VBoxEventType_OnGuestPropertyChange);
340 case VBoxEventType_SnapshotEvent:
341 return (what == VBoxEventType_OnSnapshotTaken)
342 || (what == VBoxEventType_OnSnapshotDeleted)
343 || (what == VBoxEventType_OnSnapshotChange)
344 ;
345 case VBoxEventType_Invalid:
346 return FALSE;
347 }
348 return who == what;
349}
350
351ListenerRecord::ListenerRecord(IEventListener* aListener,
352 com::SafeArray<VBoxEventType_T>& aInterested,
353 BOOL aActive,
354 EventSource* aOwner)
355 :
356 mActive(aActive),
357 mOwner(aOwner),
358 mRefCnt(0)
359{
360 mListener = aListener;
361 EventMap* aEvMap = &aOwner->m->mEvMap;
362
363 for (size_t i = 0; i < aInterested.size(); ++i)
364 {
365 VBoxEventType_T interested = aInterested[i];
366 for (int j = FirstEvent; j < LastEvent; j++)
367 {
368 VBoxEventType_T candidate = (VBoxEventType_T)j;
369 if (implies(interested, candidate))
370 {
371 (*aEvMap)[j - FirstEvent].push_back(this);
372 }
373 }
374 }
375
376 if (!mActive)
377 {
378 ::RTCritSectInit(&mcsQLock);
379 ::RTSemEventCreate (&mQEvent);
380 }
381}
382
383ListenerRecord::~ListenerRecord()
384{
385 /* Remove references to us from the event map */
386 EventMap* aEvMap = &mOwner->m->mEvMap;
387 for (int j = FirstEvent; j < LastEvent; j++)
388 {
389 (*aEvMap)[j - FirstEvent].remove(this);
390 }
391
392 if (!mActive)
393 {
394 ::RTCritSectDelete(&mcsQLock);
395 ::RTSemEventDestroy(mQEvent);
396 }
397}
398
399HRESULT ListenerRecord::process(IEvent* aEvent, BOOL aWaitable, PendingEventsMap::iterator& pit)
400{
401 if (mActive)
402 {
403 HRESULT rc = mListener->HandleEvent(aEvent);
404 if (aWaitable)
405 eventProcessed(aEvent, pit);
406 return rc;
407 }
408 else
409 return enqueue(aEvent);
410}
411
412
413HRESULT ListenerRecord::enqueue (IEvent* aEvent)
414{
415 AssertMsg(!mActive, ("must be passive\n"));
416 // put an event the queue
417 ::RTCritSectEnter(&mcsQLock);
418 mQueue.push_back(aEvent);
419 ::RTCritSectLeave(&mcsQLock);
420
421 // notify waiters
422 ::RTSemEventSignal(mQEvent);
423
424 return S_OK;
425}
426
427HRESULT ListenerRecord::dequeue (IEvent* *aEvent, LONG aTimeout)
428{
429 AssertMsg(!mActive, ("must be passive\n"));
430
431 ::RTCritSectEnter(&mcsQLock);
432 if (mQueue.empty())
433 {
434 ::RTCritSectLeave(&mcsQLock);
435 // Speed up common case
436 if (aTimeout == 0)
437 {
438 *aEvent = NULL;
439 return S_OK;
440 }
441 ::RTSemEventWait(mQEvent, aTimeout);
442 ::RTCritSectEnter(&mcsQLock);
443 }
444 if (mQueue.empty())
445 {
446 *aEvent = NULL;
447 }
448 else
449 {
450 mQueue.front().queryInterfaceTo(aEvent);
451 mQueue.pop_front();
452 }
453 ::RTCritSectLeave(&mcsQLock);
454 return S_OK;
455}
456
457HRESULT ListenerRecord::eventProcessed (IEvent* aEvent, PendingEventsMap::iterator& pit)
458{
459 if (--pit->second == 0)
460 {
461 Assert(pit->first == aEvent);
462 aEvent->SetProcessed();
463 mOwner->m->mPendingMap.erase(pit);
464 }
465
466 Assert(pit->second >= 0);
467 return S_OK;
468}
469
470EventSource::EventSource()
471{}
472
473EventSource::~EventSource()
474{}
475
476HRESULT EventSource::FinalConstruct()
477{
478 m = new Data;
479 return S_OK;
480}
481
482void EventSource::FinalRelease()
483{
484 uninit();
485 delete m;
486}
487
488HRESULT EventSource::init(IUnknown *)
489{
490 HRESULT rc = S_OK;
491
492 AutoInitSpan autoInitSpan(this);
493 AssertReturn(autoInitSpan.isOk(), E_FAIL);
494
495 /* Confirm a successful initialization */
496 autoInitSpan.setSucceeded();
497 return rc;
498}
499
500void EventSource::uninit()
501{
502 m->mListeners.clear();
503 // m->mEvMap shall be cleared at this point too by destructors
504}
505
506STDMETHODIMP EventSource::RegisterListener(IEventListener * aListener,
507 ComSafeArrayIn(VBoxEventType_T, aInterested),
508 BOOL aActive)
509{
510 CheckComArgNotNull(aListener);
511 CheckComArgSafeArrayNotNull(aInterested);
512
513 AutoCaller autoCaller(this);
514 if (FAILED(autoCaller.rc())) return autoCaller.rc();
515
516 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
517
518 Listeners::const_iterator it = m->mListeners.find(aListener);
519 if (it != m->mListeners.end())
520 return setError(E_INVALIDARG,
521 tr("This listener already registered"));
522
523 com::SafeArray<VBoxEventType_T> interested(ComSafeArrayInArg (aInterested));
524 ListenerRecordHolder lrh(new ListenerRecord(aListener, interested, aActive, this));
525 m->mListeners.insert(Listeners::value_type(aListener, lrh));
526
527 return S_OK;
528}
529
530STDMETHODIMP EventSource::UnregisterListener(IEventListener * aListener)
531{
532 CheckComArgNotNull(aListener);
533
534 AutoCaller autoCaller(this);
535 if (FAILED(autoCaller.rc())) return autoCaller.rc();
536
537 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
538
539 Listeners::iterator it = m->mListeners.find(aListener);
540 HRESULT rc;
541
542 if (it != m->mListeners.end())
543 {
544 m->mListeners.erase(it);
545 // destructor removes refs from the event map
546 rc = S_OK;
547 }
548 else
549 {
550 rc = setError(VBOX_E_OBJECT_NOT_FOUND,
551 tr("Listener was never registered"));
552 }
553
554 return rc;
555}
556
557STDMETHODIMP EventSource::FireEvent(IEvent * aEvent,
558 LONG aTimeout,
559 BOOL *aProcessed)
560{
561 CheckComArgNotNull(aEvent);
562 CheckComArgOutPointerValid(aProcessed);
563
564 AutoCaller autoCaller(this);
565 if (FAILED(autoCaller.rc())) return autoCaller.rc();
566
567 HRESULT hrc;
568 BOOL aWaitable = FALSE;
569 aEvent->COMGETTER(Waitable)(&aWaitable);
570
571 {
572 /* See comment in EventSource::GetEvent() */
573 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
574
575 VBoxEventType_T evType;
576 hrc = aEvent->COMGETTER(Type)(&evType);
577 AssertComRCReturn(hrc, VERR_ACCESS_DENIED);
578
579 std::list<ListenerRecord*>& listeners = m->mEvMap[(int)evType-FirstEvent];
580
581 uint32_t cListeners = listeners.size();
582 PendingEventsMap::iterator pit;
583
584 if (cListeners > 0 && aWaitable)
585 {
586 m->mPendingMap.insert(PendingEventsMap::value_type(aEvent, cListeners));
587 // we keep iterator here to allow processing active listeners without
588 // pending events lookup
589 pit = m->mPendingMap.find(aEvent);
590 }
591 for(std::list<ListenerRecord*>::const_iterator it = listeners.begin();
592 it != listeners.end(); ++it)
593 {
594 ListenerRecord* record = *it;
595 HRESULT cbRc;
596
597 // @todo: callback under (read) lock, is it good?
598 // We could make copy of per-event listener's list, but real issue
599 // is that we don't want to allow removal of a listener while iterating
600 cbRc = record->process(aEvent, aWaitable, pit);
601 // what to do with cbRc?
602 }
603 }
604 /* We leave the lock here */
605
606 if (aWaitable)
607 hrc = aEvent->WaitProcessed(aTimeout, aProcessed);
608 else
609 *aProcessed = TRUE;
610
611 return hrc;
612}
613
614
615STDMETHODIMP EventSource::GetEvent(IEventListener * aListener,
616 LONG aTimeout,
617 IEvent ** aEvent)
618{
619
620 CheckComArgNotNull(aListener);
621
622 AutoCaller autoCaller(this);
623 if (FAILED(autoCaller.rc())) return autoCaller.rc();
624
625 /**
626 * There's subtle dependency between this lock and one in FireEvent():
627 * we need to be able to access event queue in FireEvent() while waiting
628 * here, to make this wait preemptible, thus both take read lock (write
629 * lock in FireEvent() would do too, and probably is a bit stricter),
630 * but will interfere with .
631 */
632 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
633
634 Listeners::iterator it = m->mListeners.find(aListener);
635 HRESULT rc;
636
637 if (it != m->mListeners.end())
638 rc = it->second.obj()->dequeue(aEvent, aTimeout);
639 else
640 rc = setError(VBOX_E_OBJECT_NOT_FOUND,
641 tr("Listener was never registered"));
642
643 return rc;
644}
645
646STDMETHODIMP EventSource::EventProcessed(IEventListener * aListener,
647 IEvent * aEvent)
648{
649 CheckComArgNotNull(aListener);
650 CheckComArgNotNull(aEvent);
651
652 AutoCaller autoCaller(this);
653 if (FAILED(autoCaller.rc())) return autoCaller.rc();
654
655 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
656
657 Listeners::iterator it = m->mListeners.find(aListener);
658 HRESULT rc;
659
660 BOOL aWaitable = FALSE;
661 aEvent->COMGETTER(Waitable)(&aWaitable);
662
663 if (it != m->mListeners.end())
664 {
665 ListenerRecord* aRecord = it->second.obj();
666
667 if (aRecord->isActive())
668 return setError(E_INVALIDARG,
669 tr("Only applicable to passive listeners"));
670
671 if (aWaitable)
672 {
673 PendingEventsMap::iterator pit = m->mPendingMap.find(aEvent);
674
675 if (pit == m->mPendingMap.end())
676 {
677 AssertFailed();
678 rc = setError(VBOX_E_OBJECT_NOT_FOUND,
679 tr("Unknown event"));
680 }
681 else
682 rc = aRecord->eventProcessed(aEvent, pit);
683 }
684 else
685 {
686 // for non-waitable events we're done
687 rc = S_OK;
688 }
689 }
690 else
691 {
692 rc = setError(VBOX_E_OBJECT_NOT_FOUND,
693 tr("Listener was never registered"));
694 }
695
696 return rc;
697}
698
699/**
700 * This class serves as feasible listener implementation
701 * which could be used by clients not able to create local
702 * COM objects, but still willing to recieve event
703 * notifications in passive mode, such as webservices.
704 */
705class ATL_NO_VTABLE PassiveEventListener :
706 public VirtualBoxBase,
707 public VirtualBoxSupportErrorInfoImpl<PassiveEventListener, IEventListener>,
708 public VirtualBoxSupportTranslation<PassiveEventListener>,
709 VBOX_SCRIPTABLE_IMPL(IEventListener)
710{
711public:
712
713 VIRTUALBOXBASE_ADD_ERRORINFO_SUPPORT(PassiveEventListener)
714
715 DECLARE_NOT_AGGREGATABLE(PassiveEventListener)
716
717 DECLARE_PROTECT_FINAL_CONSTRUCT()
718
719 BEGIN_COM_MAP(PassiveEventListener)
720 COM_INTERFACE_ENTRY(ISupportErrorInfo)
721 COM_INTERFACE_ENTRY(IEventListener)
722 COM_INTERFACE_ENTRY(IDispatch)
723 END_COM_MAP()
724
725 PassiveEventListener()
726 {}
727 ~PassiveEventListener()
728 {}
729
730 HRESULT FinalConstruct()
731 {
732 return S_OK;
733 }
734 void FinalRelease()
735 {}
736
737 // IEventListener methods
738 STDMETHOD(HandleEvent)(IEvent *)
739 {
740 ComAssertMsgRet(false, ("HandleEvent() of wrapper shall never be called"),
741 E_FAIL);
742 }
743 // for VirtualBoxSupportErrorInfoImpl
744 static const wchar_t *getComponentName() { return L"PassiveEventListener"; }
745};
746
747#ifdef VBOX_WITH_XPCOM
748NS_DECL_CLASSINFO(PassiveEventListener)
749NS_IMPL_THREADSAFE_ISUPPORTS1_CI(PassiveEventListener, IEventListener)
750#endif
751
752STDMETHODIMP EventSource::CreateListener(IEventListener ** aListener)
753{
754 CheckComArgOutPointerValid(aListener);
755
756 AutoCaller autoCaller(this);
757 if (FAILED(autoCaller.rc())) return autoCaller.rc();
758
759 ComObjPtr<PassiveEventListener> listener;
760
761 HRESULT rc = listener.createObject();
762 ComAssertMsgRet(SUCCEEDED(rc), ("Could not create wrapper object (%Rrc)", rc),
763 E_FAIL);
764 listener.queryInterfaceTo(aListener);
765 return S_OK;
766}
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