|
1 | 1 | # Defines classes that provide synchronization objects. Note that use of |
2 | 2 | # this module requires that your Python support threads. |
3 | 3 | # |
4 | | -# condition() # a POSIX-like condition-variable object |
5 | | -# barrier(n) # an n-thread barrier |
6 | | -# event() # an event object |
7 | | -# semaphore(n=1)# a semaphore object, with initial count n |
| 4 | +# condition(lock=None) # a POSIX-like condition-variable object |
| 5 | +# barrier(n) # an n-thread barrier |
| 6 | +# event() # an event object |
| 7 | +# semaphore(n=1) # a semaphore object, with initial count n |
| 8 | +# mrsw() # a multiple-reader single-writer lock |
8 | 9 | # |
9 | 10 | # CONDITIONS |
10 | 11 | # |
11 | 12 | # A condition object is created via |
12 | 13 | # import this_module |
13 | | -# your_condition_object = this_module.condition() |
| 14 | +# your_condition_object = this_module.condition(lock=None) |
| 15 | +# |
| 16 | +# As explained below, a condition object has a lock associated with it, |
| 17 | +# used in the protocol to protect condition data. You can specify a |
| 18 | +# lock to use in the constructor, else the constructor will allocate |
| 19 | +# an anonymous lock for you. Specifying a lock explicitly can be useful |
| 20 | +# when more than one condition keys off the same set of shared data. |
14 | 21 | # |
15 | 22 | # Methods: |
16 | 23 | # .acquire() |
|
209 | 216 | # any) blocked by a .p(). It's an (detected) error for a .v() to |
210 | 217 | # increase the semaphore's count to a value larger than the initial |
211 | 218 | # count. |
| 219 | +# |
| 220 | +# MULTIPLE-READER SINGLE-WRITER LOCKS |
| 221 | +# |
| 222 | +# A mrsw lock is created via |
| 223 | +# import this_module |
| 224 | +# your_mrsw_lock = this_module.mrsw() |
| 225 | +# |
| 226 | +# This kind of lock is often useful with complex shared data structures. |
| 227 | +# The object lets any number of "readers" proceed, so long as no thread |
| 228 | +# wishes to "write". When a (one or more) thread declares its intention |
| 229 | +# to "write" (e.g., to update a shared structure), all current readers |
| 230 | +# are allowed to finish, and then a writer gets exclusive access; all |
| 231 | +# other readers & writers are blocked until the current writer completes. |
| 232 | +# Finally, if some thread is waiting to write and another is waiting to |
| 233 | +# read, the writer takes precedence. |
| 234 | +# |
| 235 | +# Methods: |
| 236 | +# |
| 237 | +# .read_in() |
| 238 | +# If no thread is writing or waiting to write, returns immediately. |
| 239 | +# Else blocks until no thread is writing or waiting to write. So |
| 240 | +# long as some thread has completed a .read_in but not a .read_out, |
| 241 | +# writers are blocked. |
| 242 | +# |
| 243 | +# .read_out() |
| 244 | +# Use sometime after a .read_in to declare that the thread is done |
| 245 | +# reading. When all threads complete reading, a writer can proceed. |
| 246 | +# |
| 247 | +# .write_in() |
| 248 | +# If no thread is writing (has completed a .write_in, but hasn't yet |
| 249 | +# done a .write_out) or reading (similarly), returns immediately. |
| 250 | +# Else blocks the calling thread, and threads waiting to read, until |
| 251 | +# the current writer completes writing or all the current readers |
| 252 | +# complete reading; if then more than one thread is waiting to |
| 253 | +# write, one of them is allowed to proceed, but which one is not |
| 254 | +# specified. |
| 255 | +# |
| 256 | +# .write_out() |
| 257 | +# Use sometime after a .write_in to declare that the thread is done |
| 258 | +# writing. Then if some other thread is waiting to write, it's |
| 259 | +# allowed to proceed. Else all threads (if any) waiting to read are |
| 260 | +# allowed to proceed. |
212 | 261 |
|
213 | 262 | import thread |
214 | 263 |
|
215 | 264 | class condition: |
216 | | - def __init__(self): |
| 265 | + def __init__(self, lock=None): |
217 | 266 | # the lock actually used by .acquire() and .release() |
218 | | - self.mutex = thread.allocate_lock() |
| 267 | + if lock is None: |
| 268 | + self.mutex = thread.allocate_lock() |
| 269 | + else: |
| 270 | + if hasattr(lock, 'acquire') and \ |
| 271 | + hasattr(lock, 'release'): |
| 272 | + self.mutex = lock |
| 273 | + else: |
| 274 | + raise TypeError, 'condition constructor requires ' \ |
| 275 | + 'a lock argument' |
219 | 276 |
|
220 | 277 | # lock used to block threads until a signal |
221 | 278 | self.checkout = thread.allocate_lock() |
@@ -357,6 +414,56 @@ def v(self): |
357 | 414 | self.nonzero.signal() |
358 | 415 | self.nonzero.release() |
359 | 416 |
|
| 417 | +class mrsw: |
| 418 | + def __init__(self): |
| 419 | + # critical-section lock & the data it protects |
| 420 | + self.rwOK = thread.allocate_lock() |
| 421 | + self.nr = 0 # number readers actively reading (not just waiting) |
| 422 | + self.nw = 0 # number writers either waiting to write or writing |
| 423 | + self.writing = 0 # 1 iff some thread is writing |
| 424 | + |
| 425 | + # conditions |
| 426 | + self.readOK = condition(self.rwOK) # OK to unblock readers |
| 427 | + self.writeOK = condition(self.rwOK) # OK to unblock writers |
| 428 | + |
| 429 | + def read_in(self): |
| 430 | + self.rwOK.acquire() |
| 431 | + while self.nw: |
| 432 | + self.readOK.wait() |
| 433 | + self.nr = self.nr + 1 |
| 434 | + self.rwOK.release() |
| 435 | + |
| 436 | + def read_out(self): |
| 437 | + self.rwOK.acquire() |
| 438 | + if self.nr <= 0: |
| 439 | + raise ValueError, \ |
| 440 | + '.read_out() invoked without an active reader' |
| 441 | + self.nr = self.nr - 1 |
| 442 | + if self.nr == 0: |
| 443 | + self.writeOK.signal() |
| 444 | + self.rwOK.release() |
| 445 | + |
| 446 | + def write_in(self): |
| 447 | + self.rwOK.acquire() |
| 448 | + self.nw = self.nw + 1 |
| 449 | + while self.writing or self.nr: |
| 450 | + self.writeOK.wait() |
| 451 | + self.writing = 1 |
| 452 | + self.rwOK.release() |
| 453 | + |
| 454 | + def write_out(self): |
| 455 | + self.rwOK.acquire() |
| 456 | + if not self.writing: |
| 457 | + raise ValueError, \ |
| 458 | + '.write_out() invoked without an active writer' |
| 459 | + self.writing = 0 |
| 460 | + self.nw = self.nw - 1 |
| 461 | + if self.nw: |
| 462 | + self.writeOK.signal() |
| 463 | + else: |
| 464 | + self.readOK.broadcast() |
| 465 | + self.rwOK.release() |
| 466 | + |
360 | 467 | # The rest of the file is a test case, that runs a number of parallelized |
361 | 468 | # quicksorts in parallel. If it works, you'll get about 600 lines of |
362 | 469 | # tracing output, with a line like |
|
0 commit comments