Acquire the module lock. If a potential deadlock is detected, a _DeadlockError is raised. Otherwise, the lock is always acquired and True is returned.
(self)
| 302 | ) |
| 303 | |
| 304 | def acquire(self): |
| 305 | """ |
| 306 | Acquire the module lock. If a potential deadlock is detected, |
| 307 | a _DeadlockError is raised. |
| 308 | Otherwise, the lock is always acquired and True is returned. |
| 309 | """ |
| 310 | tid = _thread.get_ident() |
| 311 | with _BlockingOnManager(tid, self): |
| 312 | while True: |
| 313 | # Protect interaction with state on self with a per-module |
| 314 | # lock. This makes it safe for more than one thread to try to |
| 315 | # acquire the lock for a single module at the same time. |
| 316 | with self.lock: |
| 317 | if self.count == [] or self.owner == tid: |
| 318 | # If the lock for this module is unowned then we can |
| 319 | # take the lock immediately and succeed. If the lock |
| 320 | # for this module is owned by the running thread then |
| 321 | # we can also allow the acquire to succeed. This |
| 322 | # supports circular imports (thread T imports module A |
| 323 | # which imports module B which imports module A). |
| 324 | self.owner = tid |
| 325 | self.count.append(True) |
| 326 | return True |
| 327 | |
| 328 | # At this point we know the lock is held (because count != |
| 329 | # 0) by another thread (because owner != tid). We'll have |
| 330 | # to get in line to take the module lock. |
| 331 | |
| 332 | # But first, check to see if this thread would create a |
| 333 | # deadlock by acquiring this module lock. If it would |
| 334 | # then just stop with an error. |
| 335 | # |
| 336 | # It's not clear who is expected to handle this error. |
| 337 | # There is one handler in _lock_unlock_module but many |
| 338 | # times this method is called when entering the context |
| 339 | # manager _ModuleLockManager instead - so _DeadlockError |
| 340 | # will just propagate up to application code. |
| 341 | # |
| 342 | # This seems to be more than just a hypothetical - |
| 343 | # https://stackoverflow.com/questions/59509154 |
| 344 | # https://github.com/encode/django-rest-framework/issues/7078 |
| 345 | if self.has_deadlock(): |
| 346 | raise _DeadlockError(f'deadlock detected by {self!r}') |
| 347 | |
| 348 | # Check to see if we're going to be able to acquire the |
| 349 | # lock. If we are going to have to wait then increment |
| 350 | # the waiters so `self.release` will know to unblock us |
| 351 | # later on. We do this part non-blockingly so we don't |
| 352 | # get stuck here before we increment waiters. We have |
| 353 | # this extra acquire call (in addition to the one below, |
| 354 | # outside the self.lock context manager) to make sure |
| 355 | # self.wakeup is held when the next acquire is called (so |
| 356 | # we block). This is probably needlessly complex and we |
| 357 | # should just take self.wakeup in the return codepath |
| 358 | # above. |
| 359 | if self.wakeup.acquire(False): |
| 360 | self.waiters.append(None) |
| 361 |
no test coverage detected