| 239 | } |
| 240 | |
| 241 | void SC::ThreadingTest::testSemaphore() |
| 242 | { |
| 243 | //! [semaphoreSnippet] |
| 244 | constexpr int maxResources = 2; // Only 2 threads can access resource at once |
| 245 | constexpr int numThreads = 4; // Total number of threads trying to access |
| 246 | constexpr int operationsPerThread = 3; // Each thread will do 3 operations |
| 247 | |
| 248 | Semaphore semaphore(maxResources); // Initialize with 2 available resources |
| 249 | struct Context |
| 250 | { |
| 251 | Semaphore& semaphore; |
| 252 | Mutex counterMutex; // To protect sharedResource counter |
| 253 | int sharedResource = 0; // Counter to verify correct synchronization |
| 254 | } ctx{semaphore, {}}; |
| 255 | |
| 256 | Thread threads[numThreads]; |
| 257 | for (int i = 0; i < numThreads; i++) |
| 258 | { |
| 259 | auto threadFunc = [this, &ctx](Thread& thread) |
| 260 | { |
| 261 | thread.setThreadName(SC_NATIVE_STR("Worker Thread")); |
| 262 | for (int j = 0; j < operationsPerThread; j++) |
| 263 | { |
| 264 | ctx.semaphore.acquire(); // Wait for resource to be available |
| 265 | |
| 266 | // Critical section |
| 267 | ctx.counterMutex.lock(); |
| 268 | ctx.sharedResource++; |
| 269 | SC_TEST_EXPECT(ctx.sharedResource <= maxResources); // Never more than maxResources threads |
| 270 | Thread::Sleep(1); // Simulate some work |
| 271 | ctx.sharedResource--; |
| 272 | ctx.counterMutex.unlock(); |
| 273 | |
| 274 | ctx.semaphore.release(); // Release the resource |
| 275 | Thread::Sleep(1); // Give other threads a chance |
| 276 | } |
| 277 | }; |
| 278 | SC_TEST_EXPECT(threads[i].start(threadFunc)); |
| 279 | } |
| 280 | |
| 281 | // Wait for all threads to finish |
| 282 | for (int i = 0; i < numThreads; i++) |
| 283 | { |
| 284 | SC_TEST_EXPECT(threads[i].join()); |
| 285 | } |
| 286 | |
| 287 | // Verify final state |
| 288 | SC_TEST_EXPECT(ctx.sharedResource == 0); |
| 289 | //! [semaphoreSnippet] |
| 290 | } |
| 291 | |
| 292 | namespace SC |
| 293 | { |