Refactored event queue. The event queue is now separated from the

buffer that holds the events and generates system events.  This
allows us to switch in/out a platform specific event handler as
necessary without losing our timers and handlers.
This commit is contained in:
crs 2004-02-08 17:07:11 +00:00
parent 3bcdf139a7
commit c44c18bfdc
6 changed files with 114 additions and 289 deletions

View File

@ -13,6 +13,7 @@
*/
#include "CEventQueue.h"
#include "CSimpleEventQueueBuffer.h"
#include "IEventJob.h"
#include "CArch.h"
@ -34,20 +35,42 @@ CEventQueue::CEventQueue()
setInstance(this);
m_mutex = ARCH->newMutex();
ARCH->setInterruptHandler(&interrupt, NULL);
m_buffer = new CSimpleEventQueueBuffer;
}
CEventQueue::~CEventQueue()
{
delete m_buffer;
ARCH->setInterruptHandler(NULL, NULL);
ARCH->closeMutex(m_mutex);
setInstance(NULL);
}
void
CEventQueue::adoptBuffer(IEventQueueBuffer* buffer)
{
CArchMutexLock lock(m_mutex);
// discard old buffer and old events
delete m_buffer;
for (CEventTable::iterator i = m_events.begin(); i != m_events.end(); ++i) {
CEvent::deleteData(i->second);
}
m_events.clear();
m_oldEventIDs.clear();
// use new buffer
m_buffer = buffer;
if (m_buffer == NULL) {
m_buffer = new CSimpleEventQueueBuffer;
}
}
bool
CEventQueue::getEvent(CEvent& event, double timeout)
{
// if no events are waiting then handle timers and then wait
if (doIsEmpty()) {
if (m_buffer->isEmpty()) {
// handle timers first
if (hasTimerExpired(event)) {
return true;
@ -62,15 +85,30 @@ CEventQueue::getEvent(CEvent& event, double timeout)
}
// wait for an event
waitForEvent(timeout);
m_buffer->waitForEvent(timeout);
}
// if no events are pending then do the timers
if (doIsEmpty()) {
if (m_buffer->isEmpty()) {
return hasTimerExpired(event);
}
return doGetEvent(event);
UInt32 dataID;
IEventQueueBuffer::Type type = m_buffer->getEvent(event, dataID);
switch (type) {
case IEventQueueBuffer::kNone:
return false;
case IEventQueueBuffer::kSystem:
return true;
case IEventQueueBuffer::kUser:
{
CArchMutexLock lock(m_mutex);
event = removeEvent(dataID);
return true;
}
}
}
bool
@ -99,11 +137,13 @@ CEventQueue::addEvent(const CEvent& event)
break;
}
CArchMutexLock lock(m_mutex);
// store the event's data locally
UInt32 eventID = saveEvent(event);
// add it
if (!doAddEvent(eventID)) {
if (!m_buffer->addEvent(eventID)) {
// failed to send event
removeEvent(eventID);
CEvent::deleteData(event);
@ -115,7 +155,7 @@ CEventQueue::newTimer(double duration, void* target)
{
assert(duration > 0.0);
CEventQueueTimer* timer = doNewTimer(duration, false);
CEventQueueTimer* timer = m_buffer->newTimer(duration, false);
CArchMutexLock lock(m_mutex);
m_timers.insert(timer);
m_timerQueue.push(CTimer(timer, duration, target, false));
@ -127,7 +167,7 @@ CEventQueue::newOneShotTimer(double duration, void* target)
{
assert(duration > 0.0);
CEventQueueTimer* timer = doNewTimer(duration, true);
CEventQueueTimer* timer = m_buffer->newTimer(duration, true);
CArchMutexLock lock(m_mutex);
m_timers.insert(timer);
m_timerQueue.push(CTimer(timer, duration, target, true));
@ -137,7 +177,6 @@ CEventQueue::newOneShotTimer(double duration, void* target)
void
CEventQueue::deleteTimer(CEventQueueTimer* timer)
{
{
CArchMutexLock lock(m_mutex);
for (CTimerQueue::iterator index = m_timerQueue.begin();
index != m_timerQueue.end(); ++index) {
@ -150,14 +189,12 @@ CEventQueue::deleteTimer(CEventQueueTimer* timer)
if (index != m_timers.end()) {
m_timers.erase(index);
}
}
doDeleteTimer(timer);
m_buffer->deleteTimer(timer);
}
void
CEventQueue::adoptHandler(void* target, IEventJob* handler)
{
CArchMutexLock lock(m_mutex);
doAdoptHandler(CEvent::kUnknown, target, handler);
}
@ -165,14 +202,12 @@ void
CEventQueue::adoptHandler(CEvent::Type type, void* target, IEventJob* handler)
{
assert(type != CEvent::kUnknown);
CArchMutexLock lock(m_mutex);
doAdoptHandler(type, target, handler);
}
IEventJob*
CEventQueue::orphanHandler(void* target)
{
CArchMutexLock lock(m_mutex);
return doOrphanHandler(CEvent::kUnknown, target);
}
@ -180,7 +215,6 @@ IEventJob*
CEventQueue::orphanHandler(CEvent::Type type, void* target)
{
assert(type != CEvent::kUnknown);
CArchMutexLock lock(m_mutex);
return doOrphanHandler(type, target);
}
@ -199,6 +233,7 @@ CEventQueue::removeHandler(CEvent::Type type, void* target)
void
CEventQueue::doAdoptHandler(CEvent::Type type, void* target, IEventJob* handler)
{
CArchMutexLock lock(m_mutex);
IEventJob*& job = m_handlers[CTypeTarget(type, target)];
delete job;
job = handler;
@ -207,6 +242,7 @@ CEventQueue::doAdoptHandler(CEvent::Type type, void* target, IEventJob* handler)
IEventJob*
CEventQueue::doOrphanHandler(CEvent::Type type, void* target)
{
CArchMutexLock lock(m_mutex);
CHandlerTable::iterator index = m_handlers.find(CTypeTarget(type, target));
if (index != m_handlers.end()) {
IEventJob* handler = index->second;
@ -221,7 +257,7 @@ CEventQueue::doOrphanHandler(CEvent::Type type, void* target)
bool
CEventQueue::isEmpty() const
{
return (doIsEmpty() && getNextTimerTimeout() != 0.0);
return (m_buffer->isEmpty() && getNextTimerTimeout() != 0.0);
}
IEventJob*
@ -243,8 +279,6 @@ CEventQueue::getHandler(CEvent::Type type, void* target) const
UInt32
CEventQueue::saveEvent(const CEvent& event)
{
CArchMutexLock lock(m_mutex);
// choose id
UInt32 id;
if (!m_oldEventIDs.empty()) {
@ -265,8 +299,6 @@ CEventQueue::saveEvent(const CEvent& event)
CEvent
CEventQueue::removeEvent(UInt32 eventID)
{
CArchMutexLock lock(m_mutex);
// look up id
CEventTable::iterator index = m_events.find(eventID);
if (index == m_events.end()) {
@ -286,8 +318,6 @@ CEventQueue::removeEvent(UInt32 eventID)
bool
CEventQueue::hasTimerExpired(CEvent& event)
{
CArchMutexLock lock(m_mutex);
// return true if there's a timer in the timer priority queue that
// has expired. if returning true then fill in event appropriately
// and reset and reinsert the timer.
@ -330,8 +360,6 @@ CEventQueue::hasTimerExpired(CEvent& event)
double
CEventQueue::getNextTimerTimeout() const
{
CArchMutexLock lock(m_mutex);
// return -1 if no timers, 0 if the top timer has expired, otherwise
// the time until the top timer in the timer priority queue will
// expire.

View File

@ -34,6 +34,7 @@ public:
virtual ~CEventQueue();
// IEventQueue overrides
virtual void adoptBuffer(IEventQueueBuffer*);
virtual bool getEvent(CEvent& event, double timeout = -1.0);
virtual bool dispatchEvent(const CEvent& event);
virtual void addEvent(const CEvent& event);
@ -52,80 +53,13 @@ public:
virtual bool isEmpty() const;
virtual IEventJob* getHandler(CEvent::Type type, void* target) const;
protected:
//! @name manipulators
//@{
//! Get the data for a given id
/*!
Takes a saved event id, \p eventID, and returns a \c CEvent. The
event id becomes invalid after this call. The \p eventID must have
been passed to a successful call to \c doAddEvent() and not removed
since.
*/
CEvent removeEvent(UInt32 eventID);
//! Block waiting for an event
/*!
Wait for an event in the system event queue for up to \p timeout
seconds.
*/
virtual void waitForEvent(double timeout) = 0;
//! Get the next event
/*!
Remove the next system event (one should be pending) and convert it
to a \c CEvent. The event type should be either \c CEvent::kSystem
if the event was not added by \c doAddEvent() or a type returned by
\c CEvent::registerType() if it was (and not \c CEvent::kTimer). A
non-system event will normally be retrieved by \c removeEvent(), but
the implementation must be able to tell the difference between a
system event and one added by \c doAddEvent().
*/
virtual bool doGetEvent(CEvent& event) = 0;
//! Post an event
/*!
Add the given event to the end of the system queue. This is a user
event and \c doGetEvent() must be able to identify it as such.
This method must cause \c waitForEvent() to return at some future
time if it's blocked waiting on an event.
*/
virtual bool doAddEvent(UInt32 dataID) = 0;
//@}
//! @name accessors
//@{
//! Check if system queue is empty
/*!
Return true iff the system queue is empty.
*/
virtual bool doIsEmpty() const = 0;
//! Create a timer object
/*!
Create and return a timer object. The object is opaque and is
used only by the subclass but it must be a valid object (i.e.
not NULL).
*/
virtual CEventQueueTimer*
doNewTimer(double duration, bool oneShot) const = 0;
//! Destroy a timer object
/*!
Destroy a timer object previously returned by \c doNewTimer().
*/
virtual void doDeleteTimer(CEventQueueTimer*) const = 0;
//@}
private:
void doAdoptHandler(CEvent::Type type,
void* target, IEventJob* handler);
IEventJob* doOrphanHandler(CEvent::Type type, void* target);
UInt32 saveEvent(const CEvent& event);
CEvent removeEvent(UInt32 eventID);
bool hasTimerExpired(CEvent& event);
double getNextTimerTimeout() const;
@ -175,6 +109,8 @@ private:
CArchMutex m_mutex;
IEventQueueBuffer* m_buffer;
CEventTable m_events;
CEventIDList m_oldEventIDs;

View File

@ -1,88 +0,0 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "CSimpleEventQueue.h"
#include "CArch.h"
class CEventQueueTimer { };
//
// CSimpleEventQueue
//
CSimpleEventQueue::CSimpleEventQueue()
{
m_queueMutex = ARCH->newMutex();
m_queueReadyCond = ARCH->newCondVar();
m_queueReady = false;
}
CSimpleEventQueue::~CSimpleEventQueue()
{
ARCH->closeCondVar(m_queueReadyCond);
ARCH->closeMutex(m_queueMutex);
}
void
CSimpleEventQueue::waitForEvent(double timeout)
{
CArchMutexLock lock(m_queueMutex);
while (!m_queueReady) {
ARCH->waitCondVar(m_queueReadyCond, m_queueMutex, -1.0);
}
}
bool
CSimpleEventQueue::doGetEvent(CEvent& event)
{
CArchMutexLock lock(m_queueMutex);
if (!m_queueReady) {
return false;
}
event = removeEvent(m_queue.back());
m_queue.pop_back();
m_queueReady = !m_queue.empty();
return true;
}
bool
CSimpleEventQueue::doAddEvent(UInt32 dataID)
{
CArchMutexLock lock(m_queueMutex);
m_queue.push_front(dataID);
if (!m_queueReady) {
m_queueReady = true;
ARCH->broadcastCondVar(m_queueReadyCond);
}
return true;
}
bool
CSimpleEventQueue::doIsEmpty() const
{
CArchMutexLock lock(m_queueMutex);
return !m_queueReady;
}
CEventQueueTimer*
CSimpleEventQueue::doNewTimer(double, bool) const
{
return new CEventQueueTimer;
}
void
CSimpleEventQueue::doDeleteTimer(CEventQueueTimer* timer) const
{
delete timer;
}

View File

@ -1,60 +0,0 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef CSIMPLEEVENTQUEUE_H
#define CSIMPLEEVENTQUEUE_H
#include "CEventQueue.h"
#include "IArchMultithread.h"
#include "stddeque.h"
//! Event queue for added events only
/*!
An event queue that provides no system events, just events added by
addEvent().
*/
class CSimpleEventQueue : public CEventQueue {
public:
CSimpleEventQueue();
virtual ~CSimpleEventQueue();
//! @name manipulators
//@{
//@}
//! @name accessors
//@{
//@}
protected:
// CEventQueue overrides
virtual void waitForEvent(double timeout);
virtual bool doGetEvent(CEvent& event);
virtual bool doAddEvent(UInt32 dataID);
virtual bool doIsEmpty() const;
virtual CEventQueueTimer*
doNewTimer(double duration, bool oneShot) const;
virtual void doDeleteTimer(CEventQueueTimer*) const;
private:
typedef std::deque<UInt32> CEventDeque;
CArchMutex m_queueMutex;
CArchCond m_queueReadyCond;
bool m_queueReady;
CEventDeque m_queue;
};
#endif

View File

@ -21,9 +21,10 @@
#define EVENTQUEUE IEventQueue::getInstance()
class IEventJob;
class IEventQueueBuffer;
// Opaque type for timer info. This is defined by subclasses of
// IEventQueue.
// IEventQueueBuffer.
class CEventQueueTimer;
//! Event queue interface
@ -44,6 +45,13 @@ public:
//! @name manipulators
//@{
//! Set the buffer
/*!
Replace the current event queue buffer. Any queued events are
discarded. The queue takes ownership of the buffer.
*/
virtual void adoptBuffer(IEventQueueBuffer*) = 0;
//! Remove event from queue
/*!
Returns the next event on the queue into \p event. If no event is

View File

@ -31,7 +31,7 @@ libbase_a_SOURCES = \
CFunctionJob.cpp \
CJobList.cpp \
CLog.cpp \
CSimpleEventQueue.cpp \
CSimpleEventQueueBuffer.cpp \
CStopwatch.cpp \
CStringUtil.cpp \
CUnicode.cpp \
@ -44,12 +44,13 @@ libbase_a_SOURCES = \
CJobList.h \
CLog.h \
CPriorityQueue.h \
CSimpleEventQueue.h \
CSimpleEventQueueBuffer.h \
CStopwatch.h \
CString.h \
CStringUtil.h \
CUnicode.h \
IEventQueue.h \
IEventQueueBuffer.h \
IJob.h \
ILogOutputter.h \
LogOutputters.h \