| //==- llvm/Support/Windows/Jobserver.inc - Windows Jobserver Impl -*- C++ -*-=// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // This file implements the Windows-specific parts of the JobserverClient class. | |
| // On Windows, the jobserver is implemented using a named semaphore. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "llvm/Support/Windows/WindowsSupport.h" | |
| #include <atomic> | |
| #include <cassert> | |
| namespace llvm { | |
| /// The constructor for the Windows jobserver client. It attempts to open a | |
| /// handle to an existing named semaphore, the name of which is provided by | |
| /// GNU make in the --jobserver-auth argument. If the semaphore is opened | |
| /// successfully, the client is marked as initialized. | |
| JobserverClientImpl::JobserverClientImpl(const JobserverConfig &Config) { | |
| Semaphore = (void *)::OpenSemaphoreA(SEMAPHORE_MODIFY_STATE | SYNCHRONIZE, | |
| FALSE, Config.Path.c_str()); | |
| if (Semaphore != nullptr) | |
| IsInitialized = true; | |
| } | |
| /// The destructor closes the handle to the semaphore, releasing the resource. | |
| JobserverClientImpl::~JobserverClientImpl() { | |
| if (Semaphore != nullptr) | |
| ::CloseHandle((HANDLE)Semaphore); | |
| } | |
| /// Tries to acquire a job slot. The first call always returns the implicit | |
| /// slot. Subsequent calls use a non-blocking wait on the semaphore | |
| /// (`WaitForSingleObject` with a timeout of 0). If the wait succeeds, the | |
| /// semaphore's count is decremented, and an explicit job slot is acquired. | |
| /// If the wait times out, it means no slots are available, and an invalid | |
| /// slot is returned. | |
| JobSlot JobserverClientImpl::tryAcquire() { | |
| if (!IsInitialized) | |
| return JobSlot(); | |
| // First, grant the implicit slot. | |
| if (HasImplicitSlot.exchange(false, std::memory_order_acquire)) { | |
| return JobSlot::createImplicit(); | |
| } | |
| // Try to acquire a slot from the semaphore without blocking. | |
| if (::WaitForSingleObject((HANDLE)Semaphore, 0) == WAIT_OBJECT_0) { | |
| // The explicit token value is arbitrary on Windows, as the semaphore | |
| // count is the real resource. | |
| return JobSlot::createExplicit(1); | |
| } | |
| return JobSlot(); // Invalid slot | |
| } | |
| /// Releases a job slot back to the pool. If the slot is implicit, it simply | |
| /// resets a flag. For an explicit slot, it increments the semaphore's count | |
| /// by one using `ReleaseSemaphore`, making the slot available to other | |
| /// processes. | |
| void JobserverClientImpl::release(JobSlot Slot) { | |
| if (!IsInitialized || !Slot.isValid()) | |
| return; | |
| if (Slot.isImplicit()) { | |
| [[maybe_unused]] bool was_already_released = | |
| HasImplicitSlot.exchange(true, std::memory_order_release); | |
| assert(!was_already_released && "Implicit slot released twice"); | |
| return; | |
| } | |
| // Release the slot by incrementing the semaphore count. | |
| (void)::ReleaseSemaphore((HANDLE)Semaphore, 1, NULL); | |
| } | |
| } // namespace llvm |