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