11import asyncio
22from contextlib import asynccontextmanager
3+ from typing import AsyncGenerator
34from ..client import Client
45from ..room import Room , event
56
@@ -42,10 +43,12 @@ async def setup(client: Client, collection: str = 'lock'):
4243 inner.room.emit('go');
4344 },
4445 acquire: |this, timeout| {
45- immediately = this.queue.len() == 0;
46+ size = this.queue.len();
47+ immediately = size == 0;
4648 inner = Inner{timeout:,};
4749 this.queue.push(inner);
48- immediately ? inner.set_task(this.id()) : inner.room.id();
50+ immediately && inner.set_task(this.id());
51+ [immediately, inner.room.id(), size];
4952 },
5053 release: |this, room_id| try({
5154 if (this.queue.first().room.id() == room_id) {
@@ -74,12 +77,16 @@ async def setup(client: Client, collection: str = 'lock'):
7477 room(room_id).name() == 'go';
7578 });
7679
80+ new_procedure('locked', |name| {
81+ bool(.lock[name].queue);
82+ });
83+
7784 new_procedure('release', |name, room_id| {
7885 wse(.lock[name].release(room_id));
7986 });
8087
8188 .to_type('Root');
82- """ )
89+ """ , scope = f'// { collection } ' )
8390
8491
8592class _InnerRoom (Room ):
@@ -93,28 +100,38 @@ async def on_join(self) -> None:
93100 # We might have missed the event during the join. If so, set the
94101 # future result to continue.
95102 ok = await self .client .run ('test' , self .id , scope = self .scope )
96- if ok :
103+ if ok and not self . future . done () :
97104 self .future .set_result (None )
98105
99106 @event ('go' )
100107 def on_go (self ):
101- self .future .set_result (None )
108+ if not self .future .done ():
109+ self .future .set_result (None )
102110
103111
104112@asynccontextmanager
105113async def lock (client : Client , name : str ,
106114 scope : str = '//lock' ,
107- timeout : int = 60 ):
115+ timeout : int = 60 ) -> AsyncGenerator [ int , None ] :
108116
109- room_id : int | None = \
117+ res : tuple [ bool , int , int ] = \
110118 await client .run ('acquire' , name , timeout , scope = scope )
111119
112- if room_id is not None :
120+ immediately , room_id , size = res
121+ if not immediately :
113122 room = _InnerRoom (room_id , scope = scope )
114123 await room .join (client , wait = None )
115- await room .future
124+ try :
125+ await asyncio .wait_for (room .future , timeout = timeout * size )
126+ except asyncio .TimeoutError :
127+ pass
116128
117129 try :
118130 yield room_id # Lock Id assigned to the 'as' target (not required)
119131 finally :
120- await client .run ('release' , room_id , scope = scope )
132+ await client .run ('release' , name , room_id , scope = scope )
133+
134+
135+ async def locked (client : Client , name : str , scope : str = '//lock' ) -> bool :
136+ res : bool = await client .run ('locked' , name , scope = scope )
137+ return res
0 commit comments