VirtualBox

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

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

Main: more events

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.7 KB
Line 
1/* $Id: EventImpl.cpp 30345 2010-06-21 16:49:59Z 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.215389.xyz. 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 <VBox/com/array.h>
29
30struct VBoxEvent::Data
31{
32 Data()
33 :
34 mType(VBoxEventType_Invalid),
35 mWaitEvent(NIL_RTSEMEVENT),
36 mWaitable(FALSE),
37 mProcessed(FALSE)
38 {}
39 ComPtr<IEventSource> mSource;
40 VBoxEventType_T mType;
41 RTSEMEVENT mWaitEvent;
42 BOOL mWaitable;
43 BOOL mProcessed;
44};
45
46HRESULT VBoxEvent::FinalConstruct()
47{
48 m = new Data;
49 return S_OK;
50}
51
52void VBoxEvent::FinalRelease()
53{
54 uninit();
55 delete m;
56}
57
58
59HRESULT VBoxEvent::init(IEventSource *aSource, VBoxEventType_T aType, BOOL aWaitable)
60{
61 HRESULT rc = S_OK;
62
63 AssertReturn(aSource != NULL, E_INVALIDARG);
64
65 AutoInitSpan autoInitSpan(this);
66 AssertReturn(autoInitSpan.isOk(), E_FAIL);
67
68 m->mSource = aSource;
69 m->mType = aType;
70 m->mWaitable = aWaitable;
71 m->mProcessed = !aWaitable;
72
73 do {
74 if (aWaitable)
75 {
76 int vrc = ::RTSemEventCreate (&m->mWaitEvent);
77
78 if (RT_FAILURE(vrc))
79 {
80 AssertFailed ();
81 return setError(E_FAIL,
82 tr("Internal error (%Rrc)"), vrc);
83 }
84 }
85 } while (0);
86
87 /* Confirm a successful initialization */
88 autoInitSpan.setSucceeded();
89
90 return rc;
91}
92
93void VBoxEvent::uninit()
94{
95 m->mProcessed = TRUE;
96 m->mType = VBoxEventType_Invalid;
97 m->mSource.setNull();
98
99 if (m->mWaitEvent != NIL_RTSEMEVENT)
100 {
101 ::RTSemEventDestroy(m->mWaitEvent);
102 }
103}
104
105STDMETHODIMP VBoxEvent::COMGETTER(Type)(VBoxEventType_T *aType)
106{
107 CheckComArgNotNull(aType);
108
109 AutoCaller autoCaller(this);
110 if (FAILED(autoCaller.rc())) return autoCaller.rc();
111
112 // never changes till event alive, no locking?
113 *aType = m->mType;
114 return S_OK;
115}
116
117STDMETHODIMP VBoxEvent::COMGETTER(Source)(IEventSource* *aSource)
118{
119 CheckComArgOutPointerValid(aSource);
120
121 AutoCaller autoCaller(this);
122 if (FAILED(autoCaller.rc())) return autoCaller.rc();
123
124 m->mSource.queryInterfaceTo(aSource);
125 return S_OK;
126}
127
128STDMETHODIMP VBoxEvent::COMGETTER(Waitable)(BOOL *aWaitable)
129{
130 CheckComArgNotNull(aWaitable);
131
132 AutoCaller autoCaller(this);
133 if (FAILED(autoCaller.rc())) return autoCaller.rc();
134
135 // never changes till event alive, no locking?
136 *aWaitable = m->mWaitable;
137 return S_OK;
138}
139
140
141STDMETHODIMP VBoxEvent::SetProcessed()
142{
143 AutoCaller autoCaller(this);
144 if (FAILED(autoCaller.rc())) return autoCaller.rc();
145
146 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
147
148 if (m->mProcessed)
149 return S_OK;
150
151 m->mProcessed = TRUE;
152
153 // notify waiters
154 ::RTSemEventSignal(m->mWaitEvent);
155
156 return S_OK;
157}
158
159STDMETHODIMP VBoxEvent::WaitProcessed(LONG aTimeout, BOOL *aResult)
160{
161 CheckComArgNotNull(aResult);
162
163 AutoCaller autoCaller(this);
164 if (FAILED(autoCaller.rc())) return autoCaller.rc();
165
166 {
167 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
168
169 if (m->mProcessed)
170 return S_OK;
171 }
172
173 int vrc = ::RTSemEventWait(m->mWaitEvent, aTimeout);
174 AssertMsg(RT_SUCCESS(vrc) || vrc == VERR_TIMEOUT || vrc == VERR_INTERRUPTED,
175 ("RTSemEventWait returned %Rrc\n", vrc));
176
177
178 if (RT_SUCCESS(vrc))
179 {
180 AssertMsg(m->mProcessed,
181 ("mProcessed must be set here\n"));
182 *aResult = m->mProcessed;
183 }
184 else
185 {
186 *aResult = FALSE;
187 }
188
189 return S_OK;
190}
191
192static const int FirstEvent = (int)VBoxEventType_LastWildcard + 1;
193static const int LastEvent = (int)VBoxEventType_Last;
194static const int NumEvents = LastEvent - FirstEvent;
195
196struct ListenerRecord;
197typedef std::list<ListenerRecord*> EventMap[NumEvents];
198typedef std::map<IEvent*, int32_t> PendingEventsMap;
199typedef std::deque<ComPtr<IEvent> > PassiveQueue;
200
201struct ListenerRecord
202{
203 ComPtr<IEventListener> mListener;
204 BOOL mActive;
205 EventSource* mOwner;
206
207 RTSEMEVENT mQEvent;
208 RTCRITSECT mcsQLock;
209 PassiveQueue mQueue;
210
211 ListenerRecord(IEventListener* aListener,
212 com::SafeArray<VBoxEventType_T>& aInterested,
213 BOOL aActive,
214 EventSource* aOwner);
215 ~ListenerRecord();
216
217 HRESULT process(IEvent* aEvent, BOOL aWaitable, PendingEventsMap::iterator& pit);
218 HRESULT enqueue(IEvent* aEvent);
219 HRESULT dequeue(IEvent* *aEvent, LONG aTimeout);
220 HRESULT eventProcessed(IEvent * aEvent, PendingEventsMap::iterator& pit);
221};
222
223typedef std::map<IEventListener*, ListenerRecord> Listeners;
224
225struct EventSource::Data
226{
227 Data() {}
228 Listeners mListeners;
229 EventMap mEvMap;
230 PendingEventsMap mPendingMap;
231};
232
233static BOOL implies(VBoxEventType_T who, VBoxEventType_T what)
234{
235 switch (who)
236 {
237 case VBoxEventType_Any:
238 return TRUE;
239 case VBoxEventType_MachineEvent:
240 return (what == VBoxEventType_OnMachineStateChange)
241 || (what == VBoxEventType_OnMachineDataChange)
242 || (what == VBoxEventType_OnMachineRegistered);
243 case VBoxEventType_Invalid:
244 return FALSE;
245 }
246 return who == what;
247}
248
249ListenerRecord::ListenerRecord(IEventListener* aListener,
250 com::SafeArray<VBoxEventType_T>& aInterested,
251 BOOL aActive,
252 EventSource* aOwner)
253 :
254 mActive(aActive),
255 mOwner(aOwner)
256{
257 mListener = aListener;
258 EventMap* aEvMap = &aOwner->m->mEvMap;
259
260 for (size_t i = 0; i < aInterested.size(); ++i)
261 {
262 VBoxEventType_T interested = aInterested[i];
263 for (int j = FirstEvent; j < LastEvent; j++)
264 {
265 VBoxEventType_T candidate = (VBoxEventType_T)j;
266 if (implies(interested, candidate))
267 {
268 (*aEvMap)[j - FirstEvent].push_back(this);
269 }
270 }
271 }
272
273 if (!mActive)
274 {
275 ::RTCritSectInitEx(&mcsQLock, 0, NIL_RTLOCKVALCLASS, RTLOCKVAL_SUB_CLASS_ANY, NULL);
276 ::RTSemEventCreate (&mQEvent);
277 }
278}
279
280ListenerRecord::~ListenerRecord()
281{
282 /* Remove references to us from the event map */
283 EventMap* aEvMap = &mOwner->m->mEvMap;
284 for (int j = FirstEvent; j < LastEvent; j++)
285 {
286 (*aEvMap)[j - FirstEvent].remove(this);
287 }
288
289 if (!mActive)
290 {
291 ::RTCritSectDelete(&mcsQLock);
292 ::RTSemEventDestroy(mQEvent);
293 }
294}
295
296HRESULT ListenerRecord::process(IEvent* aEvent, BOOL aWaitable, PendingEventsMap::iterator& pit)
297{
298 if (mActive)
299 {
300 HRESULT rc = mListener->HandleEvent(aEvent);
301 if (aWaitable)
302 eventProcessed(aEvent, pit);
303 return rc;
304 }
305 else
306 return enqueue(aEvent);
307}
308
309
310HRESULT ListenerRecord::enqueue (IEvent* aEvent)
311{
312 AssertMsg(!mActive, ("must be passive\n"));
313 ::RTCritSectEnter(&mcsQLock);
314
315 mQueue.push_back(aEvent);
316 // notify waiters
317 ::RTSemEventSignal(mQEvent);
318
319 ::RTCritSectLeave(&mcsQLock);
320
321 return S_OK;
322}
323
324HRESULT ListenerRecord::dequeue (IEvent* *aEvent, LONG aTimeout)
325{
326 AssertMsg(!mActive, ("must be passive\n"));
327
328 ::RTCritSectEnter(&mcsQLock);
329 if (mQueue.empty())
330 {
331 ::RTCritSectLeave(&mcsQLock);
332 ::RTSemEventWait(mQEvent, aTimeout);
333 ::RTCritSectEnter(&mcsQLock);
334 }
335 if (mQueue.empty())
336 {
337 *aEvent = NULL;
338 }
339 else
340 {
341 mQueue.front().queryInterfaceTo(aEvent);
342 mQueue.pop_front();
343 }
344 ::RTCritSectLeave(&mcsQLock);
345 return S_OK;
346}
347
348HRESULT ListenerRecord::eventProcessed (IEvent* aEvent, PendingEventsMap::iterator& pit)
349{
350 if (--pit->second == 0)
351 {
352 aEvent->SetProcessed();
353 mOwner->m->mPendingMap.erase(pit);
354 }
355
356 Assert(pit->second >= 0);
357 return S_OK;
358}
359
360
361EventSource::EventSource()
362{}
363
364EventSource::~EventSource()
365{}
366
367HRESULT EventSource::FinalConstruct()
368{
369 m = new Data;
370 return S_OK;
371}
372
373void EventSource::FinalRelease()
374{
375 uninit();
376 delete m;
377}
378
379HRESULT EventSource::init(IUnknown * aParent)
380{
381 HRESULT rc = S_OK;
382
383 AutoInitSpan autoInitSpan(this);
384 AssertReturn(autoInitSpan.isOk(), E_FAIL);
385
386 /* Confirm a successful initialization */
387 autoInitSpan.setSucceeded();
388 return rc;
389}
390
391void EventSource::uninit()
392{
393 m->mListeners.clear();
394 // m->mEvMap shall be cleared at this point too by destructors
395}
396
397STDMETHODIMP EventSource::CreateListener(IEventListener ** aListener)
398{
399 CheckComArgOutPointerValid(aListener);
400
401 AutoCaller autoCaller(this);
402 if (FAILED(autoCaller.rc())) return autoCaller.rc();
403
404 ComPtr<IEventListener> listener;
405
406 HRESULT rc = listener.createLocalObject(CLSID_CallbackWrapper);
407 ComAssertMsgRet(SUCCEEDED(rc), ("Could not create wrapper object (%Rrc)", rc),
408 E_FAIL);
409
410 listener.queryInterfaceTo(aListener);
411 return S_OK;
412}
413
414STDMETHODIMP EventSource::RegisterListener(IEventListener * aListener,
415 ComSafeArrayIn(VBoxEventType_T, aInterested),
416 BOOL aActive)
417{
418 CheckComArgNotNull(aListener);
419 CheckComArgSafeArrayNotNull(aInterested);
420
421 AutoCaller autoCaller(this);
422 if (FAILED(autoCaller.rc())) return autoCaller.rc();
423
424 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
425
426 Listeners::const_iterator it = m->mListeners.find(aListener);
427 if (it != m->mListeners.end())
428 return setError(E_INVALIDARG,
429 tr("This listener already registered"));
430
431 com::SafeArray<VBoxEventType_T> interested(ComSafeArrayInArg (aInterested));
432 m->mListeners.insert(
433 Listeners::value_type(aListener,
434 ListenerRecord(aListener, interested, aActive, this))
435 );
436
437 return S_OK;
438}
439
440STDMETHODIMP EventSource::UnregisterListener(IEventListener * aListener)
441{
442 CheckComArgNotNull(aListener);
443
444 AutoCaller autoCaller(this);
445 if (FAILED(autoCaller.rc())) return autoCaller.rc();
446
447 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
448
449 Listeners::iterator it = m->mListeners.find(aListener);
450 HRESULT rc;
451
452 if (it != m->mListeners.end())
453 {
454 m->mListeners.erase(it);
455 // destructor removes refs from the event map
456 rc = S_OK;
457 }
458 else
459 {
460 rc = setError(VBOX_E_OBJECT_NOT_FOUND,
461 tr("Listener was never registered"));
462 }
463
464 return rc;
465}
466
467STDMETHODIMP EventSource::FireEvent(IEvent * aEvent,
468 LONG aTimeout,
469 BOOL *aProcessed)
470{
471 CheckComArgNotNull(aEvent);
472 CheckComArgOutPointerValid(aProcessed);
473
474 AutoCaller autoCaller(this);
475 if (FAILED(autoCaller.rc())) return autoCaller.rc();
476
477 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
478
479 VBoxEventType_T evType;
480 HRESULT hrc = aEvent->COMGETTER(Type)(&evType);
481 AssertComRCReturn(hrc, VERR_ACCESS_DENIED);
482
483 BOOL aWaitable = FALSE;
484 aEvent->COMGETTER(Waitable)(&aWaitable);
485
486 std::list<ListenerRecord*>& listeners = m->mEvMap[(int)evType-FirstEvent];
487
488 uint32_t cListeners = listeners.size();
489 PendingEventsMap::iterator pit;
490
491 if (cListeners > 0 && aWaitable)
492 {
493 m->mPendingMap.insert(PendingEventsMap::value_type(aEvent, cListeners));
494 // we keep it here to allow processing active listeners without pending events lookup
495 pit = m->mPendingMap.find(aEvent);
496 }
497 for(std::list<ListenerRecord*>::const_iterator it = listeners.begin();
498 it != listeners.end(); ++it)
499 {
500 ListenerRecord* record = *it;
501 HRESULT cbRc;
502
503 // @todo: callback under (read) lock, is it good?
504 cbRc = record->process(aEvent, aWaitable, pit);
505 // what to do with cbRc?
506 }
507
508 if (aWaitable)
509 hrc = aEvent->WaitProcessed(aTimeout, aProcessed);
510 else
511 *aProcessed = TRUE;
512
513 return hrc;
514}
515
516
517STDMETHODIMP EventSource::GetEvent(IEventListener * aListener,
518 LONG aTimeout,
519 IEvent * *aEvent)
520{
521
522 CheckComArgNotNull(aListener);
523
524 AutoCaller autoCaller(this);
525 if (FAILED(autoCaller.rc())) return autoCaller.rc();
526
527 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
528
529 Listeners::iterator it = m->mListeners.find(aListener);
530 HRESULT rc;
531
532 if (it != m->mListeners.end())
533 rc = it->second.dequeue(aEvent, aTimeout);
534 else
535 rc = setError(VBOX_E_OBJECT_NOT_FOUND,
536 tr("Listener was never registered"));
537
538 return rc;
539}
540
541STDMETHODIMP EventSource::EventProcessed(IEventListener * aListener,
542 IEvent * aEvent)
543{
544 CheckComArgNotNull(aListener);
545 CheckComArgNotNull(aEvent);
546
547 AutoCaller autoCaller(this);
548 if (FAILED(autoCaller.rc())) return autoCaller.rc();
549
550 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
551
552 Listeners::iterator it = m->mListeners.find(aListener);
553 HRESULT rc;
554
555 BOOL aWaitable = FALSE;
556 aEvent->COMGETTER(Waitable)(&aWaitable);
557
558 if (it != m->mListeners.end())
559 {
560 ListenerRecord& aRecord = it->second;
561
562 if (aRecord.mActive)
563 return setError(E_INVALIDARG,
564 tr("Only applicable to passive listeners"));
565
566 if (aWaitable)
567 {
568 PendingEventsMap::iterator pit = m->mPendingMap.find(aEvent);
569
570 if (pit == m->mPendingMap.end())
571 {
572 AssertFailed();
573 rc = setError(VBOX_E_OBJECT_NOT_FOUND,
574 tr("Unknown event"));
575 }
576 else
577 rc = aRecord.eventProcessed(aEvent, pit);
578 }
579 else
580 {
581 // for non-waitable events we're done
582 rc = S_OK;
583 }
584 }
585 else
586 {
587 rc = setError(VBOX_E_OBJECT_NOT_FOUND,
588 tr("Listener was never registered"));
589 }
590
591 return rc;
592}
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