2005-06-25 04:21:26 +02:00
|
|
|
#include "precompiled.h"
|
|
|
|
|
2005-06-28 01:04:34 +02:00
|
|
|
#include "MessagePasserImpl.h"
|
2005-10-09 05:26:16 +02:00
|
|
|
#include "Messages.h"
|
2005-06-25 04:21:26 +02:00
|
|
|
|
2005-10-09 05:26:16 +02:00
|
|
|
#include "lib/timer.h"
|
2005-08-20 17:44:50 +02:00
|
|
|
|
2005-06-25 04:21:26 +02:00
|
|
|
using namespace AtlasMessage;
|
|
|
|
|
2005-08-20 17:44:50 +02:00
|
|
|
|
2005-10-31 04:36:50 +01:00
|
|
|
MessagePasserImpl::MessagePasserImpl()
|
2005-10-09 05:26:16 +02:00
|
|
|
: m_Trace(false)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2005-10-31 04:36:50 +01:00
|
|
|
void MessagePasserImpl::Add(IMessage* msg)
|
2005-06-25 04:21:26 +02:00
|
|
|
{
|
2005-08-20 17:44:50 +02:00
|
|
|
debug_assert(msg);
|
2005-10-31 04:36:50 +01:00
|
|
|
debug_assert(msg->GetType() == IMessage::Message);
|
2005-08-20 17:44:50 +02:00
|
|
|
|
2005-10-09 05:26:16 +02:00
|
|
|
if (m_Trace)
|
2005-10-31 04:36:50 +01:00
|
|
|
debug_printf("%8.3f add message: %s\n", get_time(), msg->GetName());
|
2005-10-09 05:26:16 +02:00
|
|
|
|
2005-06-25 04:21:26 +02:00
|
|
|
m_Mutex.Lock();
|
|
|
|
m_Queue.push(msg);
|
|
|
|
m_Mutex.Unlock();
|
|
|
|
}
|
|
|
|
|
2005-10-31 04:36:50 +01:00
|
|
|
IMessage* MessagePasserImpl::Retrieve()
|
2005-06-25 04:21:26 +02:00
|
|
|
{
|
2005-07-03 18:25:48 +02:00
|
|
|
// (It should be fairly easy to use a more efficient thread-safe queue,
|
|
|
|
// since there's only one thread adding items and one thread consuming;
|
|
|
|
// but it's not worthwhile yet.)
|
|
|
|
|
2005-06-25 04:21:26 +02:00
|
|
|
m_Mutex.Lock();
|
|
|
|
|
2005-10-31 04:36:50 +01:00
|
|
|
IMessage* msg = NULL;
|
2005-06-25 04:21:26 +02:00
|
|
|
if (! m_Queue.empty())
|
|
|
|
{
|
|
|
|
msg = m_Queue.front();
|
|
|
|
m_Queue.pop();
|
|
|
|
}
|
|
|
|
|
|
|
|
m_Mutex.Unlock();
|
|
|
|
|
2005-10-09 05:26:16 +02:00
|
|
|
// if (m_Trace && msg) debug_printf("%8.3f retrieved message: %s\n", get_time(), msg->GetType());
|
2005-08-20 17:44:50 +02:00
|
|
|
|
2005-06-25 04:21:26 +02:00
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
|
2005-11-03 04:49:57 +01:00
|
|
|
void MessagePasserImpl::Query(QueryMessage* qry, void(*timeoutCallback)())
|
2005-10-31 04:36:50 +01:00
|
|
|
{
|
|
|
|
debug_assert(qry);
|
|
|
|
debug_assert(qry->GetType() == IMessage::Query);
|
|
|
|
|
|
|
|
if (m_Trace)
|
|
|
|
debug_printf("%8.3f add query: %s\n", get_time(), qry->GetName());
|
|
|
|
|
|
|
|
// Initialise a semaphore, so we can block until the query has been handled
|
|
|
|
int err;
|
|
|
|
sem_t sem;
|
|
|
|
err = sem_init(&sem, 0, 0);
|
|
|
|
if (err != 0)
|
|
|
|
{
|
|
|
|
// Probably-fatal error
|
|
|
|
debug_warn("sem_init failed");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
qry->m_Semaphore = (void*)&sem;
|
|
|
|
|
|
|
|
m_Mutex.Lock();
|
|
|
|
m_Queue.push(qry);
|
|
|
|
m_Mutex.Unlock();
|
|
|
|
|
2005-11-03 04:49:57 +01:00
|
|
|
// Wait until the query handler has handled the query and called sem_post:
|
|
|
|
|
|
|
|
// At least on Win32, it is necessary for the UI thread to run its event
|
|
|
|
// loop to avoid deadlocking the system (particularly when the game
|
|
|
|
// tries to show a dialog box); so timeoutCallback is called whenever we
|
|
|
|
// think it's necessary for that to happen.
|
2005-11-04 01:02:43 +01:00
|
|
|
|
2005-11-03 04:49:57 +01:00
|
|
|
#if OS_WIN
|
|
|
|
// On Win32, use MsgWaitForMultipleObjects, which waits on the semaphore
|
|
|
|
// but is also interrupted by incoming Windows-messages.
|
2005-11-04 01:02:43 +01:00
|
|
|
while (0 != (err = sem_msgwait_np(&sem)))
|
2005-11-03 04:49:57 +01:00
|
|
|
#else
|
|
|
|
// TODO: On non-Win32, I have no idea whether the same problem exists; but
|
|
|
|
// it might do, so call the callback every few seconds just in case it helps.
|
|
|
|
struct timespec abs_timeout;
|
|
|
|
clock_gettime(CLOCK_REALTIME, &abs_timeout);
|
|
|
|
abs_timeout.tv_sec += 2;
|
|
|
|
while (0 != (err = sem_timedwait(&sem, &abs_timeout)))
|
2005-11-04 01:02:43 +01:00
|
|
|
#endif
|
2005-11-03 04:49:57 +01:00
|
|
|
{
|
|
|
|
// If timed out, call callback and try again
|
|
|
|
if (errno == ETIMEDOUT)
|
|
|
|
timeoutCallback();
|
|
|
|
// Keep retrying while EINTR, but other errors are probably fatal
|
|
|
|
else if (errno != EINTR)
|
|
|
|
{
|
2005-11-04 01:02:43 +01:00
|
|
|
debug_warn("Semaphore wait failed");
|
2005-11-03 04:49:57 +01:00
|
|
|
return; // (leak the semaphore)
|
|
|
|
}
|
|
|
|
}
|
2005-10-31 04:36:50 +01:00
|
|
|
|
|
|
|
// Clean up
|
|
|
|
qry->m_Semaphore = NULL;
|
|
|
|
sem_destroy(&sem);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool MessagePasserImpl::IsEmpty()
|
2005-06-25 04:21:26 +02:00
|
|
|
{
|
2005-09-13 05:57:34 +02:00
|
|
|
m_Mutex.Lock();
|
|
|
|
bool empty = m_Queue.empty();
|
|
|
|
m_Mutex.Unlock();
|
|
|
|
return empty;
|
2005-06-25 04:21:26 +02:00
|
|
|
}
|
|
|
|
|
2005-10-31 04:36:50 +01:00
|
|
|
void MessagePasserImpl::SetTrace(bool t)
|
2005-10-09 05:26:16 +02:00
|
|
|
{
|
|
|
|
m_Trace = t;
|
|
|
|
}
|