Commit 0ff862ae authored by Davis King's avatar Davis King

Changed the windows signaler and mutex code to use the C++11 thread library instead of

the old win32 functions.  I did this to work around how windows unloads dlls.  In particular, during dll unload
windows will kill all threads, THEN it will destruct global objects.  So this leads to problems
where a global obejct that owns threads tries to tell them to shutdown and everything goes wrong.
The specific problem this code change fixes is when signaler::broadcast() is called
on a signaler that was being waited on by one of these abruptly killed threads.  In that case, the old
code would deadlock inside signaler::broadcast().  This new code doesn't seem to have that problem,
thereby mitigating the windows dll unload behavior in some situations.
parent 5d259cd7
......@@ -12,6 +12,9 @@
#include "../windows_magic.h"
#include <windows.h>
#include "../algs.h"
#include <condition_variable>
#include <mutex>
#include <chrono>
namespace dlib
......@@ -43,21 +46,22 @@ namespace dlib
mutex (
)
{
InitializeCriticalSection(&cs);
}
~mutex (
) { DeleteCriticalSection(&cs); }
) { }
void lock (
) const { EnterCriticalSection(&cs); }
) const { cs.lock(); }
void unlock (
) const { LeaveCriticalSection(&cs); }
) const { cs.unlock(); }
private:
mutable CRITICAL_SECTION cs;
friend class signaler;
mutable std::mutex cs;
// restricted functions
mutex(mutex&); // copy constructor
......@@ -77,147 +81,40 @@ namespace dlib
signaler (
const mutex& associated_mutex
) :
hSemaphore(CreateSemaphore (NULL, 0, 100000000, NULL)),
waiters(0),
hCountSema(CreateSemaphore (NULL,0,100000000,NULL)),
m(associated_mutex)
{
if (hSemaphore == NULL || hCountSema == NULL)
{
if (hSemaphore != NULL)
{
CloseHandle(hSemaphore);
}
if (hCountSema != NULL)
{
CloseHandle(hCountSema);
}
throw dlib::thread_error(ECREATE_SIGNALER,
"in function signaler::signaler() an error occurred making the signaler"
);
}
}
~signaler (
) { CloseHandle(hSemaphore); CloseHandle(hCountSema);}
) { }
void wait (
) const
{
// get a lock on the mutex for the waiters variable
waiters_mutex.lock();
// mark that one more thread will be waiting on this signaler
++waiters;
// release the mutex for waiters
waiters_mutex.unlock();
// release the associated mutex
m.unlock();
// wait for the semaphore to be signaled
WaitForSingleObject (hSemaphore,INFINITE);
// signal that we are awake
ReleaseSemaphore(hCountSema,(LONG)1,NULL);
// relock the associated mutex
m.lock();
std::unique_lock<std::mutex> cs(m.cs, std::defer_lock);
cv.wait(cs);
}
bool wait_or_timeout (
unsigned long milliseconds
) const
{
// get a lock on the mutex for the waiters variable
waiters_mutex.lock();
// mark that one more thread will be waiting on this signaler
++waiters;
// release the mutex for waiters
waiters_mutex.unlock();
// release the associated mutex
m.unlock();
bool value;
// wait for the semaphore to be signaled
if ( WaitForSingleObject (hSemaphore, milliseconds ) == WAIT_TIMEOUT )
{
// in this case we should decrement waiters because we are returning
// due to a timeout rather than because someone called signal() or
// broadcast().
value = false;
// signal that we are awake
ReleaseSemaphore(hCountSema,(LONG)1,NULL);
// get a lock on the mutex for the waiters variable
waiters_mutex.lock();
// mark that one less thread will be waiting on this signaler.
if (waiters != 0)
--waiters;
// release the mutex for waiters
waiters_mutex.unlock();
}
else
{
value = true;
// signal that we are awake
ReleaseSemaphore(hCountSema,(LONG)1,NULL);
}
// relock the associated mutex
m.lock();
return value;
std::unique_lock<std::mutex> cs(m.cs, std::defer_lock);
auto status = cv.wait_until(cs, std::chrono::system_clock::now() + std::chrono::milliseconds(milliseconds));
return status == std::cv_status::no_timeout;
}
void signal (
) const
{
// get a lock on the mutex for the waiters variable
waiters_mutex.lock();
if (waiters > 0)
{
--waiters;
// make the semaphore release one waiting thread
ReleaseSemaphore(hSemaphore,1,NULL);
// wait for signaled thread to wake up
WaitForSingleObject(hCountSema,INFINITE);
}
// release the mutex for waiters
waiters_mutex.unlock();
cv.notify_one();
}
void broadcast (
) const
{
// get a lock on the mutex for the waiters variable
waiters_mutex.lock();
if (waiters > 0)
{
// make the semaphore release all the waiting threads
ReleaseSemaphore(hSemaphore,(LONG)waiters,NULL);
// wait for count to be zero
for (unsigned long i = 0; i < waiters; ++i)
{
WaitForSingleObject(hCountSema,INFINITE);
}
waiters = 0;
}
// release the mutex for waiters
waiters_mutex.unlock();
cv.notify_all();
}
const mutex& get_mutex (
......@@ -225,13 +122,7 @@ namespace dlib
private:
mutable HANDLE hSemaphore;
mutable unsigned long waiters;
mutex waiters_mutex;
mutable HANDLE hCountSema;
mutable std::condition_variable cv;
const mutex& m;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment