def with_lock(lock, fn):
    """
    Call fn with lock acquired. Guarantee that lock is released upon
    the return of fn.

    Returns the value returned by fn, or raises the exception raised
    by fn.

    (Lock can actually be anything responding to acquire() and
    release().)
    """
    lock.acquire()

    try:
        return fn()
    finally:
        lock.release()