Checkpointing centralized event queue stuff. Currently have:

an event queue and events, TCP sockets converted to use events,
unix multithreading and network stuff converted, and an X Windows
event queue subclass.
This commit is contained in:
crs 2004-01-24 16:09:25 +00:00
parent fa215f1b13
commit 4c7e524896
40 changed files with 2215 additions and 296 deletions

View File

@ -357,6 +357,12 @@ CArch::waitForEvent(CArchThread thread, double timeout)
return m_mt->waitForEvent(thread, timeout); return m_mt->waitForEvent(thread, timeout);
} }
void
CArch::unblockThread(CArchThread thread)
{
m_mt->unblockThread(thread);
}
bool bool
CArch::isSameThread(CArchThread thread1, CArchThread thread2) CArch::isSameThread(CArchThread thread1, CArchThread thread2)
{ {

View File

@ -117,6 +117,7 @@ public:
virtual void testCancelThread(); virtual void testCancelThread();
virtual bool wait(CArchThread, double timeout); virtual bool wait(CArchThread, double timeout);
virtual EWaitResult waitForEvent(CArchThread, double timeout); virtual EWaitResult waitForEvent(CArchThread, double timeout);
virtual void unblockThread(CArchThread thread);
virtual bool isSameThread(CArchThread, CArchThread); virtual bool isSameThread(CArchThread, CArchThread);
virtual bool isExitedThread(CArchThread); virtual bool isExitedThread(CArchThread);
virtual void* getResultOfThread(CArchThread); virtual void* getResultOfThread(CArchThread);
@ -189,4 +190,20 @@ private:
IArchTime* m_time; IArchTime* m_time;
}; };
//! Convenience object to lock/unlock an arch mutex
class CArchMutexLock {
public:
CArchMutexLock(CArchMutex mutex) : m_mutex(mutex)
{
ARCH->lockMutex(m_mutex);
}
~CArchMutexLock()
{
ARCH->unlockMutex(m_mutex);
}
private:
CArchMutex m_mutex;
};
#endif #endif

View File

@ -522,6 +522,12 @@ CArchMultithreadPosix::waitForEvent(CArchThread, double /*timeout*/)
return kTimeout; return kTimeout;
} }
void
CArchMultithreadPosix::unblockThread(CArchThread thread)
{
pthread_kill(thread->m_thread, SIGWAKEUP);
}
bool bool
CArchMultithreadPosix::isSameThread(CArchThread thread1, CArchThread thread2) CArchMultithreadPosix::isSameThread(CArchThread thread1, CArchThread thread2)
{ {

View File

@ -56,6 +56,7 @@ public:
virtual void testCancelThread(); virtual void testCancelThread();
virtual bool wait(CArchThread, double timeout); virtual bool wait(CArchThread, double timeout);
virtual EWaitResult waitForEvent(CArchThread, double timeout); virtual EWaitResult waitForEvent(CArchThread, double timeout);
virtual void unblockThread(CArchThread thread);
virtual bool isSameThread(CArchThread, CArchThread); virtual bool isSameThread(CArchThread, CArchThread);
virtual bool isExitedThread(CArchThread); virtual bool isExitedThread(CArchThread);
virtual void* getResultOfThread(CArchThread); virtual void* getResultOfThread(CArchThread);

View File

@ -200,11 +200,6 @@ CArchNetworkBSD::acceptSocket(CArchSocket s, CArchNetAddress* addr)
ARCH->testCancelThread(); ARCH->testCancelThread();
continue; continue;
} }
if (err == ECONNABORTED) {
// connection was aborted; try again
ARCH->testCancelThread();
continue;
}
delete newSocket; delete newSocket;
delete *addr; delete *addr;
*addr = NULL; *addr = NULL;
@ -244,11 +239,6 @@ CArchNetworkBSD::connectSocket(CArchSocket s, CArchNetAddress addr)
break; break;
} }
if (errno == EAGAIN) {
// connecting
throw XArchNetworkConnecting(new XArchEvalUnix(errno));
}
throwError(errno); throwError(errno);
} }
} while (false); } while (false);
@ -290,14 +280,15 @@ CArchNetworkBSD::pollSocket(CPollEntry pe[], int num, double timeout)
} }
// do the poll // do the poll
int t = (timeout < 0.0) ? -1 : static_cast<int>(1000.0 * timeout);
int n; int n;
do { do {
n = poll(pfd, num, static_cast<int>(1000.0 * timeout)); n = poll(pfd, num, t);
if (n == -1) { if (n == -1) {
if (errno == EINTR) { if (errno == EINTR) {
// interrupted system call // interrupted system call
ARCH->testCancelThread(); ARCH->testCancelThread();
continue; return 0;
} }
throwError(errno); throwError(errno);
} }
@ -382,7 +373,7 @@ CArchNetworkBSD::pollSocket(CPollEntry pe[], int num, double timeout)
// prepare timeout for select // prepare timeout for select
struct timeval timeout2; struct timeval timeout2;
struct timeval* timeout2P; struct timeval* timeout2P;
if (timeout < 0) { if (timeout < 0.0) {
timeout2P = NULL; timeout2P = NULL;
} }
else { else {
@ -404,7 +395,7 @@ CArchNetworkBSD::pollSocket(CPollEntry pe[], int num, double timeout)
if (errno == EINTR) { if (errno == EINTR) {
// interrupted system call // interrupted system call
ARCH->testCancelThread(); ARCH->testCancelThread();
continue; return 0;
} }
throwError(errno); throwError(errno);
} }

View File

@ -228,6 +228,15 @@ public:
*/ */
virtual EWaitResult waitForEvent(CArchThread thread, double timeout) = 0; virtual EWaitResult waitForEvent(CArchThread thread, double timeout) = 0;
//! Unblock thread in system call
/*!
Cause a thread that's in a blocking system call to return. This
call may return before the thread is unblocked. If the thread is
not in a blocking system call, this call has no effect. This does
not cause a lockMutex() or waitCondVar() to return prematurely.
*/
virtual void unblockThread(CArchThread thread) = 0;
//! Compare threads //! Compare threads
/*! /*!
Returns true iff two thread objects refer to the same thread. Returns true iff two thread objects refer to the same thread.

89
lib/base/CEvent.cpp Normal file
View File

@ -0,0 +1,89 @@
/*
* 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 "CEvent.h"
//
// CEvent
//
CEvent::Type CEvent::s_nextType = kLast;
CEvent::CEvent() :
m_type(kUnknown),
m_target(NULL),
m_data(NULL)
{
// do nothing
}
CEvent::CEvent(Type type, void* target, void* data) :
m_type(type),
m_target(target),
m_data(data)
{
// do nothing
}
CEvent::Type
CEvent::getType() const
{
return m_type;
}
void*
CEvent::getTarget() const
{
return m_target;
}
void*
CEvent::getData() const
{
return m_data;
}
CEvent::Type
CEvent::registerType()
{
// FIXME -- lock mutex (need a mutex)
return s_nextType++;
}
CEvent::Type
CEvent::registerTypeOnce(Type& type)
{
// FIXME -- lock mutex (need a mutex)
if (type == CEvent::kUnknown) {
type = s_nextType++;
}
return type;
}
void
CEvent::deleteData(const CEvent& event)
{
switch (event.getType()) {
case kUnknown:
case kQuit:
case kSystem:
case kTimer:
break;
default:
// yes, really delete void*
delete event.getData();
break;
}
}

100
lib/base/CEvent.h Normal file
View File

@ -0,0 +1,100 @@
/*
* 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 CEVENT_H
#define CEVENT_H
#include "BasicTypes.h"
//! Event
/*!
A \c CEvent holds an event type and a pointer to event data.
*/
class CEvent {
public:
typedef UInt32 Type;
enum {
kUnknown, //!< The event type is unknown
kQuit, //!< The quit event
kSystem, //!< The data points to a system event type
kTimer, //!< The data points to timer info
kLast //!< Must be last
};
CEvent();
//! Create \c CEvent with data
/*!
The \p type must have been registered using \c registerType().
The \p data must be POD (plain old data) which means it cannot
have a destructor or be composed of any types that do. \p target
is the intended recipient of the event.
*/
CEvent(Type type, void* target = NULL, void* data = NULL);
//! @name manipulators
//@{
//@}
//! @name accessors
//@{
//! Get event type
/*!
Returns the event type.
*/
Type getType() const;
//! Get the event target
/*!
Returns the event target.
*/
void* getTarget() const;
//! Get the event data
/*!
Returns the event data.
*/
void* getData() const;
//! Creates a new event type
/*!
Returns a unique event type id.
*/
static Type registerType();
//! Creates a new event type
/*!
If \p type contains \c kUnknown then it is set to a unique event
type id otherwise it is left alone. The final value of \p type
is returned.
*/
static Type registerTypeOnce(Type& type);
//! Release event data
/*!
Deletes event data for the given event.
*/
static void deleteData(const CEvent&);
//@}
private:
Type m_type;
void* m_target;
void* m_data;
static Type s_nextType;
};
#endif

381
lib/base/CEventQueue.cpp Normal file
View File

@ -0,0 +1,381 @@
/*
* 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 "CEventQueue.h"
#include "IEventJob.h"
#include "CArch.h"
//
// CEventQueue
//
static int g_systemTarget = 0;
CEventQueue* CEventQueue::s_instance = NULL;
CEventQueue::CEventQueue()
{
assert(s_instance == NULL);
s_instance = this;
m_mutex = ARCH->newMutex();
}
CEventQueue::~CEventQueue()
{
ARCH->closeMutex(m_mutex);
s_instance = NULL;
}
void*
CEventQueue::getSystemTarget()
{
// any unique arbitrary pointer will do
return &g_systemTarget;
}
CEventQueue*
CEventQueue::getInstance()
{
return s_instance;
}
bool
CEventQueue::getEvent(CEvent& event, double timeout)
{
// if no events are waiting then handle timers and then wait
if (doIsEmpty()) {
// handle timers first
if (hasTimerExpired(event)) {
return true;
}
// get time until next timer expires. if there is a timer
// and it'll expire before the client's timeout then use
// that duration for our timeout instead.
double timerTimeout = getNextTimerTimeout();
if (timerTimeout >= 0.0 && timerTimeout < timeout) {
timeout = timerTimeout;
}
// wait for an event
waitForEvent(timeout);
}
// if no events are pending then do the timers
if (doIsEmpty()) {
return hasTimerExpired(event);
}
return doGetEvent(event);
}
bool
CEventQueue::dispatchEvent(const CEvent& event)
{
void* target = event.getTarget();
IEventJob* job = getHandler(target);
if (job != NULL) {
job->run(event);
return true;
}
return false;
}
void
CEventQueue::addEvent(const CEvent& event)
{
// discard bogus event types
switch (event.getType()) {
case CEvent::kUnknown:
case CEvent::kSystem:
case CEvent::kTimer:
return;
default:
break;
}
// store the event's data locally
UInt32 eventID = saveEvent(event);
// add it
if (!doAddEvent(eventID)) {
// failed to send event
removeEvent(eventID);
CEvent::deleteData(event);
}
}
CEventQueueTimer*
CEventQueue::newTimer(double duration, void* target)
{
assert(duration > 0.0);
CEventQueueTimer* timer = doNewTimer(duration, false);
CArchMutexLock lock(m_mutex);
m_timers.insert(timer);
m_timerQueue.push(CTimer(timer, duration, target, false));
return timer;
}
CEventQueueTimer*
CEventQueue::newOneShotTimer(double duration, void* target)
{
assert(duration > 0.0);
CEventQueueTimer* timer = doNewTimer(duration, true);
CArchMutexLock lock(m_mutex);
m_timers.insert(timer);
m_timerQueue.push(CTimer(timer, duration, target, true));
return timer;
}
void
CEventQueue::deleteTimer(CEventQueueTimer* timer)
{
{
CArchMutexLock lock(m_mutex);
for (CTimerQueue::iterator index = m_timerQueue.begin();
index != m_timerQueue.end(); ++index) {
if (index->getTimer() == timer) {
m_timerQueue.erase(index);
break;
}
}
CTimers::iterator index = m_timers.find(timer);
if (index != m_timers.end()) {
m_timers.erase(index);
}
}
doDeleteTimer(timer);
}
void
CEventQueue::adoptHandler(void* target, IEventJob* handler)
{
CArchMutexLock lock(m_mutex);
IEventJob*& job = m_handlers[target];
delete job;
job = handler;
}
IEventJob*
CEventQueue::orphanHandler(void* target)
{
CArchMutexLock lock(m_mutex);
CHandlerTable::iterator index = m_handlers.find(target);
if (index != m_handlers.end()) {
IEventJob* handler = index->second;
m_handlers.erase(index);
return handler;
}
else {
return NULL;
}
}
bool
CEventQueue::isEmpty() const
{
return (doIsEmpty() && getNextTimerTimeout() != 0.0);
}
IEventJob*
CEventQueue::getHandler(void* target) const
{
CArchMutexLock lock(m_mutex);
CHandlerTable::const_iterator index = m_handlers.find(target);
if (index != m_handlers.end()) {
return index->second;
}
else {
return NULL;
}
}
UInt32
CEventQueue::saveEvent(const CEvent& event)
{
CArchMutexLock lock(m_mutex);
// choose id
UInt32 id;
if (!m_oldEventIDs.empty()) {
// reuse an id
id = m_oldEventIDs.back();
m_oldEventIDs.pop_back();
}
else {
// make a new id
id = static_cast<UInt32>(m_oldEventIDs.size());
}
// save data
m_events[id] = event;
return id;
}
CEvent
CEventQueue::removeEvent(UInt32 eventID)
{
CArchMutexLock lock(m_mutex);
// look up id
CEventTable::iterator index = m_events.find(eventID);
if (index == m_events.end()) {
return CEvent();
}
// get data
CEvent event = index->second;
m_events.erase(index);
// save old id for reuse
m_oldEventIDs.push_back(eventID);
return event;
}
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.
if (m_timerQueue.empty()) {
return false;
}
// get time elapsed since last check
const double time = m_time.getTime();
m_time.reset();
// countdown elapsed time
for (CTimerQueue::iterator index = m_timerQueue.begin();
index != m_timerQueue.end(); ++index) {
(*index) -= time;
}
// done if no timers are expired
if (m_timerQueue.top() > 0.0) {
return false;
}
// remove timer from queue
CTimer timer = m_timerQueue.top();
m_timerQueue.pop();
// prepare event and reset the timer's clock
timer.fillEvent(m_timerEvent);
event = CEvent(CEvent::kTimer, timer.getTarget(), &m_timerEvent);
timer.reset();
// reinsert timer into queue if it's not a one-shot
if (!timer.isOneShot()) {
m_timerQueue.push(timer);
}
return true;
}
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.
if (m_timerQueue.empty()) {
return -1.0;
}
if (m_timerQueue.top() <= 0.0) {
return 0.0;
}
return m_timerQueue.top();
}
//
// CXWindowsScreen::CTimer
//
CEventQueue::CTimer::CTimer(CEventQueueTimer* timer,
double timeout, void* target, bool oneShot) :
m_timer(timer),
m_timeout(timeout),
m_target(target),
m_oneShot(oneShot),
m_time(timeout)
{
assert(m_timeout > 0.0);
}
CEventQueue::CTimer::~CTimer()
{
// do nothing
}
void
CEventQueue::CTimer::reset()
{
m_time = m_timeout;
}
CEventQueue::CTimer::CTimer&
CEventQueue::CTimer::operator-=(double dt)
{
m_time -= dt;
return *this;
}
CEventQueue::CTimer::operator double() const
{
return m_time;
}
bool
CEventQueue::CTimer::isOneShot() const
{
return m_oneShot;
}
CEventQueueTimer*
CEventQueue::CTimer::getTimer() const
{
return m_timer;
}
void*
CEventQueue::CTimer::getTarget() const
{
return m_target;
}
void
CEventQueue::CTimer::fillEvent(CTimerEvent& event) const
{
event.m_timer = m_timer;
event.m_count = 0;
if (m_time <= 0.0) {
event.m_count = static_cast<UInt32>((m_timeout - m_time) / m_timeout);
}
}
bool
CEventQueue::CTimer::operator<(const CTimer& t) const
{
return m_time < t.m_time;
}

192
lib/base/CEventQueue.h Normal file
View File

@ -0,0 +1,192 @@
/*
* 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 CEVENTQUEUE_H
#define CEVENTQUEUE_H
#include "IEventQueue.h"
#include "CEvent.h"
#include "CPriorityQueue.h"
#include "CStopwatch.h"
#include "IArchMultithread.h"
#include "stdmap.h"
#include "stdset.h"
//! Event queue
/*!
An event queue that implements the platform independent parts and
delegates the platform dependent parts to a subclass.
*/
class CEventQueue : public IEventQueue {
public:
CEventQueue();
virtual ~CEventQueue();
//! @name manipulators
//@{
//@}
//! @name accessors
//@{
//! Get the system event type target
/*!
Returns the target to use for dispatching \c CEvent::kSystem events.
*/
static void* getSystemTarget();
//! Get the singleton instance
/*!
Returns the singleton instance of the event queue
*/
static CEventQueue* getInstance();
//@}
// IEventQueue overrides
virtual bool getEvent(CEvent& event, double timeout = -1.0);
virtual bool dispatchEvent(const CEvent& event);
virtual void addEvent(const CEvent& event);
virtual CEventQueueTimer*
newTimer(double duration, void* target = NULL);
virtual CEventQueueTimer*
newOneShotTimer(double duration, void* target = NULL);
virtual void deleteTimer(CEventQueueTimer*);
virtual void adoptHandler(void* target, IEventJob* dispatcher);
virtual IEventJob* orphanHandler(void* target);
virtual bool isEmpty() const;
virtual IEventJob* getHandler(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:
UInt32 saveEvent(const CEvent& event);
bool hasTimerExpired(CEvent& event);
double getNextTimerTimeout() const;
private:
class CTimer {
public:
CTimer(CEventQueueTimer*, double timeout, void* target, bool oneShot);
~CTimer();
void reset();
CTimer& operator-=(double);
operator double() const;
bool isOneShot() const;
CEventQueueTimer*
getTimer() const;
void* getTarget() const;
void fillEvent(CTimerEvent&) const;
bool operator<(const CTimer&) const;
private:
CEventQueueTimer* m_timer;
double m_timeout;
void* m_target;
bool m_oneShot;
double m_time;
};
typedef std::set<CEventQueueTimer*> CTimers;
typedef CPriorityQueue<CTimer> CTimerQueue;
typedef std::map<UInt32, CEvent> CEventTable;
typedef std::vector<UInt32> CEventIDList;
typedef std::map<void*, IEventJob*> CHandlerTable;
static CEventQueue* s_instance;
CArchMutex m_mutex;
CEventTable m_events;
CEventIDList m_oldEventIDs;
CStopwatch m_time;
CTimers m_timers;
CTimerQueue m_timerQueue;
CTimerEvent m_timerEvent;
CHandlerTable m_handlers;
};
#endif

View File

@ -0,0 +1,40 @@
/*
* 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 "CFunctionEventJob.h"
//
// CFunctionJob
//
CFunctionEventJob::CFunctionEventJob(
void (*func)(const CEvent&, void*), void* arg) :
m_func(func),
m_arg(arg)
{
// do nothing
}
CFunctionEventJob::~CFunctionEventJob()
{
// do nothing
}
void
CFunctionEventJob::run(const CEvent& event)
{
if (m_func != NULL) {
m_func(event, m_arg);
}
}

View File

@ -0,0 +1,38 @@
/*
* 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 CFUNCTIONEVENTJOB_H
#define CFUNCTIONEVENTJOB_H
#include "IEventJob.h"
//! Use a function as an event job
/*!
An event job class that invokes a function.
*/
class CFunctionEventJob : public IEventJob {
public:
//! run() invokes \c func(arg)
CFunctionEventJob(void (*func)(const CEvent&, void*), void* arg = NULL);
virtual ~CFunctionEventJob();
// IEventJob overrides
virtual void run(const CEvent&);
private:
void (*m_func)(const CEvent&, void*);
void* m_arg;
};
#endif

View File

@ -54,16 +54,6 @@ static const int g_prioritySuffixLength = 2;
static const int g_priorityPad = g_maxPriorityLength + static const int g_priorityPad = g_maxPriorityLength +
g_prioritySuffixLength; g_prioritySuffixLength;
//! Convenience object to lock/unlock a mutex
class CLogLock {
public:
CLogLock(CArchMutex mutex) : m_mutex(mutex) { ARCH->lockMutex(m_mutex); }
~CLogLock() { ARCH->unlockMutex(m_mutex); }
private:
CArchMutex m_mutex;
};
// //
// CLog // CLog
@ -198,7 +188,7 @@ CLog::insert(ILogOutputter* outputter, bool alwaysAtHead)
assert(outputter != NULL); assert(outputter != NULL);
assert(outputter->getNewline() != NULL); assert(outputter->getNewline() != NULL);
CLogLock lock(m_mutex); CArchMutexLock lock(m_mutex);
if (alwaysAtHead) { if (alwaysAtHead) {
m_alwaysOutputters.push_front(outputter); m_alwaysOutputters.push_front(outputter);
} }
@ -214,7 +204,7 @@ CLog::insert(ILogOutputter* outputter, bool alwaysAtHead)
void void
CLog::remove(ILogOutputter* outputter) CLog::remove(ILogOutputter* outputter)
{ {
CLogLock lock(m_mutex); CArchMutexLock lock(m_mutex);
m_outputters.remove(outputter); m_outputters.remove(outputter);
m_alwaysOutputters.remove(outputter); m_alwaysOutputters.remove(outputter);
} }
@ -222,7 +212,7 @@ CLog::remove(ILogOutputter* outputter)
void void
CLog::pop_front(bool alwaysAtHead) CLog::pop_front(bool alwaysAtHead)
{ {
CLogLock lock(m_mutex); CArchMutexLock lock(m_mutex);
COutputterList* list = alwaysAtHead ? &m_alwaysOutputters : &m_outputters; COutputterList* list = alwaysAtHead ? &m_alwaysOutputters : &m_outputters;
if (!list->empty()) { if (!list->empty()) {
delete list->front(); delete list->front();
@ -248,14 +238,14 @@ CLog::setFilter(const char* maxPriority)
void void
CLog::setFilter(int maxPriority) CLog::setFilter(int maxPriority)
{ {
CLogLock lock(m_mutex); CArchMutexLock lock(m_mutex);
m_maxPriority = maxPriority; m_maxPriority = maxPriority;
} }
int int
CLog::getFilter() const CLog::getFilter() const
{ {
CLogLock lock(m_mutex); CArchMutexLock lock(m_mutex);
return m_maxPriority; return m_maxPriority;
} }
@ -276,7 +266,7 @@ CLog::output(int priority, char* msg) const
} }
// write to each outputter // write to each outputter
CLogLock lock(m_mutex); CArchMutexLock lock(m_mutex);
for (COutputterList::const_iterator index = m_alwaysOutputters.begin(); for (COutputterList::const_iterator index = m_alwaysOutputters.begin();
index != m_alwaysOutputters.end(); index != m_alwaysOutputters.end();
++index) { ++index) {

View File

@ -56,6 +56,13 @@ public:
c.pop_back(); c.pop_back();
} }
//! Erase element
void erase(iterator i)
{
c.erase(i);
std::make_heap(c.begin(), c.end(), comp);
}
//! Get start iterator //! Get start iterator
iterator begin() iterator begin()
{ {

View File

@ -0,0 +1,88 @@
/*
* 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

@ -0,0 +1,60 @@
/*
* 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

32
lib/base/IEventJob.h Normal file
View File

@ -0,0 +1,32 @@
/*
* 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 IEVENTJOB_H
#define IEVENTJOB_H
#include "IInterface.h"
class CEvent;
//! Event handler interface
/*!
An event job is an interface for executing a event handler.
*/
class IEventJob : public IInterface {
public:
//! Run the job
virtual void run(const CEvent&) = 0;
};
#endif

142
lib/base/IEventQueue.h Normal file
View File

@ -0,0 +1,142 @@
/*
* 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 IEVENTQUEUE_H
#define IEVENTQUEUE_H
#include "IInterface.h"
#include "BasicTypes.h"
class CEvent;
class IEventJob;
// Opaque type for timer info. This is defined by subclasses of
// IEventQueue.
class CEventQueueTimer;
//! Event queue interface
/*!
An event queue provides a queue of CEvents. Clients can block waiting
on any event becoming available at the head of the queue and can place
new events at the end of the queue. Clients can also add and remove
timers which generate events periodically.
*/
class IEventQueue : public IInterface {
public:
class CTimerEvent {
public:
CEventQueueTimer* m_timer; //!< The timer
UInt32 m_count; //!< Number of repeats
};
//! @name manipulators
//@{
//! Remove event from queue
/*!
Returns the next event on the queue into \p event. If no event is
available then blocks for up to \p timeout seconds, or forever if
\p timeout is negative. Returns true iff an event was available.
*/
virtual bool getEvent(CEvent& event, double timeout = -1.0) = 0;
//! Dispatch an event
/*!
Looks up the dispatcher for the event's target and invokes it.
Returns true iff a dispatcher exists for the target.
*/
virtual bool dispatchEvent(const CEvent& event) = 0;
//! Add event to queue
/*!
Adds \p event to the end of the queue.
*/
virtual void addEvent(const CEvent& event) = 0;
//! Create a recurring timer
/*!
Creates and returns a timer. An event is returned after \p duration
seconds and the timer is reset to countdown again. When a timer event
is returned the data points to a \c CTimerEvent. The client must pass
the returned timer to \c deleteTimer() (whether or not the timer has
expired) to release the timer. The returned timer event uses the
given \p target.
Events for a single timer don't accumulate in the queue, even if the
client reading events can't keep up. Instead, the \c m_count member
of the \c CTimerEvent indicates how many events for the timer would
have been put on the queue since the last event for the timer was
removed (or since the timer was added).
*/
virtual CEventQueueTimer*
newTimer(double duration, void* target = NULL) = 0;
//! Create a one-shot timer
/*!
Creates and returns a one-shot timer. An event is returned when
the timer expires and the timer is removed from further handling.
When a timer event is returned the data points to a \c CTimerEvent.
The \m c_count member of the \c CTimerEvent is always 1. The client
must pass the returned timer to \c deleteTimer() (whether or not the
timer has expired) to release the timer. The returned timer event
uses the given \p target.
*/
virtual CEventQueueTimer*
newOneShotTimer(double duration,
void* target = NULL) = 0;
//! Destroy a timer
/*!
Destroys a previously created timer. The timer is removed from the
queue and will not generate event, even if the timer has expired.
*/
virtual void deleteTimer(CEventQueueTimer*) = 0;
//! Register an event handler
/*!
Registers an event handler for \p target. The \p handler is
adopted. Any existing handler for the target is deleted.
*/
virtual void adoptHandler(void* target, IEventJob* handler) = 0;
//! Unregister an event handler
/*!
Unregisters an event handler for \p target and returns it.
Returns NULL if there was no such handler. The client becomes
responsible for deleting the returned handler.
*/
virtual IEventJob* orphanHandler(void* target) = 0;
//@}
//! @name accessors
//@{
//! Test if queue is empty
/*!
Returns true iff the queue has no events in it, including timer
events.
*/
virtual bool isEmpty() const = 0;
//! Get an event handler
/*!
Finds and returns the event handler for \p target, or NULL if
there is no such handler.
*/
virtual IEventJob* getHandler(void* target) const = 0;
//@}
};
#endif

View File

@ -25,25 +25,34 @@ MAINTAINERCLEANFILES = \
noinst_LIBRARIES = libbase.a noinst_LIBRARIES = libbase.a
libbase_a_SOURCES = \ libbase_a_SOURCES = \
CEvent.cpp \
CEventQueue.cpp \
CFunctionEventJob.cpp \
CFunctionJob.cpp \ CFunctionJob.cpp \
CJobList.cpp \ CJobList.cpp \
CLog.cpp \ CLog.cpp \
CSimpleEventQueue.cpp \
CStopwatch.cpp \ CStopwatch.cpp \
CStringUtil.cpp \ CStringUtil.cpp \
CUnicode.cpp \ CUnicode.cpp \
LogOutputters.cpp \ LogOutputters.cpp \
XBase.cpp \ XBase.cpp \
CEvent.h \
CEventQueue.h \
CFunctionJob.h \ CFunctionJob.h \
CJobList.h \ CJobList.h \
CLog.h \ CLog.h \
CPriorityQueue.h \ CPriorityQueue.h \
CSimpleEventQueue.h \
CStopwatch.h \ CStopwatch.h \
CString.h \ CString.h \
CStringUtil.h \ CStringUtil.h \
CUnicode.h \ CUnicode.h \
IEventQueue.h \
IJob.h \ IJob.h \
ILogOutputter.h \ ILogOutputter.h \
LogOutputters.h \ LogOutputters.h \
TMethodEventJob.h \
TMethodJob.h \ TMethodJob.h \
XBase.h \ XBase.h \
$(NULL) $(NULL)

View File

@ -0,0 +1,70 @@
/*
* 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 CMETHODEVENTJOB_H
#define CMETHODEVENTJOB_H
#include "IEventJob.h"
//! Use a member function as an event job
/*!
An event job class that invokes a member function.
*/
template <class T>
class TMethodEventJob : public IEventJob {
public:
//! run(event) invokes \c object->method(event, arg)
TMethodEventJob(T* object,
void (T::*method)(const CEvent&, void*),
void* arg = NULL);
virtual ~TMethodEventJob();
// IJob overrides
virtual void run(const CEvent&);
private:
T* m_object;
void (T::*m_method)(const CEvent&, void*);
void* m_arg;
};
template <class T>
inline
TMethodEventJob<T>::TMethodEventJob(T* object,
void (T::*method)(const CEvent&, void*), void* arg) :
m_object(object),
m_method(method),
m_arg(arg)
{
// do nothing
}
template <class T>
inline
TMethodEventJob<T>::~TMethodEventJob()
{
// do nothing
}
template <class T>
inline
void
TMethodEventJob<T>::run(const CEvent& event)
{
if (m_object != NULL) {
(m_object->*m_method)(event, m_arg);
}
}
#endif

View File

@ -26,9 +26,10 @@
// //
CBufferedInputStream::CBufferedInputStream( CBufferedInputStream::CBufferedInputStream(
CMutex* mutex, IJob* adoptedCloseCB) : CMutex* mutex, IJob* adoptedEmptyCB, IJob* adoptedCloseCB) :
m_mutex(mutex), m_mutex(mutex),
m_empty(mutex, true), m_empty(mutex, true),
m_emptyCB(adoptedEmptyCB),
m_closeCB(adoptedCloseCB), m_closeCB(adoptedCloseCB),
m_closed(false), m_closed(false),
m_hungup(false) m_hungup(false)
@ -39,6 +40,7 @@ CBufferedInputStream::CBufferedInputStream(
CBufferedInputStream::~CBufferedInputStream() CBufferedInputStream::~CBufferedInputStream()
{ {
delete m_closeCB; delete m_closeCB;
delete m_emptyCB;
} }
void void
@ -64,6 +66,9 @@ CBufferedInputStream::readNoLock(void* buffer, UInt32 n, double timeout)
if (m_closed) { if (m_closed) {
throw XIOClosed(); throw XIOClosed();
} }
if (n == 0) {
return n;
}
// wait for data, hangup, or timeout // wait for data, hangup, or timeout
CStopwatch timer(true); CStopwatch timer(true);
@ -90,6 +95,9 @@ CBufferedInputStream::readNoLock(void* buffer, UInt32 n, double timeout)
if (m_buffer.getSize() == 0) { if (m_buffer.getSize() == 0) {
m_empty = true; m_empty = true;
m_empty.broadcast(); m_empty.broadcast();
if (m_emptyCB != NULL) {
m_emptyCB->run();
}
} }
return n; return n;
} }

View File

@ -36,8 +36,12 @@ public:
The \c mutex must not be NULL and will be used to ensure thread The \c mutex must not be NULL and will be used to ensure thread
safe access. If \c adoptedCloseCB is not NULL it will be called safe access. If \c adoptedCloseCB is not NULL it will be called
when close() is called, allowing the creator to detect the close. when close() is called, allowing the creator to detect the close.
If adoptedEmptyCB is not NULL, it will be called whenever the
buffer becomes empty (except it won't be called by the c'tor nor
when the buffer is closed).
*/ */
CBufferedInputStream(CMutex* mutex, IJob* adoptedCloseCB); CBufferedInputStream(CMutex* mutex,
IJob* adoptedEmptyCB, IJob* adoptedCloseCB);
~CBufferedInputStream(); ~CBufferedInputStream();
//! @name manipulators //! @name manipulators
@ -87,6 +91,7 @@ public:
private: private:
CMutex* m_mutex; CMutex* m_mutex;
CCondVar<bool> m_empty; CCondVar<bool> m_empty;
IJob* m_emptyCB;
IJob* m_closeCB; IJob* m_closeCB;
CStreamBuffer m_buffer; CStreamBuffer m_buffer;
bool m_closed; bool m_closed;

View File

@ -24,8 +24,9 @@
// //
CBufferedOutputStream::CBufferedOutputStream( CBufferedOutputStream::CBufferedOutputStream(
CMutex* mutex, IJob* adoptedCloseCB) : CMutex* mutex, IJob* adoptedFillCB, IJob* adoptedCloseCB) :
m_mutex(mutex), m_mutex(mutex),
m_fillCB(adoptedFillCB),
m_closeCB(adoptedCloseCB), m_closeCB(adoptedCloseCB),
m_empty(mutex, true), m_empty(mutex, true),
m_closed(false) m_closed(false)
@ -36,6 +37,7 @@ CBufferedOutputStream::CBufferedOutputStream(
CBufferedOutputStream::~CBufferedOutputStream() CBufferedOutputStream::~CBufferedOutputStream()
{ {
delete m_closeCB; delete m_closeCB;
delete m_fillCB;
} }
const void* const void*
@ -68,7 +70,6 @@ CBufferedOutputStream::close()
} }
m_closed = true; m_closed = true;
m_buffer.pop(m_buffer.getSize());
if (m_closeCB != NULL) { if (m_closeCB != NULL) {
m_closeCB->run(); m_closeCB->run();
} }
@ -82,7 +83,13 @@ CBufferedOutputStream::write(const void* buffer, UInt32 n)
throw XIOClosed(); throw XIOClosed();
} }
bool wasEmpty = (m_buffer.getSize() == 0);
m_buffer.write(buffer, n); m_buffer.write(buffer, n);
if (wasEmpty && n > 0) {
if (m_fillCB != NULL) {
m_fillCB->run();
}
}
return n; return n;
} }

View File

@ -36,8 +36,11 @@ public:
The \c mutex must not be NULL and will be used to ensure thread The \c mutex must not be NULL and will be used to ensure thread
safe access. If \c adoptedCloseCB is not NULL it will be called safe access. If \c adoptedCloseCB is not NULL it will be called
when close() is called, allowing the creator to detect the close. when close() is called, allowing the creator to detect the close.
If \c adoptedFillCB is not NULL, it will be called whenever the
buffer becomes non-empty.
*/ */
CBufferedOutputStream(CMutex* mutex, IJob* adoptedCloseCB); CBufferedOutputStream(CMutex* mutex,
IJob* adoptedFillCB, IJob* adoptedCloseCB);
~CBufferedOutputStream(); ~CBufferedOutputStream();
//! @name manipulators //! @name manipulators
@ -79,6 +82,7 @@ public:
private: private:
CMutex* m_mutex; CMutex* m_mutex;
IJob* m_fillCB;
IJob* m_closeCB; IJob* m_closeCB;
CCondVar<bool> m_empty; CCondVar<bool> m_empty;
CStreamBuffer m_buffer; CStreamBuffer m_buffer;

View File

@ -109,6 +109,12 @@ CThread::waitForEvent(double timeout) const
return s_map[ARCH->waitForEvent(m_thread, timeout)]; return s_map[ARCH->waitForEvent(m_thread, timeout)];
} }
void
CThread::unblock() const
{
ARCH->unblockThread(m_thread);
}
void* void*
CThread::getResult() const CThread::getResult() const
{ {

View File

@ -180,6 +180,15 @@ public:
*/ */
EWaitResult waitForEvent(double timeout = -1.0) const; EWaitResult waitForEvent(double timeout = -1.0) const;
//! Unblock thread in system call
/*!
Cause a thread that's in a blocking system call to return. This
call may return before the thread is unblocked. If the thread is
not in a blocking system call, this call has no effect. This does
not cause CMutex::lock() or CCondVar::wait() to return prematurely.
*/
void unblock() const;
//! Get the exit result //! Get the exit result
/*! /*!
Returns the exit result. This does an implicit wait(). It returns Returns the exit result. This does an implicit wait(). It returns

View File

@ -13,11 +13,16 @@
*/ */
#include "CTCPListenSocket.h" #include "CTCPListenSocket.h"
#include "CTCPSocket.h"
#include "CNetworkAddress.h" #include "CNetworkAddress.h"
#include "XIO.h" #include "CSocketMultiplexer.h"
#include "CTCPSocket.h"
#include "TSocketMultiplexerMethodJob.h"
#include "XSocket.h" #include "XSocket.h"
#include "CThread.h" #include "XIO.h"
#include "CEvent.h"
#include "CEventQueue.h"
#include "CLock.h"
#include "CMutex.h"
#include "CArch.h" #include "CArch.h"
#include "XArch.h" #include "XArch.h"
@ -25,8 +30,10 @@
// CTCPListenSocket // CTCPListenSocket
// //
CTCPListenSocket::CTCPListenSocket() CTCPListenSocket::CTCPListenSocket() :
m_target(NULL)
{ {
m_mutex = new CMutex;
try { try {
m_socket = ARCH->newSocket(IArchNetwork::kINET, IArchNetwork::kSTREAM); m_socket = ARCH->newSocket(IArchNetwork::kINET, IArchNetwork::kSTREAM);
} }
@ -38,19 +45,28 @@ CTCPListenSocket::CTCPListenSocket()
CTCPListenSocket::~CTCPListenSocket() CTCPListenSocket::~CTCPListenSocket()
{ {
try { try {
if (m_socket != NULL) {
CSocketMultiplexer::getInstance()->removeSocket(this);
ARCH->closeSocket(m_socket); ARCH->closeSocket(m_socket);
} }
}
catch (...) { catch (...) {
// ignore // ignore
} }
delete m_mutex;
} }
void void
CTCPListenSocket::bind(const CNetworkAddress& addr) CTCPListenSocket::bind(const CNetworkAddress& addr)
{ {
try { try {
CLock lock(m_mutex);
ARCH->bindSocket(m_socket, addr.getAddress()); ARCH->bindSocket(m_socket, addr.getAddress());
ARCH->listenOnSocket(m_socket); ARCH->listenOnSocket(m_socket);
CSocketMultiplexer::getInstance()->addSocket(this,
new TSocketMultiplexerMethodJob<CTCPListenSocket>(
this, &CTCPListenSocket::serviceListening,
m_socket, true, false));
} }
catch (XArchNetworkAddressInUse& e) { catch (XArchNetworkAddressInUse& e) {
throw XSocketAddressInUse(e.what()); throw XSocketAddressInUse(e.what());
@ -63,32 +79,27 @@ CTCPListenSocket::bind(const CNetworkAddress& addr)
IDataSocket* IDataSocket*
CTCPListenSocket::accept() CTCPListenSocket::accept()
{ {
// accept asynchronously so we can check for cancellation
IArchNetwork::CPollEntry pfds[1];
pfds[0].m_socket = m_socket;
pfds[0].m_events = IArchNetwork::kPOLLIN;
for (;;) {
ARCH->testCancelThread();
try { try {
const int status = ARCH->pollSocket(pfds, 1, 0.01); CSocketMultiplexer::getInstance()->addSocket(this,
if (status > 0 && new TSocketMultiplexerMethodJob<CTCPListenSocket>(
(pfds[0].m_revents & IArchNetwork::kPOLLIN) != 0) { this, &CTCPListenSocket::serviceListening,
m_socket, true, false));
return new CTCPSocket(ARCH->acceptSocket(m_socket, NULL)); return new CTCPSocket(ARCH->acceptSocket(m_socket, NULL));
} }
}
catch (XArchNetwork&) { catch (XArchNetwork&) {
// ignore and retry return NULL;
}
} }
} }
void void
CTCPListenSocket::close() CTCPListenSocket::close()
{ {
CLock lock(m_mutex);
if (m_socket == NULL) { if (m_socket == NULL) {
throw XIOClosed(); throw XIOClosed();
} }
try { try {
CSocketMultiplexer::getInstance()->removeSocket(this);
ARCH->closeSocket(m_socket); ARCH->closeSocket(m_socket);
m_socket = NULL; m_socket = NULL;
} }
@ -96,3 +107,27 @@ CTCPListenSocket::close()
throw XSocketIOClose(e.what()); throw XSocketIOClose(e.what());
} }
} }
void
CTCPListenSocket::setEventTarget(void* target)
{
CLock lock(m_mutex);
m_target = target;
}
ISocketMultiplexerJob*
CTCPListenSocket::serviceListening(ISocketMultiplexerJob* job,
bool read, bool, bool error)
{
if (error) {
close();
return NULL;
}
if (read) {
CEventQueue::getInstance()->addEvent(
CEvent(getConnectingEvent(), m_target, NULL));
// stop polling on this socket until the client accepts
return NULL;
}
return job;
}

View File

@ -18,6 +18,9 @@
#include "IListenSocket.h" #include "IListenSocket.h"
#include "IArchNetwork.h" #include "IArchNetwork.h"
class CMutex;
class ISocketMultiplexerJob;
//! TCP listen socket //! TCP listen socket
/*! /*!
A listen socket using TCP. A listen socket using TCP.
@ -30,12 +33,20 @@ public:
// ISocket overrides // ISocket overrides
virtual void bind(const CNetworkAddress&); virtual void bind(const CNetworkAddress&);
virtual void close(); virtual void close();
virtual void setEventTarget(void*);
// IListenSocket overrides // IListenSocket overrides
virtual IDataSocket* accept(); virtual IDataSocket* accept();
private:
ISocketMultiplexerJob*
serviceListening(ISocketMultiplexerJob*,
bool, bool, bool);
private: private:
CArchSocket m_socket; CArchSocket m_socket;
CMutex* m_mutex;
void* m_target;
}; };
#endif #endif

View File

@ -13,14 +13,16 @@
*/ */
#include "CTCPSocket.h" #include "CTCPSocket.h"
#include "CNetworkAddress.h"
#include "CSocketMultiplexer.h"
#include "TSocketMultiplexerMethodJob.h"
#include "CBufferedInputStream.h" #include "CBufferedInputStream.h"
#include "CBufferedOutputStream.h" #include "CBufferedOutputStream.h"
#include "CNetworkAddress.h"
#include "XIO.h"
#include "XSocket.h" #include "XSocket.h"
#include "XIO.h"
#include "CLock.h" #include "CLock.h"
#include "CMutex.h" #include "CMutex.h"
#include "CThread.h" #include "CEventQueue.h"
#include "TMethodJob.h" #include "TMethodJob.h"
#include "CArch.h" #include "CArch.h"
#include "XArch.h" #include "XArch.h"
@ -45,21 +47,18 @@ CTCPSocket::CTCPSocket(CArchSocket socket) :
{ {
assert(m_socket != NULL); assert(m_socket != NULL);
init();
// socket starts in connected state // socket starts in connected state
m_connected = kReadWrite; init();
setState(kReadWrite, true);
// start handling socket
m_thread = new CThread(new TMethodJob<CTCPSocket>(
this, &CTCPSocket::ioThread));
} }
CTCPSocket::~CTCPSocket() CTCPSocket::~CTCPSocket()
{ {
try { try {
if (m_socket != NULL) {
close(); close();
} }
}
catch (...) { catch (...) {
// ignore // ignore
} }
@ -87,44 +86,28 @@ CTCPSocket::bind(const CNetworkAddress& addr)
void void
CTCPSocket::close() CTCPSocket::close()
{ {
// see if buffers should be flushed
bool doFlush = false;
{
CLock lock(m_mutex);
doFlush = (m_thread != NULL && (m_connected & kWrite) != 0);
}
// flush buffers // flush buffers
if (doFlush) {
m_output->flush(); m_output->flush();
}
// cause ioThread to exit // now closed
if (m_socket != NULL) { setState(kClosed, true);
CLock lock(m_mutex);
// close buffers
try { try {
ARCH->closeSocketForRead(m_socket); m_input->close();
} }
catch (XArchNetwork&) { catch (...) {
// ignore // ignore
} }
try { try {
ARCH->closeSocketForWrite(m_socket); m_output->close();
} }
catch (XArchNetwork&) { catch (...) {
// ignore // ignore
} }
m_connected = kClosed;
}
// wait for thread
if (m_thread != NULL) {
m_thread->wait();
delete m_thread;
m_thread = NULL;
}
// close socket // close socket
CLock lock(m_mutex);
if (m_socket != NULL) { if (m_socket != NULL) {
try { try {
ARCH->closeSocket(m_socket); ARCH->closeSocket(m_socket);
@ -136,66 +119,29 @@ CTCPSocket::close()
} }
} }
void
CTCPSocket::setEventTarget(void* target)
{
CLock lock(m_mutex);
m_target = target;
}
void void
CTCPSocket::connect(const CNetworkAddress& addr) CTCPSocket::connect(const CNetworkAddress& addr)
{ {
do {
// connect asynchronously so we can check for cancellation.
// we can't wrap setting and resetting the blocking flag in
// the c'tor/d'tor of a class (to make resetting automatic)
// because setBlockingOnSocket() can throw and it might be
// called while unwinding the stack due to a throw.
try { try {
ARCH->setBlockingOnSocket(m_socket, false); // FIXME -- don't throw if in progress, just return that info
ARCH->connectSocket(m_socket, addr.getAddress()); ARCH->connectSocket(m_socket, addr.getAddress());
ARCH->setBlockingOnSocket(m_socket, true); setState(kReadWrite, true);
// connected
break;
} }
catch (XArchNetworkConnecting&) { catch (XArchNetworkConnecting&) {
// connection is in progress // connection is in progress
ARCH->setBlockingOnSocket(m_socket, true); setState(kConnecting, true);
}
catch (XArchNetwork& e) {
ARCH->setBlockingOnSocket(m_socket, true);
throw XSocketConnect(e.what());
}
// wait for connection or failure
IArchNetwork::CPollEntry pfds[1];
pfds[0].m_socket = m_socket;
pfds[0].m_events = IArchNetwork::kPOLLOUT;
for (;;) {
ARCH->testCancelThread();
try {
const int status = ARCH->pollSocket(pfds, 1, 0.01);
if (status > 0) {
if ((pfds[0].m_revents & (IArchNetwork::kPOLLERR |
IArchNetwork::kPOLLNVAL)) != 0) {
// connection failed
ARCH->throwErrorOnSocket(m_socket);
}
if ((pfds[0].m_revents & IArchNetwork::kPOLLOUT) != 0) {
// connection may have failed or succeeded
ARCH->throwErrorOnSocket(m_socket);
// connected!
break;
}
}
} }
catch (XArchNetwork& e) { catch (XArchNetwork& e) {
throw XSocketConnect(e.what()); throw XSocketConnect(e.what());
} }
} }
} while (false);
// start servicing the socket
m_connected = kReadWrite;
m_thread = new CThread(new TMethodJob<CTCPSocket>(
this, &CTCPSocket::ioThread));
}
IInputStream* IInputStream*
CTCPSocket::getInputStream() CTCPSocket::getInputStream()
@ -213,14 +159,23 @@ void
CTCPSocket::init() CTCPSocket::init()
{ {
m_mutex = new CMutex; m_mutex = new CMutex;
m_thread = NULL;
m_connected = kClosed;
m_input = new CBufferedInputStream(m_mutex, m_input = new CBufferedInputStream(m_mutex,
new TMethodJob<CTCPSocket>(
this, &CTCPSocket::emptyInput),
new TMethodJob<CTCPSocket>( new TMethodJob<CTCPSocket>(
this, &CTCPSocket::closeInput)); this, &CTCPSocket::closeInput));
m_output = new CBufferedOutputStream(m_mutex, m_output = new CBufferedOutputStream(m_mutex,
new TMethodJob<CTCPSocket>(
this, &CTCPSocket::fillOutput),
new TMethodJob<CTCPSocket>( new TMethodJob<CTCPSocket>(
this, &CTCPSocket::closeOutput)); this, &CTCPSocket::closeOutput));
m_state = kUnconnected;
m_target = NULL;
m_job = NULL;
// make socket non-blocking
// FIXME -- check for error
ARCH->setBlockingOnSocket(m_socket, false);
// turn off Nagle algorithm. we send lots of very short messages // turn off Nagle algorithm. we send lots of very short messages
// that should be sent without (much) delay. for example, the // that should be sent without (much) delay. for example, the
@ -241,115 +196,101 @@ CTCPSocket::init()
} }
} }
void ISocketMultiplexerJob*
CTCPSocket::ioThread(void*) CTCPSocket::newMultiplexerJob(JobFunc func, bool readable, bool writable)
{ {
try { return new TSocketMultiplexerMethodJob<CTCPSocket>(
ioService(); this, func, m_socket, readable, writable);
ioCleanup();
}
catch (...) {
ioCleanup();
throw;
}
} }
void ISocketMultiplexerJob*
CTCPSocket::ioCleanup() CTCPSocket::setState(State state, bool setJob)
{ {
try { if (m_state == state || m_state == kClosed) {
m_input->close(); return m_job;
}
catch (...) {
// ignore
}
try {
m_output->close();
}
catch (...) {
// ignore
}
} }
void State oldState = m_state;
CTCPSocket::ioService() m_state = state;
{
assert(m_socket != NULL);
// now service the connection bool read = (m_input->getSize() > 0);
IArchNetwork::CPollEntry pfds[1]; bool write = (m_output->getSize() > 0);
pfds[0].m_socket = m_socket; CEvent::Type eventType = 0;
for (;;) { m_job = NULL;
{ switch (m_state) {
// choose events to poll for case kUnconnected:
CLock lock(m_mutex); assert(0 && "cannot re-enter unconnected state");
pfds[0].m_events = 0; break;
if (m_connected == 0) {
return;
}
if ((m_connected & kRead) != 0) {
// still open for reading
pfds[0].m_events |= IArchNetwork::kPOLLIN;
}
if ((m_connected & kWrite) != 0 && m_output->getSize() > 0) {
// data queued for writing
pfds[0].m_events |= IArchNetwork::kPOLLOUT;
}
}
try { case kConnecting:
// check for status m_job = newMultiplexerJob(&CTCPSocket::serviceConnecting, false, true);
const int status = ARCH->pollSocket(pfds, 1, 0.01); break;
// transfer data and handle errors case kReadWrite:
if (status == 1) { if (oldState == kConnecting) {
if ((pfds[0].m_revents & (IArchNetwork::kPOLLERR | eventType = IDataSocket::getConnectedEvent();
IArchNetwork::kPOLLNVAL)) != 0) { }
// stream is no good anymore so bail m_job = newMultiplexerJob(&CTCPSocket::serviceConnected, true, write);
CLock lock(m_mutex); break;
case kReadOnly:
if (!write) {
eventType = IDataSocket::getShutdownOutputEvent();
}
if (oldState == kWriteOnly) {
goto shutdown;
}
m_job = newMultiplexerJob(&CTCPSocket::serviceConnected, true, write);
break;
case kWriteOnly:
if (!read) {
m_input->hangup(); m_input->hangup();
return; eventType = IDataSocket::getShutdownInputEvent();
} }
if (oldState == kReadOnly) {
goto shutdown;
}
m_job = newMultiplexerJob(&CTCPSocket::serviceConnected, false, write);
break;
// read some data case kShutdown:
if (pfds[0].m_revents & IArchNetwork::kPOLLIN) { shutdown:
UInt8 buffer[4096]; if (!read && !write) {
size_t n = ARCH->readSocket(m_socket, eventType = ISocket::getDisconnectedEvent();
buffer, sizeof(buffer)); m_state = kClosed;
CLock lock(m_mutex);
if (n > 0) {
m_input->write(buffer, n);
} }
else { else {
// stream hungup m_state = kShutdown;
}
break;
case kClosed:
m_input->hangup(); m_input->hangup();
m_connected &= ~kRead; if (oldState == kConnecting) {
eventType = IDataSocket::getConnectionFailedEvent();
} }
else {
eventType = ISocket::getDisconnectedEvent();
}
break;
} }
// write some data // notify
if (pfds[0].m_revents & IArchNetwork::kPOLLOUT) { if (eventType != 0) {
CLock lock(m_mutex); sendEvent(eventType);
}
// get amount of data to write // cut over to new job. multiplexer will delete the old job.
UInt32 n = m_output->getSize(); if (setJob) {
if (m_job == NULL) {
// write data CSocketMultiplexer::getInstance()->removeSocket(this);
const void* buffer = m_output->peek(n); }
size_t n2 = ARCH->writeSocket(m_socket, buffer, n); else {
CSocketMultiplexer::getInstance()->addSocket(this, m_job);
// discard written data
if (n2 > 0) {
m_output->pop(n2);
}
}
}
}
catch (XArchNetwork&) {
// socket has failed
return;
} }
} }
return m_job;
} }
void void
@ -358,7 +299,7 @@ CTCPSocket::closeInput(void*)
// note -- m_mutex should already be locked // note -- m_mutex should already be locked
try { try {
ARCH->closeSocketForRead(m_socket); ARCH->closeSocketForRead(m_socket);
m_connected &= ~kRead; setState(kWriteOnly, true);
} }
catch (XArchNetwork&) { catch (XArchNetwork&) {
// ignore // ignore
@ -370,10 +311,156 @@ CTCPSocket::closeOutput(void*)
{ {
// note -- m_mutex should already be locked // note -- m_mutex should already be locked
try { try {
ARCH->closeSocketForWrite(m_socket); // ARCH->closeSocketForWrite(m_socket);
m_connected &= ~kWrite; setState(kReadOnly, true);
} }
catch (XArchNetwork&) { catch (XArchNetwork&) {
// ignore // ignore
} }
} }
void
CTCPSocket::emptyInput(void*)
{
// note -- m_mutex should already be locked
bool write = (m_output->getSize() > 0);
if (m_state == kWriteOnly && !write) {
m_state = kShutdown;
}
if (m_state == kWriteOnly) {
m_job = newMultiplexerJob(&CTCPSocket::serviceConnected, false, write);
CSocketMultiplexer::getInstance()->addSocket(this, m_job);
m_input->hangup();
sendEvent(IDataSocket::getShutdownInputEvent());
}
else if (m_state == kShutdown) {
m_job = NULL;
CSocketMultiplexer::getInstance()->removeSocket(this);
if (!write) {
sendEvent(ISocket::getDisconnectedEvent());
m_state = kClosed;
}
}
}
void
CTCPSocket::fillOutput(void*)
{
// note -- m_mutex should already be locked
if (m_state == kReadWrite) {
m_job = newMultiplexerJob(&CTCPSocket::serviceConnected, true, true);
CSocketMultiplexer::getInstance()->addSocket(this, m_job);
}
else if (m_state == kWriteOnly) {
m_job = newMultiplexerJob(&CTCPSocket::serviceConnected, false, true);
CSocketMultiplexer::getInstance()->addSocket(this, m_job);
}
}
ISocketMultiplexerJob*
CTCPSocket::serviceConnecting(ISocketMultiplexerJob* job,
bool, bool write, bool error)
{
CLock lock(m_mutex);
if (write && !error) {
try {
// connection may have failed or succeeded
ARCH->throwErrorOnSocket(m_socket);
}
catch (XArchNetwork&) {
error = true;
}
}
if (error) {
return setState(kClosed, false);
}
if (write) {
return setState(kReadWrite, false);
}
return job;
}
ISocketMultiplexerJob*
CTCPSocket::serviceConnected(ISocketMultiplexerJob* job,
bool read, bool write, bool error)
{
CLock lock(m_mutex);
if (error) {
return setState(kClosed, false);
}
if (write) {
// get amount of data to write
UInt32 n = m_output->getSize();
// write data
try {
const void* buffer = m_output->peek(n);
size_t n2 = ARCH->writeSocket(m_socket, buffer, n);
// discard written data
if (n2 > 0) {
m_output->pop(n2);
}
}
catch (XArchNetworkDisconnected&) {
// stream hungup
return setState(kReadOnly, false);
}
}
if (read) {
UInt8 buffer[4096];
size_t n = ARCH->readSocket(m_socket, buffer, sizeof(buffer));
if (n > 0) {
// slurp up as much as possible
do {
m_input->write(buffer, n);
try {
n = ARCH->readSocket(m_socket, buffer, sizeof(buffer));
}
catch (XArchNetworkWouldBlock&) {
break;
}
} while (n > 0);
// notify
sendEvent(IDataSocket::getInputEvent());
}
else {
// stream hungup
return setState(kWriteOnly, false);
}
}
if (write && m_output->getSize() == 0) {
if (m_state == kReadOnly) {
ARCH->closeSocketForWrite(m_socket);
sendEvent(IDataSocket::getShutdownOutputEvent());
m_job = newMultiplexerJob(&CTCPSocket::serviceConnected,
true, false);
job = m_job;
}
else if (m_state == kReadWrite || m_state == kReadOnly) {
m_job = newMultiplexerJob(&CTCPSocket::serviceConnected,
true, false);
job = m_job;
}
else if (m_state == kWriteOnly) {
m_job = NULL;
job = m_job;
}
}
return job;
}
void
CTCPSocket::sendEvent(CEvent::Type type)
{
CEventQueue::getInstance()->addEvent(CEvent(type, m_target, NULL));
}

View File

@ -16,6 +16,7 @@
#define CTCPSOCKET_H #define CTCPSOCKET_H
#include "IDataSocket.h" #include "IDataSocket.h"
#include "CEvent.h"
#include "BasicTypes.h" #include "BasicTypes.h"
#include "IArchNetwork.h" #include "IArchNetwork.h"
@ -23,6 +24,7 @@ class CMutex;
class CThread; class CThread;
class CBufferedInputStream; class CBufferedInputStream;
class CBufferedOutputStream; class CBufferedOutputStream;
class ISocketMultiplexerJob;
//! TCP data socket //! TCP data socket
/*! /*!
@ -37,6 +39,7 @@ public:
// ISocket overrides // ISocket overrides
virtual void bind(const CNetworkAddress&); virtual void bind(const CNetworkAddress&);
virtual void close(); virtual void close();
virtual void setEventTarget(void*);
// IDataSocket overrides // IDataSocket overrides
virtual void connect(const CNetworkAddress&); virtual void connect(const CNetworkAddress&);
@ -44,23 +47,52 @@ public:
virtual IOutputStream* getOutputStream(); virtual IOutputStream* getOutputStream();
private: private:
enum State {
kUnconnected,
kConnecting,
kReadWrite,
kReadOnly,
kWriteOnly,
kShutdown,
kClosed
};
void init(); void init();
void ioThread(void*);
void ioCleanup(); ISocketMultiplexerJob*
void ioService(); setState(State, bool setJob);
void closeInput(void*); void closeInput(void*);
void closeOutput(void*); void closeOutput(void*);
void emptyInput(void*);
void fillOutput(void*);
ISocketMultiplexerJob*
serviceConnecting(ISocketMultiplexerJob*,
bool, bool, bool);
ISocketMultiplexerJob*
serviceConnected(ISocketMultiplexerJob*,
bool, bool, bool);
typedef ISocketMultiplexerJob* (CTCPSocket::*JobFunc)(
ISocketMultiplexerJob*,
bool, bool, bool);
ISocketMultiplexerJob*
newMultiplexerJob(JobFunc,
bool readable, bool writable);
void sendEvent(CEvent::Type);
private: private:
enum { kClosed = 0, kRead = 1, kWrite = 2, kReadWrite = 3 };
CArchSocket m_socket; CArchSocket m_socket;
CBufferedInputStream* m_input; CBufferedInputStream* m_input;
CBufferedOutputStream* m_output; CBufferedOutputStream* m_output;
CMutex* m_mutex; CMutex* m_mutex;
CThread* m_thread; State m_state;
UInt32 m_connected; void* m_target;
ISocketMultiplexerJob* m_job;
}; };
#endif #endif

55
lib/net/IDataSocket.cpp Normal file
View File

@ -0,0 +1,55 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 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 "IDataSocket.h"
//
// IDataSocket
//
CEvent::Type IDataSocket::s_connectedEvent = CEvent::kUnknown;
CEvent::Type IDataSocket::s_failedEvent = CEvent::kUnknown;
CEvent::Type IDataSocket::s_inputEvent = CEvent::kUnknown;
CEvent::Type IDataSocket::s_shutdownInputEvent = CEvent::kUnknown;
CEvent::Type IDataSocket::s_shutdownOutputEvent = CEvent::kUnknown;
CEvent::Type
IDataSocket::getConnectedEvent()
{
return CEvent::registerTypeOnce(s_connectedEvent);
}
CEvent::Type
IDataSocket::getConnectionFailedEvent()
{
return CEvent::registerTypeOnce(s_failedEvent);
}
CEvent::Type
IDataSocket::getInputEvent()
{
return CEvent::registerTypeOnce(s_inputEvent);
}
CEvent::Type
IDataSocket::getShutdownInputEvent()
{
return CEvent::registerTypeOnce(s_shutdownInputEvent);
}
CEvent::Type
IDataSocket::getShutdownOutputEvent()
{
return CEvent::registerTypeOnce(s_shutdownOutputEvent);
}

View File

@ -46,6 +46,7 @@ public:
stream will shutdown the socket for reading. stream will shutdown the socket for reading.
*/ */
virtual IInputStream* getInputStream() = 0; virtual IInputStream* getInputStream() = 0;
//! Get output stream //! Get output stream
/*! /*!
Returns the output stream for writing to the socket. Closing this Returns the output stream for writing to the socket. Closing this
@ -53,11 +54,60 @@ public:
*/ */
virtual IOutputStream* getOutputStream() = 0; virtual IOutputStream* getOutputStream() = 0;
//@}
//! @name accessors
//@{
//! Get connected event type
/*!
Returns the socket connected event type. A socket sends this
event when a remote connection has been established.
*/
static CEvent::Type getConnectedEvent();
//! Get connection failed event type
/*!
Returns the socket connection failed event type. A socket sends
this event when an attempt to connect to a remote port has failed.
*/
static CEvent::Type getConnectionFailedEvent();
//! Get input event type
/*!
Returns the socket input event type. A socket sends this
event when data is available to read from the input stream.
*/
static CEvent::Type getInputEvent();
//! Get shutdown input event type
/*!
Returns the socket shutdown input event type. A socket sends this
event when the remote side of the connection has shutdown for
writing and there is no more data to read from the socket.
*/
static CEvent::Type getShutdownInputEvent();
//! Get shutdown input event type
/*!
Returns the socket shutdown input event type. A socket sends this
event when the remote side of the connection has shutdown for
writing and there is no more data to read from the socket.
*/
static CEvent::Type getShutdownOutputEvent();
//@} //@}
// ISocket overrides // ISocket overrides
virtual void bind(const CNetworkAddress&) = 0; virtual void bind(const CNetworkAddress&) = 0;
virtual void close() = 0; virtual void close() = 0;
virtual void setEventTarget(void*) = 0;
private:
static CEvent::Type s_connectedEvent;
static CEvent::Type s_failedEvent;
static CEvent::Type s_inputEvent;
static CEvent::Type s_shutdownInputEvent;
static CEvent::Type s_shutdownOutputEvent;
}; };
#endif #endif

27
lib/net/IListenSocket.cpp Normal file
View File

@ -0,0 +1,27 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 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 "IListenSocket.h"
//
// IListenSocket
//
CEvent::Type IListenSocket::s_connectingEvent = CEvent::kUnknown;
CEvent::Type
IListenSocket::getConnectingEvent()
{
return CEvent::registerTypeOnce(s_connectingEvent);
}

View File

@ -31,18 +31,32 @@ public:
//! Accept connection //! Accept connection
/*! /*!
Wait for and accept a connection, returning a socket representing Accept a connection, returning a socket representing the full-duplex
the full-duplex data stream. data stream. Returns NULL if no socket is waiting to be accepted.
This is only valid after a call to \c bind().
(cancellation point)
*/ */
virtual IDataSocket* accept() = 0; virtual IDataSocket* accept() = 0;
//@}
//! @name accessors
//@{
//! Get connecting event type
/*!
Returns the socket connecting event type. A socket sends this
event when a remote connection is waiting to be accepted.
*/
static CEvent::Type getConnectingEvent();
//@} //@}
// ISocket overrides // ISocket overrides
virtual void bind(const CNetworkAddress&) = 0; virtual void bind(const CNetworkAddress&) = 0;
virtual void close() = 0; virtual void close() = 0;
virtual void setEventTarget(void*) = 0;
private:
static CEvent::Type s_connectingEvent;
}; };
#endif #endif

27
lib/net/ISocket.cpp Normal file
View File

@ -0,0 +1,27 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 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 "ISocket.h"
//
// ISocket
//
CEvent::Type ISocket::s_disconnectedEvent = CEvent::kUnknown;
CEvent::Type
ISocket::getDisconnectedEvent()
{
return CEvent::registerTypeOnce(s_disconnectedEvent);
}

View File

@ -16,6 +16,7 @@
#define ISOCKET_H #define ISOCKET_H
#include "IInterface.h" #include "IInterface.h"
#include "CEvent.h"
class CNetworkAddress; class CNetworkAddress;
@ -40,7 +41,28 @@ public:
*/ */
virtual void close() = 0; virtual void close() = 0;
//! Set the socket's event target
/*!
Sets the target of any events sent by the socket. The default is NULL.
*/
virtual void setEventTarget(void*) = 0;
//@} //@}
//! @name accessors
//@{
//! Get disconnected event type
/*!
Returns the socket disconnected event type. A socket sends this
event when the remote side of the socket has disconnected or
shutdown.
*/
static CEvent::Type getDisconnectedEvent();
//@}
private:
static CEvent::Type s_disconnectedEvent;
}; };
#endif #endif

View File

@ -26,11 +26,16 @@ MAINTAINERCLEANFILES = \
noinst_LIBRARIES = libnet.a noinst_LIBRARIES = libnet.a
libnet_a_SOURCES = \ libnet_a_SOURCES = \
CNetworkAddress.cpp \ CNetworkAddress.cpp \
CSocketMultiplexer.cpp \
CTCPListenSocket.cpp \ CTCPListenSocket.cpp \
CTCPSocket.cpp \ CTCPSocket.cpp \
CTCPSocketFactory.cpp \ CTCPSocketFactory.cpp \
IDataSocket.cpp \
IListenSocket.cpp \
ISocket.cpp \
XSocket.cpp \ XSocket.cpp \
CNetworkAddress.h \ CNetworkAddress.h \
CSocketMultiplexer.h \
CTCPListenSocket.h \ CTCPListenSocket.h \
CTCPSocket.h \ CTCPSocket.h \
CTCPSocketFactory.h \ CTCPSocketFactory.h \
@ -38,6 +43,8 @@ libnet_a_SOURCES = \
IListenSocket.h \ IListenSocket.h \
ISocket.h \ ISocket.h \
ISocketFactory.h \ ISocketFactory.h \
ISocketMultiplexerJob.h \
TSocketMultiplexerMethodJob.h \
XSocket.h \ XSocket.h \
$(NULL) $(NULL)
INCLUDES = \ INCLUDES = \

View File

@ -0,0 +1,182 @@
/*
* 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 "CXWindowsEventQueue.h"
#include "CEvent.h"
#include "CThread.h"
#if UNIX_LIKE
# if HAVE_POLL
# include <sys/poll.h>
# else
# if HAVE_SYS_SELECT_H
# include <sys/select.h>
# endif
# if HAVE_SYS_TIME_H
# include <sys/time.h>
# endif
# if HAVE_SYS_TYPES_H
# include <sys/types.h>
# endif
# if HAVE_UNISTD_H
# include <unistd.h>
# endif
# endif
#endif
//
// CEventQueueTimer
//
class CEventQueueTimer { };
//
// CXWindowsEventQueue
//
CXWindowsEventQueue::CXWindowsEventQueue(Display* display) :
m_display(display)
{
m_userEvent = XInternAtom(m_display, "SYNERGY_USER_EVENT", False);
XSetWindowAttributes attr;
m_window = XCreateWindow(m_display, DefaultRootWindow(m_display),
0, 0, 1, 1, 0, 0, InputOnly, CopyFromParent,
0, &attr);
}
CXWindowsEventQueue::~CXWindowsEventQueue()
{
XDestroyWindow(m_display, m_window);
}
void
CXWindowsEventQueue::processSystemEvent(CEvent& event)
{
event = CEvent(CEvent::kSystem, getSystemTarget(), &m_event);
}
void
CXWindowsEventQueue::processClientMessage(CEvent& event)
{
assert(m_event.xany.type == ClientMessage);
// handle user events specially
if (m_event.xclient.message_type == m_userEvent) {
// get event data
CEventData data = removeEventData(m_event.xclient.data.l[1]);
// create event
event = CEvent(static_cast<size_t>(m_event.xclient.data.l[0]),
data.first, data.second);
}
else {
processSystemEvent(event);
}
}
void
CXWindowsEventQueue::waitForEvent(double dtimeout)
{
// use poll() to wait for a message from the X server or for timeout.
// this is a good deal more efficient than polling and sleeping.
#if HAVE_POLL
struct pollfd pfds[1];
pfds[0].fd = ConnectionNumber(m_display);
pfds[0].events = POLLIN;
int timeout = (dtimeout < 0.0) ? -1 :
static_cast<int>(1000.0 * dtimeout);
#else
struct timeval timeout;
struct timeval* timeoutPtr;
if (dtimeout < 0.0) {
timeoutPtr = NULL;
}
else {
timeout.tv_sec = static_cast<int>(dtimeout);
timeout.tv_usec = static_cast<int>(1.0e+6 *
(dtimeout - timeout.tv_sec));
timeoutPtr = &timeout;
}
// initialize file descriptor sets
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(ConnectionNumber(m_display), &rfds);
#endif
// wait for message from X server or for timeout. also check
// if the thread has been cancelled. poll() should return -1
// with EINTR when the thread is cancelled.
CThread::testCancel();
#if HAVE_POLL
poll(pfds, 1, timeout);
#else
select(ConnectionNumber(m_display) + 1,
SELECT_TYPE_ARG234 &rfds,
SELECT_TYPE_ARG234 NULL,
SELECT_TYPE_ARG234 NULL,
SELECT_TYPE_ARG5 timeoutPtr);
#endif
CThread::testCancel();
}
bool
CXWindowsEventQueue::doGetEvent(CEvent& event)
{
// get next event
XNextEvent(m_display, &m_event);
// process event
if (m_event.xany.type == ClientMessage) {
processClientMessage(event);
}
else {
processSystemEvent(event);
}
return true;
}
bool
CXWindowsEventQueue::doAddEvent(CEvent::Type type, UInt32 dataID)
{
// send ourself a message
XEvent xevent;
xevent.xclient.type = ClientMessage;
xevent.xclient.window = m_window;
xevent.xclient.message_type = m_userEvent;
xevent.xclient.format = 32;
xevent.xclient.data.l[0] = static_cast<long>(type);
xevent.xclient.data.l[1] = static_cast<long>(dataID);
return (XSendEvent(m_display, m_window, False, 0, &xevent) != 0);
}
bool
CXWindowsEventQueue::doIsEmpty() const
{
return (XPending(m_display) == 0);
}
CEventQueueTimer*
CXWindowsEventQueue::doNewTimer(double, bool) const
{
return new CEventQueueTimer();
}
void
CXWindowsEventQueue::doDeleteTimer(CEventQueueTimer* timer) const
{
delete timer;
}

View File

@ -0,0 +1,61 @@
/*
* 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 CXWINDOWSEVENTQUEUE_H
#define CXWINDOWSEVENTQUEUE_H
#include "CEventQueue.h"
#if defined(X_DISPLAY_MISSING)
# error X11 is required to build synergy
#else
# include <X11/Xlib.h>
#endif
//! Event queue for X11
class CXWindowsEventQueue : public CEventQueue {
public:
CXWindowsEventQueue(Display*);
virtual ~CXWindowsEventQueue();
//! @name manipulators
//@{
//@}
//! @name accessors
//@{
//@}
protected:
// CEventQueue overrides
virtual void waitForEvent(double timeout);
virtual bool doGetEvent(CEvent& event);
virtual bool doAddEvent(CEvent::Type type, UInt32 dataID);
virtual bool doIsEmpty() const;
virtual CEventQueueTimer*
doNewTimer(double duration, bool oneShot) const;
virtual void doDeleteTimer(CEventQueueTimer*) const;
private:
void processSystemEvent(CEvent& event);
void processClientMessage(CEvent& event);
private:
Display* m_display;
Window m_window;
Atom m_userEvent;
XEvent m_event;
};
#endif

View File

@ -49,6 +49,7 @@ libplatform_a_SOURCES = \
CXWindowsClipboardTextConverter.cpp \ CXWindowsClipboardTextConverter.cpp \
CXWindowsClipboardUCS2Converter.cpp \ CXWindowsClipboardUCS2Converter.cpp \
CXWindowsClipboardUTF8Converter.cpp \ CXWindowsClipboardUTF8Converter.cpp \
CXWindowsEventQueue.cpp \
CXWindowsKeyMapper.cpp \ CXWindowsKeyMapper.cpp \
CXWindowsScreen.cpp \ CXWindowsScreen.cpp \
CXWindowsScreenSaver.cpp \ CXWindowsScreenSaver.cpp \
@ -57,6 +58,7 @@ libplatform_a_SOURCES = \
CXWindowsClipboardTextConverter.h \ CXWindowsClipboardTextConverter.h \
CXWindowsClipboardUCS2Converter.h \ CXWindowsClipboardUCS2Converter.h \
CXWindowsClipboardUTF8Converter.h \ CXWindowsClipboardUTF8Converter.h \
CXWindowsEventQueue.h \
CXWindowsKeyMapper.h \ CXWindowsKeyMapper.h \
CXWindowsScreen.h \ CXWindowsScreen.h \
CXWindowsScreenSaver.h \ CXWindowsScreenSaver.h \