function with_lock_v1
A decorator that implements reentrant locking for async methods, allowing the same task to acquire the lock multiple times without deadlocking.
/tf/active/vicechatdev/rmcl/items.py
26 - 35
moderate
Purpose
This decorator wraps async methods to ensure thread-safe execution using a Trio lock. It checks if the current task already owns the lock (reentrant behavior) and if so, executes the function directly without attempting to acquire the lock again. Otherwise, it acquires the lock before executing the function. This is useful for protecting shared resources in async class methods where methods might call each other.
Source Code
def with_lock(func):
@functools.wraps(func)
async def decorated(self, *args, **kw):
if self._lock.statistics().owner == trio.lowlevel.current_task():
return await func(self, *args, **kw)
async with self._lock:
return await func(self, *args, **kw)
return decorated
Parameters
| Name | Type | Default | Kind |
|---|---|---|---|
func |
- | - | positional_or_keyword |
Parameter Details
func: An async function/method to be decorated. Expected to be a method that takes 'self' as its first parameter, followed by any number of positional and keyword arguments. The function must be async (coroutine).
Return Value
Returns a decorated async function that wraps the original function with lock acquisition logic. When called, the decorated function returns whatever the original function returns, but ensures it executes within a lock context unless the current task already owns the lock.
Dependencies
functoolstrio
Required Imports
import functools
import trio
Usage Example
import functools
import trio
class MyResource:
def __init__(self):
self._lock = trio.Lock()
self.data = []
@with_lock
async def add_item(self, item):
self.data.append(item)
await trio.sleep(0.1)
@with_lock
async def add_multiple(self, items):
# This can call add_item without deadlock due to reentrant behavior
for item in items:
await self.add_item(item)
async def main():
resource = MyResource()
await resource.add_item('test')
await resource.add_multiple(['a', 'b', 'c'])
print(resource.data)
trio.run(main)
Best Practices
- Only use this decorator on async methods (coroutines), not regular synchronous functions
- Ensure the class has a '_lock' attribute initialized as a trio.Lock before any decorated methods are called
- The decorator assumes 'self' is the first parameter, so it's designed for instance methods, not standalone functions
- Be aware that reentrant locking allows the same task to enter the lock multiple times, which can mask certain concurrency bugs
- Use this pattern when you have methods that need to call each other while maintaining thread safety
- Consider the performance implications of lock acquisition in high-frequency method calls
Similar Components
AI-powered semantic similarity - components with related functionality:
-
function add_sync 62.4% similar
-
function with_lock 61.7% similar
-
function async_execute 52.9% similar
-
function get_task_status_v1 51.8% similar
-
function update_task_progress 50.5% similar