quill
BackendWorkerLock.h
1 
7 #pragma once
8 
9 #if defined(_WIN32)
10  #if !defined(WIN32_LEAN_AND_MEAN)
11  #define WIN32_LEAN_AND_MEAN
12  #endif
13 
14  #if !defined(NOMINMAX)
15  // Mingw already defines this, so no need to redefine
16  #define NOMINMAX
17  #endif
18 
19  #include <windows.h>
20 #else
21  #include <cstring>
22  #include <fcntl.h> // For O_CREAT and O_EXCL
23  #include <semaphore.h>
24 #endif
25 
26 #include "quill/core/Attributes.h"
27 #include "quill/core/QuillError.h"
28 
29 #include <cstring>
30 #include <string>
31 
32 QUILL_BEGIN_NAMESPACE
33 
34 namespace detail
35 {
47 {
48 public:
49  /***/
50  explicit BackendWorkerLock(std::string const& pid)
51  {
52 #if defined(_WIN32)
53  std::string name = "Local\\QuillLock" + pid;
54 
55  // Create a named mutex. If it already exists in this process, the flag ERROR_ALREADY_EXISTS is set.
56  _handle = CreateMutexA(nullptr, TRUE, name.data());
57 
58  if (_handle == nullptr)
59  {
60  QUILL_THROW(QuillError{"Failed to create mutex"});
61  }
62 
63  if (GetLastError() == ERROR_ALREADY_EXISTS)
64  {
65  // Another instance in the same process already holds the lock.
66  QUILL_THROW(QuillError{
67  "Duplicate backend worker thread detected. This indicates that the logging library has "
68  "been compiled into multiple binary modules (for instance, one module using a static build "
69  "and another using a shared build), resulting in separate instances of the backend worker. "
70  "Please build and link the logging library uniformly as a shared library with exported "
71  "symbols to ensure a single backend instance."});
72  }
73 #elif defined(__ANDROID__)
74  // disabled
75 #else
76  std::string name = "/QuillLock" + pid;
77 
78  // Open or create the named semaphore.
79  // O_CREAT will create the semaphore if it doesn't exist, and if it does exist it will simply open the same semaphore.
80  _sem = sem_open(name.data(), O_CREAT, 0644, 1);
81  if (_sem == SEM_FAILED)
82  {
83  QUILL_THROW(QuillError{"Failed to create semaphore - errno: " + std::to_string(errno) +
84  " error: " + std::strerror(errno)});
85  }
86 
87  // Immediately unlink it so that it leaves no traces.
88  // The semaphore will still exist until all descriptors are closed.
89  sem_unlink(name.data());
90 
91  // Try to lock the semaphore.
92  // If it’s already locked (by another instance within the same process),
93  // sem_trywait will return -1 and set errno.
94  if (sem_trywait(_sem) != 0)
95  {
96  QUILL_THROW(QuillError{
97  "Duplicate backend worker thread detected. This indicates that the logging library has "
98  "been compiled into multiple binary modules (for instance, one module using a static build "
99  "and another using a shared build), resulting in separate instances of the backend worker. "
100  "Please build and link the logging library uniformly as a shared library with exported "
101  "symbols to ensure a single backend instance."});
102  }
103 #endif
104  }
105 
106  /***/
108  {
109 #if defined(_WIN32)
110  if (_handle != nullptr)
111  {
112  ReleaseMutex(_handle);
113  CloseHandle(_handle);
114  _handle = nullptr;
115  }
116 #else
117  if (_sem != SEM_FAILED)
118  {
119  sem_post(_sem);
120  sem_close(_sem);
121  _sem = SEM_FAILED;
122  }
123 #endif
124  }
125 
126  // Disable copy and assignment.
127  BackendWorkerLock(BackendWorkerLock const&) = delete;
128  BackendWorkerLock& operator=(BackendWorkerLock const&) = delete;
129 
130 private:
131 #if defined(_WIN32)
132  HANDLE _handle{nullptr};
133 #else
134  sem_t* _sem{SEM_FAILED};
135 #endif
136 };
137 } // namespace detail
138 
139 QUILL_END_NAMESPACE
Ensures that only one instance of the backend worker is active.
Definition: BackendWorkerLock.h:46
Setups a signal handler to handle fatal signals.
Definition: BackendManager.h:24
custom exception
Definition: QuillError.h:45