EventImpl.cpp revision 3539851da787ca6f987a1e28dd2e7faedb68065b
/* $Id$ */
/** @file
* VirtualBox COM Event class implementation
*/
/*
* Copyright (C) 2010 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
/** @page pg_main_events Events
*
* Theory of operations.
*
* This code implements easily extensible event mechanism, letting us
* to make any VirtualBox object an event source (by aggregating an EventSource instance).
* Another entity could subscribe to the event source for events it is interested in.
* If an event is waitable, it's possible to wait until all listeners
* registered at the moment of firing event as ones interested in this
* event acknowledged that they finished event processing (thus allowing
* vetoable events).
*
* Listeners can be registered as active or passive ones, defining policy of delivery.
* For *active* listeners, their HandleEvent() method is invoked when event is fired by
* the event source (pretty much callbacks).
* For *passive* listeners, it's up to an event consumer to perform GetEvent() operation
* with given listener, and then perform desired operation with returned event, if any.
* For passive listeners case, listener instance serves as merely a key referring to
* particular event consumer, thus HandleEvent() implementation isn't that important.
* IEventSource's CreateListener() could be used to create such a listener.
* Passive mode is designed for transports not allowing callbacks, such as webservices
* running on top of HTTP, and for situations where consumer wants exact control on
* context where event handler is executed (such as GUI thread for some toolkits).
*
* Internal EventSource data structures are optimized for fast event delivery, while
* listener registration/unregistration operations are expected being pretty rare.
* Passive mode listeners keep an internal event queue for all events they receive,
* and all waitable events are addded to the pending events map. This map keeps track
* of how many listeners are still not acknowledged their event, and once this counter
* reach zero, element is removed from pending events map, and event is marked as processed.
* Thus if passive listener's user forgets to call IEventSource's EventProcessed()
* waiters may never know that event processing finished.
*/
#include <list>
#include <map>
#include <deque>
#include "EventImpl.h"
#include "AutoCaller.h"
#include "Logging.h"
#include <iprt/semaphore.h>
#include <iprt/critsect.h>
class ListenerRecord;
{
Data()
{}
};
{
m = new Data;
return S_OK;
}
void VBoxEvent::FinalRelease()
{
if (m)
{
uninit();
delete m;
m = 0;
}
}
{
AutoInitSpan autoInitSpan(this);
m->mProcessed = !aWaitable;
do {
if (aWaitable)
{
if (RT_FAILURE(vrc))
{
AssertFailed ();
}
}
} while (0);
/* Confirm a successful initialization */
return rc;
}
{
if (!m)
return;
m->mProcessed = TRUE;
m->mType = VBoxEventType_Invalid;
if (m->mWaitEvent != NIL_RTSEMEVENT)
{
::RTSemEventDestroy(m->mWaitEvent);
m->mWaitEvent = NIL_RTSEMEVENT;
}
}
{
AutoCaller autoCaller(this);
// never changes till event alive, no locking?
return S_OK;
}
{
AutoCaller autoCaller(this);
return S_OK;
}
{
AutoCaller autoCaller(this);
// never changes till event alive, no locking?
return S_OK;
}
{
AutoCaller autoCaller(this);
if (m->mProcessed)
return S_OK;
m->mProcessed = TRUE;
// notify waiters
::RTSemEventSignal(m->mWaitEvent);
return S_OK;
}
{
AutoCaller autoCaller(this);
{
if (m->mProcessed)
{
return S_OK;
}
if (aTimeout == 0)
{
*aResult = m->mProcessed;
return S_OK;
}
}
/* @todo: maybe while loop for spurious wakeups? */
("RTSemEventWait returned %Rrc\n", vrc));
if (RT_SUCCESS(vrc))
{
AssertMsg(m->mProcessed,
("mProcessed must be set here\n"));
*aResult = m->mProcessed;
}
else
{
}
return S_OK;
}
struct VBoxVetoEvent::Data
{
Data()
:
{}
};
{
m = new Data;
return S_OK;
}
void VBoxVetoEvent::FinalRelease()
{
if (m)
{
uninit();
delete m;
m = 0;
}
}
{
// all veto events are waitable
return rc;
}
void VBoxVetoEvent::uninit()
{
if (!m)
return;
}
{
AutoCaller autoCaller(this);
if (aVeto)
return S_OK;
}
{
AutoCaller autoCaller(this);
return S_OK;
}
{
if (ComSafeArrayOutIsNull(aVetos))
return E_POINTER;
AutoCaller autoCaller(this);
int i = 0;
++it, ++i)
{
}
return S_OK;
}
static const int LastEvent = (int)VBoxEventType_Last;
/**
* Class replacing std::list and able to provide required stability
* during iteration. It's acheived by delaying structural modifications
* to the list till the moment particular element is no longer used by
* current iterators.
*/
class EventMapRecord
{
public:
/**
* We have to be double linked, as structural modifications in list are delayed
* till element removed, so we have to know our previous one to update its next
*/
bool mAlive;
private:
public:
:
mNext(0),
mAlive(true),
mPrev(0),
mRefCnt(1)
{}
{
}
{
if (mNext)
if (mPrev)
}
void addRef()
{
}
void release()
{
if (ASMAtomicDecS32(&mRefCnt) <= 0) delete this;
}
// Called when an element is no longer needed
void kill()
{
mAlive = false;
release();
}
{
}
friend class EventMapList;
};
class EventMapList
{
public:
:
mHead(0),
mSize(0)
{}
~EventMapList()
{
while (aCur)
{
}
}
/*
* Elements have to be added to the front of the list, to make sure
* that iterators doesn't see newly added listeners, and iteration
* will always complete.
*/
{
if (mHead)
mSize++;
}
/*
* Mark element as removed, actual removal could be delayed until
* all consumers release it too. This helps to keep list stable
* enough for iterators to allow long and probably intrusive callbacks.
*/
{
while (aCur)
{
{
mSize--;
// break?
}
}
}
{
return mSize;
}
struct iterator
{
iterator()
: mCur(0)
{}
explicit
{
// Prevent element removal, till we're at it
if (mCur)
}
~iterator()
{
if (mCur)
}
operator*() const
{
}
operator++()
{
do {
// now we can safely release previous element
// And grab the new current
if (mCur)
return *this;
}
bool
{
}
bool
{
}
};
{
}
{
return iterator(0);
}
};
class ListenerRecord
{
private:
public:
~ListenerRecord();
HRESULT process(IEvent* aEvent, BOOL aWaitable, PendingEventsMap::iterator& pit, AutoLockBase& alock);
void addRef()
{
}
void release()
{
if (ASMAtomicDecS32(&mRefCnt) <= 0) delete this;
}
{
return mActive;
}
friend class EventSource;
};
/* Handy class with semantics close to ComPtr, but for list records */
template<typename Held>
class RecordHolder
{
public:
:
{
addref();
}
:
{
addref();
}
:
held(0)
{
}
~RecordHolder()
{
release();
}
{
return held;
}
{
return *this;
}
private:
void addref()
{
if (held)
}
void release()
{
if (held)
}
{
if (that_p)
release();
}
};
struct EventSource::Data
{
Data() {}
};
/**
* This function defines what wildcard expands to.
*/
{
switch (who)
{
case VBoxEventType_Any:
return TRUE;
return (what == VBoxEventType_OnMachineStateChanged)
|| (what == VBoxEventType_OnGuestPropertyChanged);
return (what == VBoxEventType_OnSnapshotTaken)
|| (what == VBoxEventType_OnSnapshotDeleted)
|| (what == VBoxEventType_OnSnapshotChanged)
;
case VBoxEventType_InputEvent:
return (what == VBoxEventType_OnKeyboardLedsChanged)
;
case VBoxEventType_Invalid:
return FALSE;
default:
}
}
:
mRefCnt(0)
{
{
for (int j = FirstEvent; j < LastEvent; j++)
{
{
}
}
}
if (!mActive)
{
::RTCritSectInit(&mcsQLock);
::RTSemEventCreate (&mQEvent);
mLastRead = RTTimeMilliTS();
}
}
{
/* Remove references to us from the event map */
for (int j = FirstEvent; j < LastEvent; j++)
{
}
if (!mActive)
{
// at this moment nobody could add elements to our queue, so we can safely
// clean it up, otherwise there will be pending events map elements
while (true)
{
break;
if (aWaitable)
{
}
}
::RTCritSectDelete(&mcsQLock);
}
}
{
if (mActive)
{
/*
* We release lock here to allow modifying ops on EventSource inside callback.
*/
if (mListener)
{
}
if (aWaitable)
return rc;
}
else
}
{
// put an event the queue
::RTCritSectEnter(&mcsQLock);
// If there was no events reading from the listener for the long time,
// and events keep coming we shall unregister it.
{
::RTCritSectLeave(&mcsQLock);
return E_ABORT;
}
::RTCritSectLeave(&mcsQLock);
// notify waiters
return S_OK;
}
{
// retain listener record
::RTCritSectEnter(&mcsQLock);
mLastRead = RTTimeMilliTS();
::RTCritSectLeave(&mcsQLock);
// Speed up common case
if (aTimeout == 0)
{
return S_OK;
}
// release lock while waiting, listener will not go away due to above holder
// reacquire lock
::RTCritSectEnter(&mcsQLock);
}
{
}
else
{
}
::RTCritSectLeave(&mcsQLock);
return S_OK;
}
{
{
aEvent->SetProcessed();
}
return S_OK;
}
{}
{}
{
m = new Data;
return S_OK;
}
void EventSource::FinalRelease()
{
uninit();
delete m;
}
{
AutoInitSpan autoInitSpan(this);
/* Confirm a successful initialization */
return rc;
}
void EventSource::uninit()
{
AutoUninitSpan autoUninitSpan(this);
if (autoUninitSpan.uninitDone())
return;
m->mListeners.clear();
// m->mEvMap shall be cleared at this point too by destructors, assert?
}
{
AutoCaller autoCaller(this);
{
return setError(E_INVALIDARG,
tr("This listener already registered"));
}
return S_OK;
}
{
AutoCaller autoCaller(this);
{
{
// destructor removes refs from the event map
}
else
{
tr("Listener was never registered"));
}
}
{
}
return rc;
}
{
AutoCaller autoCaller(this);
do {
/* Anyone interested in this event? */
if (cListeners == 0)
{
aEvent->SetProcessed();
break; // just leave the lock and update event object state
}
if (aWaitable)
{
// we keep iterator here to allow processing active listeners without
// pending events lookup
}
{
// keep listener record reference, in case someone will remove it while in callback
/**
* We pass lock here to allow modifying ops on EventSource inside callback
* in active mode. Note that we expect list iterator stability as 'alock'
* could be temporary released when calling event handler.
*/
{
}
// anything else to do with cbRc?
}
} while (0);
/* We leave the lock here */
if (aWaitable)
else
*aProcessed = TRUE;
return hrc;
}
{
AutoCaller autoCaller(this);
else
tr("Listener was never registered"));
return rc;
}
{
AutoCaller autoCaller(this);
{
return setError(E_INVALIDARG,
tr("Only applicable to passive listeners"));
if (aWaitable)
{
{
AssertFailed();
tr("Unknown event"));
}
else
}
else
{
// for non-waitable events we're done
}
}
else
{
tr("Listener was never registered"));
}
return rc;
}
/**
* This class serves as feasible listener implementation
* which could be used by clients not able to create local
* COM objects, but still willing to receive event
* notifications in passive mode, such as webservices.
*/
class ATL_NO_VTABLE PassiveEventListener :
public VirtualBoxBase,
{
public:
{}
{}
{
return S_OK;
}
void FinalRelease()
{}
// IEventListener methods
{
ComAssertMsgRet(false, ("HandleEvent() of wrapper shall never be called"),
E_FAIL);
}
};
#ifdef VBOX_WITH_XPCOM
#endif
{
AutoCaller autoCaller(this);
E_FAIL);
return S_OK;
}