aboutsummaryrefslogtreecommitdiff
path: root/libs/usvfs/src/usvfs_dll/semaphore.cpp
blob: 609b2f5ce7f6ca1a1c2865e6753c28fd414f2879 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include "semaphore.h"
#include "exceptionex.h"

RecursiveBenaphore::RecursiveBenaphore() : m_Counter(0), m_OwnerId(0UL), m_Recursion(0)
{
  m_Semaphore = ::CreateSemaphore(nullptr, 1, 1, nullptr);
}

RecursiveBenaphore::~RecursiveBenaphore()
{
  ::CloseHandle(m_Semaphore);
}

void RecursiveBenaphore::wait(DWORD timeout)
{
  DWORD tid = ::GetCurrentThreadId();

  if (::_InterlockedIncrement(&m_Counter) > 1) {
    if (tid != m_OwnerId) {
      int tries = 3;
      while (::WaitForSingleObject(m_Semaphore, timeout) != WAIT_OBJECT_0) {
        HANDLE owner = ::OpenThread(SYNCHRONIZE, FALSE, m_OwnerId);
        ON_BLOCK_EXIT([owner]() {
          ::CloseHandle(owner);
        });
        if ((tries <= 0) || (::WaitForSingleObject(owner, 0) == WAIT_OBJECT_0)) {
          // owner has quit without releasing the semaphore!
          m_Recursion = 0;
          spdlog::get("usvfs")->error("thread {} never released the mutex", m_OwnerId);
          break;
        } else {
          --tries;
        }
      }
    }
  }
  m_OwnerId = tid;
  ++m_Recursion;
}

void RecursiveBenaphore::signal()
{
  if (m_Recursion == 0) {
    return;
  }
  // no validation the signaling thread is the one owning the lock
  DWORD recursion = --m_Recursion;
  if (recursion == 0) {
    m_OwnerId = 0;
  }
  DWORD result = ::_InterlockedDecrement(&m_Counter);
  if (result > 0) {
    if (recursion == 0) {
      ::ReleaseSemaphore(m_Semaphore, 1, nullptr);
    }
  }
}