1
0

Merge pull request #1563 from mc-server/c++11Events

Reimplemented cEvent using C++11 primitives.
This commit is contained in:
Mattes D 2014-12-07 22:41:34 +01:00
commit d323c0ba76
4 changed files with 75 additions and 146 deletions

View File

@ -252,6 +252,8 @@ template class SizeChecker<UInt16, 2>;
#include <set>
#include <queue>
#include <limits>
#include <chrono>

View File

@ -12,68 +12,9 @@
cEvent::cEvent(void)
cEvent::cEvent(void) :
m_ShouldWait(true)
{
#ifdef _WIN32
m_Event = CreateEvent(nullptr, FALSE, FALSE, nullptr);
if (m_Event == nullptr)
{
LOGERROR("cEvent: cannot create event, GLE = %u. Aborting server.", (unsigned)GetLastError());
abort();
}
#else // *nix
m_bIsNamed = false;
m_Event = new sem_t;
if (sem_init(m_Event, 0, 0))
{
// This path is used by MacOS, because it doesn't support unnamed semaphores.
delete m_Event;
m_bIsNamed = true;
AString EventName;
Printf(EventName, "cEvent%p", this);
m_Event = sem_open(EventName.c_str(), O_CREAT, 777, 0);
if (m_Event == SEM_FAILED)
{
AString error = GetOSErrorString(errno);
LOGERROR("cEvent: Cannot create event, err = %s. Aborting server.", error.c_str());
abort();
}
// Unlink the semaphore immediately - it will continue to function but will not pollute the namespace
// We don't store the name, so can't call this in the destructor
if (sem_unlink(EventName.c_str()) != 0)
{
AString error = GetOSErrorString(errno);
LOGWARN("ERROR: Could not unlink cEvent. (%s)", error.c_str());
}
}
#endif // *nix
}
cEvent::~cEvent()
{
#ifdef _WIN32
CloseHandle(m_Event);
#else
if (m_bIsNamed)
{
if (sem_close(m_Event) != 0)
{
AString error = GetOSErrorString(errno);
LOGERROR("ERROR: Could not close cEvent. (%s)", error.c_str());
}
}
else
{
sem_destroy(m_Event);
delete m_Event;
m_Event = nullptr;
}
#endif
}
@ -82,67 +23,48 @@ cEvent::~cEvent()
void cEvent::Wait(void)
{
#ifdef _WIN32
DWORD res = WaitForSingleObject(m_Event, INFINITE);
if (res != WAIT_OBJECT_0)
{
LOGWARN("cEvent: waiting for the event failed: %u, GLE = %u. Continuing, but server may be unstable.", (unsigned)res, (unsigned)GetLastError());
}
#else
int res = sem_wait(m_Event);
if (res != 0)
{
AString error = GetOSErrorString(errno);
LOGWARN("cEvent: waiting for the event failed: %i, err = %s. Continuing, but server may be unstable.", res, error.c_str());
}
#endif
std::unique_lock<std::mutex> Lock(m_Mutex);
while (m_ShouldWait)
{
m_CondVar.wait(Lock);
}
m_ShouldWait = true;
}
bool cEvent::Wait(int a_TimeoutMSec)
bool cEvent::Wait(unsigned a_TimeoutMSec)
{
#ifdef _WIN32
DWORD res = WaitForSingleObject(m_Event, (DWORD)a_TimeoutMSec);
switch (res)
auto dst = std::chrono::system_clock::now() + std::chrono::milliseconds(a_TimeoutMSec);
std::unique_lock<std::mutex> Lock(m_Mutex); // We assume that this lock is acquired without much delay - we are the only user of the mutex
while (m_ShouldWait && (std::chrono::system_clock::now() <= dst))
{
switch (m_CondVar.wait_until(Lock, dst))
{
case WAIT_OBJECT_0: return true; // Regular event signalled
case WAIT_TIMEOUT: return false; // Regular event timeout
default:
case std::cv_status::no_timeout:
{
LOGWARN("cEvent: waiting for the event failed: %u, GLE = %u. Continuing, but server may be unstable.", (unsigned)res, (unsigned)GetLastError());
// The wait was successful, check for spurious wakeup:
if (!m_ShouldWait)
{
m_ShouldWait = true;
return true;
}
// This was a spurious wakeup, wait again:
continue;
}
case std::cv_status::timeout:
{
// The wait timed out, return failure:
return false;
}
}
#else
// Get the current time:
timespec timeout;
if (clock_gettime(CLOCK_REALTIME, &timeout) == -1)
{
LOGWARN("cEvent: Getting current time failed: %i, err = %s. Continuing, but the server may be unstable.", errno, GetOSErrorString(errno).c_str());
return false;
}
} // switch (wait_until())
} // while (m_ShouldWait && not timeout)
// Add the specified timeout:
timeout.tv_sec += a_TimeoutMSec / 1000;
timeout.tv_nsec += (a_TimeoutMSec % 1000) * 1000000; // 1 msec = 1000000 usec
// Wait with timeout:
int res = sem_timedwait(m_Event, &timeout);
switch (res)
{
case 0: return true; // Regular event signalled
case ETIMEDOUT: return false; // Regular even timeout
default:
{
AString error = GetOSErrorString(errno);
LOGWARN("cEvent: waiting for the event failed: %i, err = %s. Continuing, but server may be unstable.", res, error.c_str());
return false;
}
}
#endif
// The wait timed out in the while() condition:
return false;
}
@ -151,19 +73,11 @@ bool cEvent::Wait(int a_TimeoutMSec)
void cEvent::Set(void)
{
#ifdef _WIN32
if (!SetEvent(m_Event))
{
LOGWARN("cEvent: Could not set cEvent: GLE = %u", (unsigned)GetLastError());
}
#else
int res = sem_post(m_Event);
if (res != 0)
{
AString error = GetOSErrorString(errno);
LOGWARN("cEvent: Could not set cEvent: %i, err = %s", res, error.c_str());
}
#endif
{
std::unique_lock<std::mutex> Lock(m_Mutex);
m_ShouldWait = false;
}
m_CondVar.notify_one();
}

View File

@ -1,16 +1,17 @@
// Event.h
// Interfaces to the cEvent object representing an OS-specific synchronization primitive that can be waited-for
// Implemented as an Event on Win and as a 1-semaphore on *nix
// Interfaces to the cEvent object representing a synchronization primitive that can be waited-for
// Implemented using C++11 condition variable and mutex
#pragma once
#ifndef CEVENT_H_INCLUDED
#define CEVENT_H_INCLUDED
#include <mutex>
#include <condition_variable>
@ -20,32 +21,32 @@ class cEvent
{
public:
cEvent(void);
~cEvent();
/** Waits until the event has been set.
If the event has been set before it has been waited for, Wait() returns immediately. */
void Wait(void);
/** Sets the event - releases one thread that has been waiting in Wait().
If there was no thread waiting, the next call to Wait() will not block. */
void Set (void);
/** Waits for the event until either it is signalled, or the (relative) timeout is passed.
Returns true if the event was signalled, false if the timeout was hit or there was an error. */
bool Wait(int a_TimeoutMSec);
bool Wait(unsigned a_TimeoutMSec);
private:
#ifdef _WIN32
HANDLE m_Event;
#else
sem_t * m_Event;
bool m_bIsNamed;
#endif
/** Used for checking for spurious wakeups. */
bool m_ShouldWait;
/** Mutex protecting m_ShouldWait from multithreaded access. */
std::mutex m_Mutex;
/** The condition variable used as the Event. */
std::condition_variable m_CondVar;
} ;
#endif // CEVENT_H_INCLUDED

View File

@ -161,26 +161,38 @@ class cMojangAPI::cUpdateThread :
{
typedef cIsThread super;
public:
cUpdateThread() :
super("cMojangAPI::cUpdateThread")
cUpdateThread(cMojangAPI & a_MojangAPI) :
super("cMojangAPI::cUpdateThread"),
m_MojangAPI(a_MojangAPI)
{
}
~cUpdateThread()
{
// Notify the thread that it should stop:
m_ShouldTerminate = true;
m_evtNotify.Set();
// Wait for the thread to actually finish work:
Stop();
}
protected:
/** The cMojangAPI instance to update. */
cMojangAPI & m_MojangAPI;
/** The event used for notifying that the thread should terminate, as well as timing. */
cEvent m_evtNotify;
// cIsThread override:
virtual void Execute(void) override
{
do
{
cRoot::Get()->GetMojangAPI().Update();
} while (!m_evtNotify.Wait(60 * 60 * 1000)); // Repeat every 60 minutes
m_MojangAPI.Update();
} while (!m_ShouldTerminate && !m_evtNotify.Wait(60 * 60 * 1000)); // Repeat every 60 minutes until termination request
}
} ;
@ -197,7 +209,7 @@ cMojangAPI::cMojangAPI(void) :
m_UUIDToProfileServer(DEFAULT_UUID_TO_PROFILE_SERVER),
m_UUIDToProfileAddress(DEFAULT_UUID_TO_PROFILE_ADDRESS),
m_RankMgr(nullptr),
m_UpdateThread(new cUpdateThread())
m_UpdateThread(new cUpdateThread(*this))
{
}