|
|
import threading |
|
|
from functools import wraps |
|
|
from contextlib import nullcontext |
|
|
|
|
|
|
|
|
def atomic(lock=None): |
|
|
""" |
|
|
threading safe decorator, it can be used to decorate a function or receive a lock: |
|
|
1. directly decorate a function: @atomic |
|
|
2. receive a lock: @atomic(lock=shared_lock) |
|
|
""" |
|
|
lock = lock or threading.Lock() |
|
|
def decorator(func): |
|
|
@wraps(func) |
|
|
def wrapper(*args, **kwargs): |
|
|
with lock: |
|
|
return func(*args, **kwargs) |
|
|
return wrapper |
|
|
|
|
|
return decorator if not callable(lock) else decorator(lock) |
|
|
|
|
|
|
|
|
def atomic_method(func): |
|
|
""" |
|
|
threading safe decorator for class methods. |
|
|
If there are self._lock in the instance, it will use the lock. Otherwise, use nullcontext for execution. |
|
|
""" |
|
|
@wraps(func) |
|
|
def wrapper(self, *args, **kwargs): |
|
|
context = getattr(self, "_lock", nullcontext()) |
|
|
with context: |
|
|
return func(self, *args, **kwargs) |
|
|
return wrapper |
|
|
|
|
|
|
|
|
|