@@ -27,6 +27,7 @@ def __init__(self, scheduler):
2727 "condition_notify" : self .notify ,
2828 "condition_acquire" : self .acquire ,
2929 "condition_release" : self .release ,
30+ "condition_notify_all" : self .notify_all ,
3031 }
3132 )
3233
@@ -37,13 +38,15 @@ def _get_lock(self, name):
3738
3839 @log_errors
3940 async def acquire (self , name = None , id = None ):
41+ """Acquire the underlying lock"""
4042 lock = self ._get_lock (name )
4143 await lock .acquire ()
4244 self ._lock_holders [name ] = id
4345 return True
4446
4547 @log_errors
4648 async def release (self , name = None , id = None ):
49+ """Release the underlying lock"""
4750 if self ._lock_holders .get (name ) != id :
4851 return False
4952
@@ -59,6 +62,7 @@ async def release(self, name=None, id=None):
5962
6063 @log_errors
6164 async def wait (self , name = None , id = None , timeout = None ):
65+ """Wait on condition"""
6266 # Verify lock is held by this client
6367 if self ._lock_holders .get (name ) != id :
6468 raise RuntimeError ("wait() called without holding the lock" )
@@ -99,6 +103,7 @@ async def wait(self, name=None, id=None, timeout=None):
99103
100104 @log_errors
101105 def notify (self , name = None , n = 1 ):
106+ """Notify n waiters"""
102107 if self ._lock_holders .get (name ) is None :
103108 raise RuntimeError ("notify() called without holding the lock" )
104109
@@ -111,6 +116,7 @@ def notify(self, name=None, n=1):
111116
112117 @log_errors
113118 def notify_all (self , name = None ):
119+ """Notify all waiters"""
114120 if self ._lock_holders .get (name ) is None :
115121 raise RuntimeError ("notify_all() called without holding the lock" )
116122
@@ -123,18 +129,27 @@ def notify_all(self, name=None):
123129class Condition (SyncMethodMixin ):
124130 """Distributed Condition Variable
125131
132+ Mimics asyncio.Condition API. Allows coordination between
133+ distributed workers using wait/notify pattern.
134+
126135 Parameters
127136 ----------
128- name: str, optional
137+ name : str, optional
129138 Name of the condition. Same name = shared state.
130- client: Client, optional
139+ client : Client, optional
131140 Client for scheduler communication.
132141
133142 Examples
134143 --------
144+ >>> from distributed import Condition
145+ >>> condition = Condition('my-condition')
146+ >>> async with condition:
147+ ... await condition.wait() # Wait for notification
148+
149+ >>> # In another worker/client
135150 >>> condition = Condition('my-condition')
136151 >>> async with condition:
137- ... await condition.wait()
152+ ... condition.notify() # Wake one waiter
138153 """
139154
140155 def __init__ (self , name = None , client = None ):
@@ -152,11 +167,20 @@ def client(self):
152167 pass
153168 return self ._client
154169
170+ @property
171+ def loop (self ):
172+ return self .client .loop if self .client else None
173+
155174 def _verify_running (self ):
156175 if not self .client :
157- raise RuntimeError (f"{ type (self )} object not properly initialized." )
176+ raise RuntimeError (
177+ f"{ type (self )} object not properly initialized. This can happen"
178+ " if the object is being deserialized outside of the context of"
179+ " a Client or Worker."
180+ )
158181
159182 async def acquire (self ):
183+ """Acquire underlying lock"""
160184 self ._verify_running ()
161185 result = await self .client .scheduler .condition_acquire (
162186 name = self .name , id = self .id
@@ -165,13 +189,29 @@ async def acquire(self):
165189 return result
166190
167191 async def release (self ):
192+ """Release underlying lock"""
168193 if not self ._locked :
169194 raise RuntimeError ("Cannot release un-acquired lock" )
170195 self ._verify_running ()
171196 await self .client .scheduler .condition_release (name = self .name , id = self .id )
172197 self ._locked = False
173198
174199 async def wait (self , timeout = None ):
200+ """Wait until notified
201+
202+ Must be called while lock is held. Releases lock and waits
203+ for notify(), then reacquires lock before returning.
204+
205+ Parameters
206+ ----------
207+ timeout : number or string or timedelta, optional
208+ Seconds to wait on the condition in the scheduler.
209+
210+ Returns
211+ -------
212+ bool
213+ True if notified, False if timeout occurred
214+ """
175215 if not self ._locked :
176216 raise RuntimeError ("wait() called without holding the lock" )
177217
@@ -182,19 +222,43 @@ async def wait(self, timeout=None):
182222 )
183223 return result
184224
185- async def notify (self , n = 1 ):
225+ def notify (self , n = 1 ):
226+ """Wake up one or more waiters
227+
228+ Parameters
229+ ----------
230+ n : int, optional
231+ Number of waiters to wake. Default is 1.
232+
233+ Returns
234+ -------
235+ int
236+ Number of waiters notified
237+ """
186238 if not self ._locked :
187239 raise RuntimeError ("Cannot notify without holding the lock" )
188240 self ._verify_running ()
189- return await self .client .scheduler .condition_notify (name = self .name , n = n )
241+ return self .client .sync (
242+ self .client .scheduler .condition_notify , name = self .name , n = n
243+ )
244+
245+ def notify_all (self ):
246+ """Wake up all waiters
190247
191- async def notify_all (self ):
248+ Returns
249+ -------
250+ int
251+ Number of waiters notified
252+ """
192253 if not self ._locked :
193254 raise RuntimeError ("Cannot notify without holding the lock" )
194255 self ._verify_running ()
195- return await self .client .scheduler .condition_notify_all (name = self .name )
256+ return self .client .sync (
257+ self .client .scheduler .condition_notify_all , name = self .name
258+ )
196259
197260 def locked (self ):
261+ """Return True if lock is held"""
198262 return self ._locked
199263
200264 async def __aenter__ (self ):
0 commit comments