summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMattes D <github@xoft.cz>2014-12-07 22:41:34 +0100
committerMattes D <github@xoft.cz>2014-12-07 22:41:34 +0100
commitd323c0ba7625a8ff766779392d55813b5aea79ad (patch)
treebdd693ca3b279bd02b9bf01f1909a3cec2ccaaf0
parentFixed format warning. (diff)
parentcMojangAPI: Fixed a possible problem with thread termination order. (diff)
downloadcuberite-d323c0ba7625a8ff766779392d55813b5aea79ad.tar
cuberite-d323c0ba7625a8ff766779392d55813b5aea79ad.tar.gz
cuberite-d323c0ba7625a8ff766779392d55813b5aea79ad.tar.bz2
cuberite-d323c0ba7625a8ff766779392d55813b5aea79ad.tar.lz
cuberite-d323c0ba7625a8ff766779392d55813b5aea79ad.tar.xz
cuberite-d323c0ba7625a8ff766779392d55813b5aea79ad.tar.zst
cuberite-d323c0ba7625a8ff766779392d55813b5aea79ad.zip
-rw-r--r--src/Globals.h2
-rw-r--r--src/OSSupport/Event.cpp156
-rw-r--r--src/OSSupport/Event.h35
-rw-r--r--src/Protocol/MojangAPI.cpp22
4 files changed, 72 insertions, 143 deletions
diff --git a/src/Globals.h b/src/Globals.h
index b68e9fd68..61f500db9 100644
--- a/src/Globals.h
+++ b/src/Globals.h
@@ -252,6 +252,8 @@ template class SizeChecker<UInt16, 2>;
#include <set>
#include <queue>
#include <limits>
+#include <chrono>
+
diff --git a/src/OSSupport/Event.cpp b/src/OSSupport/Event.cpp
index 87bc110ce..d6ba937f9 100644
--- a/src/OSSupport/Event.cpp
+++ b/src/OSSupport/Event.cpp
@@ -12,137 +12,59 @@
-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()
+void cEvent::Wait(void)
{
-#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
+ std::unique_lock<std::mutex> Lock(m_Mutex);
+ while (m_ShouldWait)
{
- sem_destroy(m_Event);
- delete m_Event;
- m_Event = nullptr;
+ m_CondVar.wait(Lock);
}
-#endif
+ m_ShouldWait = true;
}
-void cEvent::Wait(void)
+bool cEvent::Wait(unsigned a_TimeoutMSec)
{
- #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
-}
-
-
-
-
-
-bool cEvent::Wait(int 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());
- return false;
+ // The wait was successful, check for spurious wakeup:
+ if (!m_ShouldWait)
+ {
+ m_ShouldWait = true;
+ return true;
+ }
+ // This was a spurious wakeup, wait again:
+ continue;
}
- }
- #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;
- }
-
- // 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:
+
+ case std::cv_status::timeout:
{
- AString error = GetOSErrorString(errno);
- LOGWARN("cEvent: waiting for the event failed: %i, err = %s. Continuing, but server may be unstable.", res, error.c_str());
+ // The wait timed out, return failure:
return false;
}
- }
- #endif
+ } // switch (wait_until())
+ } // while (m_ShouldWait && not timeout)
+
+ // 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();
}
diff --git a/src/OSSupport/Event.h b/src/OSSupport/Event.h
index e2fa65a05..572388a3f 100644
--- a/src/OSSupport/Event.h
+++ b/src/OSSupport/Event.h
@@ -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,31 +21,31 @@ 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
diff --git a/src/Protocol/MojangAPI.cpp b/src/Protocol/MojangAPI.cpp
index 67f513e44..570754204 100644
--- a/src/Protocol/MojangAPI.cpp
+++ b/src/Protocol/MojangAPI.cpp
@@ -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))
{
}