Use POSIX IPC named semaphores with a high-level API similar to Python's threading.Semaphore
. This repository wraps around existing bindings from the posix_ipc package to provide a more Pythonic interface.
- Named Semaphores: Handle POSIX IPC named semaphores with ease.
- Cross-Platform: Works on Linux, macOS, and Windows (via Cygwin).
- Thread-Safe: Built on POSIX IPC, ensuring robust multi-process handling.
- Pythonic API: Similar to Python's built-in
threading.Semaphore
for familiarity. - Flexible Creation: Choose how to handle existing semaphores (
RAISE_IF_EXISTS
,LINK_OR_CREATE
, etc.). - Timeouts: Optionally specify timeouts for acquiring semaphores (platform-dependent).
- Automatic Cleanup: Semaphore can be automatically unlinked when the object is deleted.
You can install the package from PyPI:
pip install named-semaphores
The NamedSemaphore
class provides a high-level API for working with POSIX IPC named semaphores. Below are various usage examples demonstrating its flexibility and functionality.
Create a semaphore with a default initial value of 1.
from named_semaphores import NamedSemaphore
sem = NamedSemaphore("example_semaphore")
print(f"Semaphore '{sem.name}' created.")
You can also specify a custom initial value for the semaphore.
sem = NamedSemaphore("example_semaphore", initial_value=3)
print(f"Semaphore '{sem.name}' created with initial value of 3.")
You can acquire and release the semaphore, specify a custom initial value, or use a timeout for acquiring (if supported on your platform).
sem = NamedSemaphore("example_semaphore")
# Acquire the semaphore
sem.acquire()
try:
print("Critical section protected by semaphore.")
finally:
# Release the semaphore
sem.release()
print("Semaphore released.")
# Acquire the semaphore with a 5-second timeout
acquired = sem.acquire(timeout=5)
if acquired:
print("Semaphore acquired within timeout.")
sem.release()
else:
print("Failed to acquire semaphore within timeout.")
The semaphore can be used in a with statement to automatically acquire and release it.
with NamedSemaphore("example_semaphore") as sem:
print(f"Semaphore '{sem.name}' acquired.")
# Critical section here
print(f"Semaphore '{sem.name}' released.")
Unlinking a semaphore removes it from the system after all processes using it have closed it. Note some comments made by the posix_ipc
author:
“If any processes have the semaphore open when unlink is called, the call to unlink returns immediately but destruction of the semaphore is postponed until all processes have closed the semaphore.
Note, however, that once a semaphore has been unlinked, calls to open() with the same name should refer to a new semaphore. Sound confusing? It is, and you'd probably be wise structure your code so as to avoid this situation.”
By default, semaphores created by this class are automatically unlinked when the object is deleted. You can override this behavior with the unlink_on_delete
parameter.
- Automatic unlinking example:
# Create a semaphore
handle_create = NamedSemaphore(
"example_semaphore",
handle_existence=NamedSemaphore.Flags.RAISE_IF_EXISTS
)
# Link to the semaphore
handle_link = NamedSemaphore(
"example_semaphore",
handle_existence=NamedSemaphore.Flags.RAISE_IF_NOT_EXISTS
)
# Trigger garbage collection
del handle_link # Only closes the handle, does not unlink the semaphore
del handle_create # Unlinks the semaphore, because this handle created it
- Manual unlinking example:
# Set `unlink_on_delete` to False to prevent automatic unlinking on object deletion
sem = NamedSemaphore("example_semaphore", unlink_on_delete=False)
del sem # Semaphore is not unlinked
sem = NamedSemaphore("example_semaphore", unlink_on_delete=False)
# Unlink the semaphore manually, handle will be closed in the destructor
sem.unlink()
The NamedSemaphore
class provides flags to handle existing semaphores in different ways. The safest ways are the RAISE_IF_EXISTS
and RAISE_IF_NOT_EXISTS
flags, which raise an error if the semaphore already exists or does not exist, respectively.
Some other flags are LINK_OR_CREATE
and UNLINK_AND_CREATE
, which link to an existing semaphore or unlink it if it already exists. They may be useful in certain scenarios, but should be used with caution, as they may silently hide issues in the code (e.g., not properly cleaning up semaphores).
Use the RAISE_IF_EXISTS
flag to ensure a new semaphore is created and raise an error if one already exists.
try:
sem = NamedSemaphore(
"example_semaphore",
handle_existence=NamedSemaphore.Flags.RAISE_IF_EXISTS
)
print(f"Semaphore '{sem.name}' created without existing conflict.")
except FileExistsError:
print("Handling existing semaphore error...")
Use the LINK_OR_CREATE
flag to link to an existing semaphore or create a new one if it doesn't exist.
sem = NamedSemaphore(
"example_semaphore",
handle_existence=NamedSemaphore.Flags.LINK_OR_CREATE
)
print(f"Semaphore '{sem.name}' linked or created.")
Use the RAISE_IF_NOT_EXISTS
flag to link to an existing semaphore, raising an error if it does not exist.
try:
sem = NamedSemaphore(
"example_semaphore",
handle_existence=NamedSemaphore.Flags.RAISE_IF_NOT_EXISTS
)
print(f"Semaphore '{sem.name}' linked.")
except FileNotFoundError:
print("Handling non-existing semaphore error...")
Use the UNLINK_AND_CREATE
flag to unlink semaphore if it already exists and create a new one.
sem = NamedSemaphore(
"example_semaphore",
handle_existence=NamedSemaphore.Flags.UNLINK_AND_CREATE
)
print(f"Semaphore '{sem.name}' deleted and created.")
Note: although convenient, rather than using a forced deletion and create, it is better practice to handle the existence of semaphores in a more controlled manner. An already existing semaphore usually indicates that it is being used by another process, or that it was not properly cleaned up by a previous process.
These examples cover the most common scenarios for using NamedSemaphore. For advanced use cases or troubleshooting, refer to the module's documentation or docstrings.
Contributions are welcome! Please follow these steps:
- Fork the repository.
- Create a new branch (
git checkout -b feature/your-feature
). - Commit your changes (
git commit -m "Add your feature"
). - Push to the branch (
git push origin feature/your-feature
). - Open a Pull Request.
This project is licensed under the Apache License 2.0. See the LICENSE file for more details.
Please note that this project depends on the posix_ipc package, which is licensed under a BSD-style license. For details about the posix_ipc
license, refer to its license file.