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

View File

@ -34,6 +34,7 @@ public:
virtual ~CEventQueue(); virtual ~CEventQueue();
// IEventQueue overrides // IEventQueue overrides
virtual void adoptBuffer(IEventQueueBuffer*);
virtual bool getEvent(CEvent& event, double timeout = -1.0); virtual bool getEvent(CEvent& event, double timeout = -1.0);
virtual bool dispatchEvent(const CEvent& event); virtual bool dispatchEvent(const CEvent& event);
virtual void addEvent(const CEvent& event); virtual void addEvent(const CEvent& event);
@ -52,80 +53,13 @@ public:
virtual bool isEmpty() const; virtual bool isEmpty() const;
virtual IEventJob* getHandler(CEvent::Type type, void* target) 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: private:
void doAdoptHandler(CEvent::Type type, void doAdoptHandler(CEvent::Type type,
void* target, IEventJob* handler); void* target, IEventJob* handler);
IEventJob* doOrphanHandler(CEvent::Type type, void* target); IEventJob* doOrphanHandler(CEvent::Type type, void* target);
UInt32 saveEvent(const CEvent& event); UInt32 saveEvent(const CEvent& event);
CEvent removeEvent(UInt32 eventID);
bool hasTimerExpired(CEvent& event); bool hasTimerExpired(CEvent& event);
double getNextTimerTimeout() const; double getNextTimerTimeout() const;
@ -175,6 +109,8 @@ private:
CArchMutex m_mutex; CArchMutex m_mutex;
IEventQueueBuffer* m_buffer;
CEventTable m_events; CEventTable m_events;
CEventIDList m_oldEventIDs; 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() #define EVENTQUEUE IEventQueue::getInstance()
class IEventJob; class IEventJob;
class IEventQueueBuffer;
// Opaque type for timer info. This is defined by subclasses of // Opaque type for timer info. This is defined by subclasses of
// IEventQueue. // IEventQueueBuffer.
class CEventQueueTimer; class CEventQueueTimer;
//! Event queue interface //! Event queue interface
@ -44,6 +45,13 @@ public:
//! @name manipulators //! @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 //! Remove event from queue
/*! /*!
Returns the next event on the queue into \p event. If no event is 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 \ CFunctionJob.cpp \
CJobList.cpp \ CJobList.cpp \
CLog.cpp \ CLog.cpp \
CSimpleEventQueue.cpp \ CSimpleEventQueueBuffer.cpp \
CStopwatch.cpp \ CStopwatch.cpp \
CStringUtil.cpp \ CStringUtil.cpp \
CUnicode.cpp \ CUnicode.cpp \
@ -44,12 +44,13 @@ libbase_a_SOURCES = \
CJobList.h \ CJobList.h \
CLog.h \ CLog.h \
CPriorityQueue.h \ CPriorityQueue.h \
CSimpleEventQueue.h \ CSimpleEventQueueBuffer.h \
CStopwatch.h \ CStopwatch.h \
CString.h \ CString.h \
CStringUtil.h \ CStringUtil.h \
CUnicode.h \ CUnicode.h \
IEventQueue.h \ IEventQueue.h \
IEventQueueBuffer.h \
IJob.h \ IJob.h \
ILogOutputter.h \ ILogOutputter.h \
LogOutputters.h \ LogOutputters.h \