Try a nonblocking lock several times and report whether the guard stayed open or blocked a later attempt.

nonblocking lock A `threading.Lock` acquired without blocking reports whether the guard was free, so a second attempt that fails shows the lock is already held.

Lock Guard Coordination Report

holds
lock_guard_coordination_report.py
Replay: real traced execution (multi-file project)
import threading


holds = 1
guard = threading.Lock()
acquired = 0
blocked = 0

for _ in range(holds):
    if guard.acquire(blocking=False):
        acquired += 1
    else:
        blocked += 1

status = "open" if acquired == 0 else "blocked" if blocked > 0 else "guarded"

if acquired > 0:
    guard.release()

print("holds=" + str(holds) + " acquired=" + str(acquired) + " blocked=" + str(blocked) + " status=" + status)
import threading


holds = 0
guard = threading.Lock()
acquired = 0
blocked = 0

for _ in range(holds):
    if guard.acquire(blocking=False):
        acquired += 1
    else:
        blocked += 1

status = "open" if acquired == 0 else "blocked" if blocked > 0 else "guarded"

if acquired > 0:
    guard.release()

print("holds=" + str(holds) + " acquired=" + str(acquired) + " blocked=" + str(blocked) + " status=" + status)
import threading


holds = 2
guard = threading.Lock()
acquired = 0
blocked = 0

for _ in range(holds):
    if guard.acquire(blocking=False):
        acquired += 1
    else:
        blocked += 1

status = "open" if acquired == 0 else "blocked" if blocked > 0 else "guarded"

if acquired > 0:
    guard.release()

print("holds=" + str(holds) + " acquired=" + str(acquired) + " blocked=" + str(blocked) + " status=" + status)
  1. holds ← 1, guard ← ⟨unlocked _thread.lock A⟩, acquired ← 0, blocked ← 0

    4holds→ 1 = 1  #@holds=0, 25guard→ ⟨unlocked _thread.lock A⟩ = threading<module 'threading' from '/usr/local/lib/python3.12/threading.py'>.Lock()6acquired→ 0 = 07blocked→ 0 = 0
  2. for _ in range(holds):

    9for _0 in range(holds1):10    if guard.acquire(blocking=False):11        acquired += 1
  3. acquired ← 1

    9for _ in range(holds):10    if guard⟨locked _thread.lock A⟩.acquire(blocking=False):11        acquired→ 1 += 112    else:
  4. status ← guarded

    15status→ guarded = "open" if acquired1 == 0 else "blocked" if blocked0 > 0 else "guarded"
  5. guard ← ⟨unlocked _thread.lock A⟩

    17if acquired1 > 0:18    guard→ ⟨unlocked _thread.lock A⟩.release()
  6. print("holds=" + str(holds) + " acquired=" + str(acquired) + " blocked…

    20print("holds=" + str(holds1) + " acquired=" + str(acquired1) + " blocked=" + str(blocked0) + " status=" + statusguarded)
    outputholds=1 acquired=1 blocked=0 status=guarded
  1. holds ← 0, guard ← ⟨unlocked _thread.lock A⟩, acquired ← 0, blocked ← 0

    4holds→ 0 = 05guard→ ⟨unlocked _thread.lock A⟩ = threading<module 'threading' from '/usr/local/lib/python3.12/threading.py'>.Lock()6acquired→ 0 = 07blocked→ 0 = 089for _ in range(holds):10    if guard.acquire(blocking=False):11        acquired += 112    else:13        blocked += 11415status→ open = "open" if acquired0 == 0 else "blocked" if blocked0 > 0 else "guarded"1617if acquired > 0:18    guard.release()1920print("holds=" + str(holds0) + " acquired=" + str(acquired0) + " blocked=" + str(blocked0) + " status=" + statusopen)
    outputholds=0 acquired=0 blocked=0 status=open
  1. holds ← 2, guard ← ⟨unlocked _thread.lock A⟩, acquired ← 0, blocked ← 0

    4holds→ 2 = 25guard→ ⟨unlocked _thread.lock A⟩ = threading<module 'threading' from '/usr/local/lib/python3.12/threading.py'>.Lock()6acquired→ 0 = 07blocked→ 0 = 0
  2. for _ in range(holds):

    pass 1 of 2
    9for _0 in range(holds2):10    if guard.acquire(blocking=False):11        acquired += 1
  3. acquired ← 1

    9for _ in range(holds):10    if guard⟨locked _thread.lock A⟩.acquire(blocking=False):11        acquired→ 1 += 112    else:
  4. for _ in range(holds):

    pass 2 of 2
    9for _1 in range(holds2):10    if guard.acquire(blocking=False):11        acquired += 1
  5. blocked ← 1

    10if guard.acquire(blocking=False):11    acquired += 112else:13    blocked→ 1 += 1
  6. status ← blocked

    15status→ blocked = "open" if acquired1 == 0 else "blocked" if blocked1 > 0 else "guarded"
  7. guard ← ⟨unlocked _thread.lock A⟩

    17if acquired1 > 0:18    guard→ ⟨unlocked _thread.lock A⟩.release()
  8. print("holds=" + str(holds) + " acquired=" + str(acquired) + " blocked…

    20print("holds=" + str(holds2) + " acquired=" + str(acquired1) + " blocked=" + str(blocked1) + " status=" + statusblocked)
    outputholds=2 acquired=1 blocked=1 status=blocked