# chapter=3 variant=concurrent 
def make_mutex():
    cell = llist(False)
    def the_mutex(m):
        return ((the_mutex("acquire")  # retry
                 if test_and_set(cell)
                 else True)
                if m == "acquire"
                else clear(cell) if m == "release"
                else error("unknown request -- mutex", m))
    return the_mutex

def clear(cell):
    set_head(cell, False)
def make_serializer():
    mutex = make_mutex()
    def serializer(f):
        def serialized_f():
            mutex("acquire")
            val = f()
            mutex("release")
            return val
        return serialized_f
    return serializer
def make_account_and_serializer(balance):
    def withdraw(amount):
        nonlocal balance
        if balance > amount:
            balance = balance - amount
            return balance
        else:
            return "Insufficient funds"
    def deposit(amount):
        nonlocal balance
        balance = balance + amount
        return balance
    balance_serializer = make_serializer()
    return lambda m: (withdraw if m == "withdraw"
                      else deposit if m == "deposit"
                      else balance if m == "balance"
                      else balance_serializer if m == "serializer"
                      else error("unknown request -- make_account", m))
def make_account(balance):
    def withdraw(amount):
        nonlocal balance
        if balance > amount:
            balance = balance - amount
            return balance
        else:
            return "Insufficient funds"
    def deposit(amount):
        nonlocal balance
        balance = balance + amount
        return balance
    protect = make_serializer()
    def dispatch(m):
        return (protect(withdraw) if m == "withdraw"
                else protect(deposit) if m == "deposit"
                else balance if m == "balance"
                else error("unknown request -- make_account", m))
    return dispatch
def transfer(from_account, to_account, amount):
    from_account("withdraw")(amount)
    to_account("deposit")(amount)

a1 = make_account(400)
a2 = make_account(100)

def peter():
    display(a1("balance"), "Peter balance a1 before")
    display(a2("balance"), "Peter balance a2 before")
    transfer(a1, a2, 50)
    display(a1("balance"), "Peter balance a1 after")
    display(a2("balance"), "Peter balance a2 after")

def paul():
    display(a1("balance"), "Paul balance a1")
    display(a2("balance"), "Paul balance a2")
    display(a1("balance"), "Paul balance a1")
    display(a2("balance"), "Paul balance a2")
    display(a1("balance"), "Paul balance a1")
    display(a2("balance"), "Paul balance a2")

print(concurrent_execute(peter, paul))
