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 @@ ...@@ -12,6 +12,9 @@
#include "../windows_magic.h" #include "../windows_magic.h"
#include <windows.h> #include <windows.h>
#include "../algs.h" #include "../algs.h"
#include <condition_variable>
#include <mutex>
#include <chrono>
namespace dlib namespace dlib
...@@ -43,21 +46,22 @@ namespace dlib ...@@ -43,21 +46,22 @@ namespace dlib
mutex ( mutex (
) )
{ {
InitializeCriticalSection(&cs);
} }
~mutex ( ~mutex (
) { DeleteCriticalSection(&cs); } ) { }
void lock ( void lock (
) const { EnterCriticalSection(&cs); } ) const { cs.lock(); }
void unlock ( void unlock (
) const { LeaveCriticalSection(&cs); } ) const { cs.unlock(); }
private: private:
mutable CRITICAL_SECTION cs; friend class signaler;
mutable std::mutex cs;
// restricted functions // restricted functions
mutex(mutex&); // copy constructor mutex(mutex&); // copy constructor
...@@ -77,147 +81,40 @@ namespace dlib ...@@ -77,147 +81,40 @@ namespace dlib
signaler ( signaler (
const mutex& associated_mutex const mutex& associated_mutex
) : ) :
hSemaphore(CreateSemaphore (NULL, 0, 100000000, NULL)),
waiters(0),
hCountSema(CreateSemaphore (NULL,0,100000000,NULL)),
m(associated_mutex) 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 ( ~signaler (
) { CloseHandle(hSemaphore); CloseHandle(hCountSema);} ) { }
void wait ( void wait (
) const ) const
{ {
// get a lock on the mutex for the waiters variable std::unique_lock<std::mutex> cs(m.cs, std::defer_lock);
waiters_mutex.lock(); cv.wait(cs);
// 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();
} }
bool wait_or_timeout ( bool wait_or_timeout (
unsigned long milliseconds unsigned long milliseconds
) const ) const
{ {
// get a lock on the mutex for the waiters variable std::unique_lock<std::mutex> cs(m.cs, std::defer_lock);
waiters_mutex.lock(); auto status = cv.wait_until(cs, std::chrono::system_clock::now() + std::chrono::milliseconds(milliseconds));
// mark that one more thread will be waiting on this signaler return status == std::cv_status::no_timeout;
++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;
} }
void signal ( void signal (
) const ) const
{ {
// get a lock on the mutex for the waiters variable cv.notify_one();
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();
} }
void broadcast ( void broadcast (
) const ) const
{ {
// get a lock on the mutex for the waiters variable cv.notify_all();
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();
} }
const mutex& get_mutex ( const mutex& get_mutex (
...@@ -225,13 +122,7 @@ namespace dlib ...@@ -225,13 +122,7 @@ namespace dlib
private: private:
mutable HANDLE hSemaphore; mutable std::condition_variable cv;
mutable unsigned long waiters;
mutex waiters_mutex;
mutable HANDLE hCountSema;
const mutex& m; 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