{
  "id": "P119",
  "slug": "two-phase-commit",
  "key": "P119-two-phase-commit",
  "title": "Two-phase commit",
  "summary": "A coordinator and its participants as state machines, the coordinator crashing after any step: wait, ask peers, presume abort and presume commit at every crash point, with the time each participant's data stays locked in doubt.",
  "entry": "main.eml",
  "ui": "terminal",
  "readme": "# P119 - Two-phase commit\n\nA coordinator asks every participant to prepare, logs commit only if all\nvote yes, then tells each one. A participant that voted yes is in doubt,\nits data locked, until it hears. The coordinator can crash after any\nstep.\n\n- `main.eml` - the menu, the transaction, one run tick by tick, every\n  crash point under four strategies, and the votes and timing\n- `twopc.eml` - the coordinator and the participants, one coordinator\n  step a tick, the four strategies, the outcome of a run, and the count\n  over every pattern of votes\n\nHow each part works:\n\n- The coordinator's steps: prepare each participant in turn (a yes locks\n  its data, a no aborts at once and ends the asking), log the decision,\n  tell each participant that voted yes. A coordinator that comes back with\n  no decision logged decides abort; with one logged, it tells everyone\n  again.\n- A participant in doubt for the timeout acts on its strategy:\n  - wait keeps waiting for the coordinator;\n  - ask peers asks the coordinator if it is up, else the others: one that\n    has committed or aborted settles it, and one never asked aborts on\n    its own, and so does the one that asked;\n  - presume abort and presume commit decide on their own.\n- A run is wrong when it splits, goes against the coordinator's log, or\n  commits though someone never voted yes. As in the corpus case\n  `two-phase-commit-window`, with three yes votes, a timeout of 5 ticks and\n  the coordinator back after 10:\n  - wait and ask peers are never wrong, and hold the data for up to 17\n    and 14 ticks;\n  - presume abort is wrong at 3 of the 9 crash points, and presume commit\n    at 3.\n- If the coordinator never comes back, wait is left in doubt at 6 crash\n  points, and ask peers only at 2: after every participant voted yes and\n  before any heard the decision. That window is where two-phase commit\n  blocks.\n- A timeout of 3 ticks makes presume abort wrong with no crash at all:\n  each participant is in doubt for 4 ticks before it hears.\n\nWhat is checked: menu choices 1 to 6; one of the four strategies; a crash\npoint from 0 to the last step, or none; 2 to 4 votes of yes or no; a\ntimeout from 1 to 9 ticks; the coordinator back after 1 to 30 ticks, or\nnever. An empty answer cancels.\n\nSessions: `sessions/basic.in` gives:\n- the transaction and every crash point;\n- runs: wait after step 3, presume abort after step 4 (it aborts what the\n  coordinator committed), ask peers after steps 2 and 4;\n- the coordinator never back: every crash point, and ask peers after step\n  3, left in doubt;\n- the votes yes, no, yes and yes, presume commit after step 1, and every\n  crash point;\n- three yes votes again with a timeout of 3, every crash point, and\n  presume abort with no crash.\n\n`sessions/bad-input.in` gives:\n- menu choices 0 and x;\n- runs: a cancel, the strategy fast, then crash points of a cancel, 8, x\n  and -1;\n- votes: a cancel, one vote, five votes, yes maybe, and YES yes;\n- timing: a cancel, 0, 10 and x, then 5 with a cancel, 0, 31 and\n  sometime.\n\nBuilt on the verified corpus case `two-phase-commit-window` (a coordinator\ncrash leaves participants in doubt, and presuming an outcome trades the\nblocking for a split).\n",
  "modules": [
    {
      "name": "main.eml",
      "eml": "# P119 two-phase commit: one run tick by tick with the coordinator crashing\n# after a chosen step, every crash point under four strategies for a\n# participant in doubt, and every pattern of votes counted.\nimport twopc\n\ndef plain(s):\n    0 => i\n    len(s) => j\n    while i < j and s[i] == \" \":\n        i + 1 => i\n    while j > i and s[j - 1] == \" \":\n        j - 1 => j\n    return s[i:j]\n\ndef pad(s, n):\n    s => out\n    while len(out) < n:\n        out + \" \" => out\n    return out\n\ndef lpad(s, n):\n    s => out\n    while len(out) < n:\n        \" \" + out => out\n    return out\n\ndef is_digits(s):\n    if len(s) == 0:\n        return False\n    for c in s:\n        if c < \"0\" or c > \"9\":\n            return False\n    return True\n\ndef ticks_text(n):\n    if n == 1:\n        return \"1 tick\"\n    return str(n) + \" ticks\"\n\ndef names_text(ns):\n    \"\" => out\n    for i in [0:len(ns) - 1]:\n        if i > 0 and i == len(ns) - 1:\n            out + \" and \" => out\n        elif i > 0:\n            out + \", \" => out\n        out + ns[i] => out\n    return out\n\ndef votes_text(votes):\n    [] => out\n    for v in votes:\n        if v:\n            out + [\"yes\"] => out\n        else:\n            out + [\"no\"] => out\n    return out\n\ndef back_text(back):\n    if back == 0:\n        return \"never comes back\"\n    return \"is back after \" + ticks_text(back)\n\ndef show(votes, timeout, back):\n    [] => ps\n    for i in [0:len(votes) - 1]:\n        ps + [twopc.name(i)] => ps\n    (\"Participants \" + names_text(ps) + \" vote \" + names_text(votes_text(votes)) + \".\") ^0\n    twopc.run(votes, \"wait\", timeout, back, 0 - 1, 40) => r\n    \"\" => line\n    for k in [0:len(r[7]) - 1]:\n        if line != \"\":\n            line + \", \" => line\n        line + str(k + 1) + \" \" + r[7][k] => line\n    (\"With no crash, the coordinator takes \" + str(r[5]) + \" steps, one a tick: \" + line + \".\") ^0\n    (\"A participant in doubt for \" + ticks_text(timeout) + \" acts on its strategy; a crashed coordinator \" + back_text(back) + \".\") ^0\n\ndef ask_strategy():\n    plain(input(\"strategy (wait, ask peers, presume abort or presume commit)> \")) => s\n    if s == \"\":\n        \"Cancelled.\" ^0\n        return \"\"\n    for x in twopc.strategies():\n        if x == s:\n            return s\n    \"Type wait, ask peers, presume abort or presume commit.\" ^0\n    return \"\"\n\ndef verdict(r):\n    twopc.outcome(r) => o\n    [] => c\n    [] => a\n    [] => d\n    for i in [0:len(r[1]) - 1]:\n        if r[1][i] == \"committed\":\n            c + [twopc.name(i)] => c\n        elif r[1][i] == \"aborted\":\n            a + [twopc.name(i)] => a\n        elif r[1][i] == \"prepared\":\n            d + [twopc.name(i)] => d\n    if o[0]:\n        (\"Split: \" + names_text(c) + \" committed, and \" + names_text(a) + \" aborted.\") ^0\n    if o[1]:\n        [] => wrong\n        for i in [0:len(r[1]) - 1]:\n            if (r[1][i] == \"committed\" or r[1][i] == \"aborted\") and r[1][i] != twopc.past(r[4]):\n                wrong + [twopc.name(i)] => wrong\n        (\"Against the coordinator, which logged \" + r[4] + \": \" + names_text(wrong) + \" \" + opposite(r[4]) + \".\") ^0\n    if o[3]:\n        [] => never\n        for i in [0:len(r[1]) - 1]:\n            if r[8][i] < 0:\n                never + [twopc.name(i)] => never\n        (\"Committed without every yes: \" + names_text(c) + \" committed, though \" + names_text(never) + \" never voted yes.\") ^0\n    if o[2]:\n        (\"Still in doubt at tick 40, data locked: \" + names_text(d) + \".\") ^0\n    if len(c) == 0 and len(a) == 0 and len(d) == 0:\n        if r[4] == \"\":\n            \"No participant was asked, and the coordinator never logged a decision.\" ^0\n        else:\n            (\"No participant was asked; the coordinator logged \" + r[4] + \".\") ^0\n    elif not twopc.wrong(o) and not o[2]:\n        \"aborted\" => common\n        if len(c) > 0:\n            \"committed\" => common\n        if r[4] == \"\":\n            (\"Atomic: everyone who finished \" + common + \", and the coordinator never logged a decision.\") ^0\n        else:\n            (\"Atomic: everyone who finished \" + common + \", as the coordinator logged.\") ^0\n\ndef opposite(d):\n    if d == \"commit\":\n        return \"aborted\"\n    return \"committed\"\n\ndef one_run(votes, timeout, back):\n    ask_strategy() => s\n    if s == \"\":\n        return\n    twopc.crash_points(votes, timeout, back, 40) => n\n    plain(input(\"crash after step (0 to \" + str(n) + \", or none)> \")) => t\n    if t == \"\":\n        \"Cancelled.\" ^0\n        return\n    0 - 1 => crash\n    if t != \"none\":\n        if not is_digits(t) or len(t) > 2 or int(t) > n:\n            (\"Type a step from 0 to \" + str(n) + \", or none.\") ^0\n            return\n        int(t) => crash\n    twopc.run(votes, s, timeout, back, crash, 40) => r\n    if crash < 0:\n        (s + \", no crash:\") ^0\n    else:\n        (s + \", the coordinator crashing after step \" + str(crash) + \" (it \" + back_text(back) + \"):\") ^0\n    for e in r[0]:\n        (\"  tick \" + lpad(str(e[0]), 2) + \"  \" + e[1]) ^0\n    for i in [0:len(votes) - 1]:\n        twopc.name(i) => p\n        r[1][i] => st\n        if st == \"prepared\":\n            (\"  \" + p + \" still in doubt at tick 40, its data locked for \" + ticks_text(r[2][i]) + \".\") ^0\n        elif st == \"idle\":\n            (\"  \" + p + \" was never asked.\") ^0\n        elif r[2][i] == 0:\n            (\"  \" + p + \" \" + st + \" at tick \" + str(r[6][i]) + \", never locked.\") ^0\n        else:\n            (\"  \" + p + \" \" + st + \" at tick \" + str(r[6][i]) + \", its data locked for \" + ticks_text(r[2][i]) + \".\") ^0\n    verdict(r)\n\ndef every(votes, timeout, back):\n    twopc.crash_points(votes, timeout, back, 40) => n\n    twopc.run(votes, \"wait\", timeout, back, 0 - 1, 40)[7] => labels\n    (\"Every crash point, votes \" + names_text(votes_text(votes)) + \"; a participant in doubt acts after \" + ticks_text(timeout) + \"; the coordinator \" + back_text(back) + \":\") ^0\n    [9, 9, 13, 14] => widths\n    \"  \" + pad(\"crash after\", 26) => head\n    for k in [0:3]:\n        head + \"  \" + pad(twopc.strategies()[k], widths[k]) => head\n    rstrip(head) ^0\n    [0, 0, 0, 0] => wrong\n    [0, 0, 0, 0] => doubt\n    [0, 0, 0, 0] => most\n    [] => stuck\n    for c in [0 - 1:n]:\n        \"none\" => label\n        if c == 0:\n            \"0 before any step\" => label\n        elif c > 0:\n            str(c) + \" \" + labels[c - 1] => label\n        \"  \" + pad(label, 26) => line\n        for k in [0:3]:\n            twopc.run(votes, twopc.strategies()[k], timeout, back, c, 40) => r\n            twopc.outcome(r) => o\n            twopc.letters(r) => cell\n            if twopc.wrong(o):\n                cell + \" !\" => cell\n                wrong[k] + 1 => wrong[k]\n            if o[2]:\n                doubt[k] + 1 => doubt[k]\n                if k == 1:\n                    stuck + [str(c)] => stuck\n            for x in r[2]:\n                if x > most[k]:\n                    x => most[k]\n            line + \"  \" + pad(cell, widths[k]) => line\n        rstrip(line) ^0\n    \"  c committed, a aborted, ? still in doubt at tick 40, - never asked; ! wrong: split, against the coordinator, or committed without every yes.\" ^0\n    for k in [0:3]:\n        (\"  \" + pad(twopc.strategies()[k] + \":\", 16) + \"wrong at \" + str(wrong[k]) + \" of \" + str(n + 2) + \" crash points, still in doubt at \" + str(doubt[k]) + \", data locked at most \" + ticks_text(most[k]) + \".\") ^0\n    if len(stuck) == 1:\n        (\"Ask peers is still in doubt only at crash point \" + stuck[0] + \": every participant had voted yes, and none had heard the decision.\") ^0\n    elif len(stuck) > 1:\n        (\"Ask peers is still in doubt only at crash points \" + names_text(stuck) + \": every participant had voted yes, and none had heard the decision.\") ^0\n    (\"Every pattern of \" + str(len(votes)) + \" votes, each with every crash point and none:\") ^0\n    \"  strategy          runs  wrong  in doubt at the end  most ticks locked\" ^0\n    for s in twopc.strategies():\n        twopc.census(len(votes), s, timeout, back, 40) => x\n        (\"  \" + pad(s, 14) + lpad(str(x[0]), 8) + lpad(str(x[1]), 7) + lpad(str(x[2]), 21) + lpad(str(x[3]), 19)) ^0\n\ndef rstrip(s):\n    len(s) => j\n    while j > 0 and s[j - 1] == \" \":\n        j - 1 => j\n    return s[0:j]\n\ndef ask_votes():\n    plain(input(\"votes (2 to 4 of yes or no, such as yes yes no)> \")) => t\n    if t == \"\":\n        \"Cancelled.\" ^0\n        return []\n    [] => out\n    \"\" => w\n    for c in t + \" \":\n        if c == \" \":\n            if w == \"yes\":\n                out + [True] => out\n            elif w == \"no\":\n                out + [False] => out\n            elif w != \"\":\n                \"Type 2 to 4 votes, each yes or no.\" ^0\n                return []\n            \"\" => w\n        else:\n            w + c => w\n    if len(out) < 2 or len(out) > 4:\n        \"Type 2 to 4 votes, each yes or no.\" ^0\n        return []\n    return out\n\ndef ask_timing(timeout, back):\n    plain(input(\"in doubt for how long before acting (1 to 9 ticks)> \")) => t\n    if t == \"\":\n        \"Cancelled.\" ^0\n        return []\n    if len(t) != 1 or t < \"1\" or t > \"9\":\n        \"Type a whole number from 1 to 9.\" ^0\n        return []\n    plain(input(\"coordinator back after (1 to 30 ticks, or never)> \")) => b\n    if b == \"\":\n        \"Cancelled.\" ^0\n        return []\n    0 => nb\n    if b != \"never\":\n        if not is_digits(b) or len(b) > 2 or int(b) < 1 or int(b) > 30:\n            \"Type a whole number from 1 to 30, or never.\" ^0\n            return []\n        int(b) => nb\n    return [int(t), nb]\n\n[True, True, True] => votes\n5 => timeout\n10 => back\n\n\"== Two-phase commit ==\" ^0\n\"A coordinator asks every participant to prepare, logs commit only if all vote yes, then tells each one. A participant that voted yes waits in doubt, its data locked, until it hears.\" ^0\nTrue => running\nwhile running:\n    \"\" ^0\n    \"1) the transaction  2) one run  3) every crash point  4) votes  5) timing  6) quit\" ^0\n    plain(input(\"choice> \")) => choice\n    if choice == \"1\":\n        show(votes, timeout, back)\n    elif choice == \"2\":\n        one_run(votes, timeout, back)\n    elif choice == \"3\":\n        every(votes, timeout, back)\n    elif choice == \"4\":\n        ask_votes() => v\n        if len(v) > 0:\n            v => votes\n            show(votes, timeout, back)\n    elif choice == \"5\":\n        ask_timing(timeout, back) => x\n        if len(x) > 0:\n            x[0] => timeout\n            x[1] => back\n            show(votes, timeout, back)\n    elif choice == \"6\":\n        False => running\n    else:\n        \"Pick a number from 1 to 6.\" ^0\n\"Bye.\" ^0\n",
      "python": "import twopc\n\ndef plain(s):\n    i = 0\n    j = len(s)\n    while i < j and s[i] == \" \":\n        i = i + 1\n    while j > i and s[j - 1] == \" \":\n        j = j - 1\n    return s[i:j]\n\ndef pad(s, n):\n    out = s\n    while len(out) < n:\n        out = out + \" \"\n    return out\n\ndef lpad(s, n):\n    out = s\n    while len(out) < n:\n        out = \" \" + out\n    return out\n\ndef is_digits(s):\n    if len(s) == 0:\n        return False\n    for c in s:\n        if c < \"0\" or c > \"9\":\n            return False\n    return True\n\ndef ticks_text(n):\n    if n == 1:\n        return \"1 tick\"\n    return str(n) + \" ticks\"\n\ndef names_text(ns):\n    out = \"\"\n    for i in range(0, len(ns)):\n        if i > 0 and i == len(ns) - 1:\n            out = out + \" and \"\n        elif i > 0:\n            out = out + \", \"\n        out = out + ns[i]\n    return out\n\ndef votes_text(votes):\n    out = []\n    for v in votes:\n        if v:\n            out = out + [\"yes\"]\n        else:\n            out = out + [\"no\"]\n    return out\n\ndef back_text(back):\n    if back == 0:\n        return \"never comes back\"\n    return \"is back after \" + ticks_text(back)\n\ndef show(votes, timeout, back):\n    ps = []\n    for i in range(0, len(votes)):\n        ps = ps + [twopc.name(i)]\n    print(\"Participants \" + names_text(ps) + \" vote \" + names_text(votes_text(votes)) + \".\")\n    r = twopc.run(votes, \"wait\", timeout, back, 0 - 1, 40)\n    line = \"\"\n    for k in range(0, len(r[7])):\n        if line != \"\":\n            line = line + \", \"\n        line = line + str(k + 1) + \" \" + r[7][k]\n    print(\"With no crash, the coordinator takes \" + str(r[5]) + \" steps, one a tick: \" + line + \".\")\n    print(\"A participant in doubt for \" + ticks_text(timeout) + \" acts on its strategy; a crashed coordinator \" + back_text(back) + \".\")\n\ndef ask_strategy():\n    s = plain(input(\"strategy (wait, ask peers, presume abort or presume commit)> \"))\n    if s == \"\":\n        print(\"Cancelled.\")\n        return \"\"\n    for x in twopc.strategies():\n        if x == s:\n            return s\n    print(\"Type wait, ask peers, presume abort or presume commit.\")\n    return \"\"\n\ndef verdict(r):\n    o = twopc.outcome(r)\n    c = []\n    a = []\n    d = []\n    for i in range(0, len(r[1])):\n        if r[1][i] == \"committed\":\n            c = c + [twopc.name(i)]\n        elif r[1][i] == \"aborted\":\n            a = a + [twopc.name(i)]\n        elif r[1][i] == \"prepared\":\n            d = d + [twopc.name(i)]\n    if o[0]:\n        print(\"Split: \" + names_text(c) + \" committed, and \" + names_text(a) + \" aborted.\")\n    if o[1]:\n        wrong = []\n        for i in range(0, len(r[1])):\n            if (r[1][i] == \"committed\" or r[1][i] == \"aborted\") and r[1][i] != twopc.past(r[4]):\n                wrong = wrong + [twopc.name(i)]\n        print(\"Against the coordinator, which logged \" + r[4] + \": \" + names_text(wrong) + \" \" + opposite(r[4]) + \".\")\n    if o[3]:\n        never = []\n        for i in range(0, len(r[1])):\n            if r[8][i] < 0:\n                never = never + [twopc.name(i)]\n        print(\"Committed without every yes: \" + names_text(c) + \" committed, though \" + names_text(never) + \" never voted yes.\")\n    if o[2]:\n        print(\"Still in doubt at tick 40, data locked: \" + names_text(d) + \".\")\n    if len(c) == 0 and len(a) == 0 and len(d) == 0:\n        if r[4] == \"\":\n            print(\"No participant was asked, and the coordinator never logged a decision.\")\n        else:\n            print(\"No participant was asked; the coordinator logged \" + r[4] + \".\")\n    elif not twopc.wrong(o) and not o[2]:\n        common = \"aborted\"\n        if len(c) > 0:\n            common = \"committed\"\n        if r[4] == \"\":\n            print(\"Atomic: everyone who finished \" + common + \", and the coordinator never logged a decision.\")\n        else:\n            print(\"Atomic: everyone who finished \" + common + \", as the coordinator logged.\")\n\ndef opposite(d):\n    if d == \"commit\":\n        return \"aborted\"\n    return \"committed\"\n\ndef one_run(votes, timeout, back):\n    s = ask_strategy()\n    if s == \"\":\n        return\n    n = twopc.crash_points(votes, timeout, back, 40)\n    t = plain(input(\"crash after step (0 to \" + str(n) + \", or none)> \"))\n    if t == \"\":\n        print(\"Cancelled.\")\n        return\n    crash = 0 - 1\n    if t != \"none\":\n        if not is_digits(t) or len(t) > 2 or int(t) > n:\n            print(\"Type a step from 0 to \" + str(n) + \", or none.\")\n            return\n        crash = int(t)\n    r = twopc.run(votes, s, timeout, back, crash, 40)\n    if crash < 0:\n        print(s + \", no crash:\")\n    else:\n        print(s + \", the coordinator crashing after step \" + str(crash) + \" (it \" + back_text(back) + \"):\")\n    for e in r[0]:\n        print(\"  tick \" + lpad(str(e[0]), 2) + \"  \" + e[1])\n    for i in range(0, len(votes)):\n        p = twopc.name(i)\n        st = r[1][i]\n        if st == \"prepared\":\n            print(\"  \" + p + \" still in doubt at tick 40, its data locked for \" + ticks_text(r[2][i]) + \".\")\n        elif st == \"idle\":\n            print(\"  \" + p + \" was never asked.\")\n        elif r[2][i] == 0:\n            print(\"  \" + p + \" \" + st + \" at tick \" + str(r[6][i]) + \", never locked.\")\n        else:\n            print(\"  \" + p + \" \" + st + \" at tick \" + str(r[6][i]) + \", its data locked for \" + ticks_text(r[2][i]) + \".\")\n    verdict(r)\n\ndef every(votes, timeout, back):\n    n = twopc.crash_points(votes, timeout, back, 40)\n    labels = twopc.run(votes, \"wait\", timeout, back, 0 - 1, 40)[7]\n    print(\"Every crash point, votes \" + names_text(votes_text(votes)) + \"; a participant in doubt acts after \" + ticks_text(timeout) + \"; the coordinator \" + back_text(back) + \":\")\n    widths = [9, 9, 13, 14]\n    head = \"  \" + pad(\"crash after\", 26)\n    for k in range(0, 4):\n        head = head + \"  \" + pad(twopc.strategies()[k], widths[k])\n    print(rstrip(head))\n    wrong = [0, 0, 0, 0]\n    doubt = [0, 0, 0, 0]\n    most = [0, 0, 0, 0]\n    stuck = []\n    for c in range(0 - 1, n+1):\n        label = \"none\"\n        if c == 0:\n            label = \"0 before any step\"\n        elif c > 0:\n            label = str(c) + \" \" + labels[c - 1]\n        line = \"  \" + pad(label, 26)\n        for k in range(0, 4):\n            r = twopc.run(votes, twopc.strategies()[k], timeout, back, c, 40)\n            o = twopc.outcome(r)\n            cell = twopc.letters(r)\n            if twopc.wrong(o):\n                cell = cell + \" !\"\n                wrong[k] = wrong[k] + 1\n            if o[2]:\n                doubt[k] = doubt[k] + 1\n                if k == 1:\n                    stuck = stuck + [str(c)]\n            for x in r[2]:\n                if x > most[k]:\n                    most[k] = x\n            line = line + \"  \" + pad(cell, widths[k])\n        print(rstrip(line))\n    print(\"  c committed, a aborted, ? still in doubt at tick 40, - never asked; ! wrong: split, against the coordinator, or committed without every yes.\")\n    for k in range(0, 4):\n        print(\"  \" + pad(twopc.strategies()[k] + \":\", 16) + \"wrong at \" + str(wrong[k]) + \" of \" + str(n + 2) + \" crash points, still in doubt at \" + str(doubt[k]) + \", data locked at most \" + ticks_text(most[k]) + \".\")\n    if len(stuck) == 1:\n        print(\"Ask peers is still in doubt only at crash point \" + stuck[0] + \": every participant had voted yes, and none had heard the decision.\")\n    elif len(stuck) > 1:\n        print(\"Ask peers is still in doubt only at crash points \" + names_text(stuck) + \": every participant had voted yes, and none had heard the decision.\")\n    print(\"Every pattern of \" + str(len(votes)) + \" votes, each with every crash point and none:\")\n    print(\"  strategy          runs  wrong  in doubt at the end  most ticks locked\")\n    for s in twopc.strategies():\n        x = twopc.census(len(votes), s, timeout, back, 40)\n        print(\"  \" + pad(s, 14) + lpad(str(x[0]), 8) + lpad(str(x[1]), 7) + lpad(str(x[2]), 21) + lpad(str(x[3]), 19))\n\ndef rstrip(s):\n    j = len(s)\n    while j > 0 and s[j - 1] == \" \":\n        j = j - 1\n    return s[0:j]\n\ndef ask_votes():\n    t = plain(input(\"votes (2 to 4 of yes or no, such as yes yes no)> \"))\n    if t == \"\":\n        print(\"Cancelled.\")\n        return []\n    out = []\n    w = \"\"\n    for c in t + \" \":\n        if c == \" \":\n            if w == \"yes\":\n                out = out + [True]\n            elif w == \"no\":\n                out = out + [False]\n            elif w != \"\":\n                print(\"Type 2 to 4 votes, each yes or no.\")\n                return []\n            w = \"\"\n        else:\n            w = w + c\n    if len(out) < 2 or len(out) > 4:\n        print(\"Type 2 to 4 votes, each yes or no.\")\n        return []\n    return out\n\ndef ask_timing(timeout, back):\n    t = plain(input(\"in doubt for how long before acting (1 to 9 ticks)> \"))\n    if t == \"\":\n        print(\"Cancelled.\")\n        return []\n    if len(t) != 1 or t < \"1\" or t > \"9\":\n        print(\"Type a whole number from 1 to 9.\")\n        return []\n    b = plain(input(\"coordinator back after (1 to 30 ticks, or never)> \"))\n    if b == \"\":\n        print(\"Cancelled.\")\n        return []\n    nb = 0\n    if b != \"never\":\n        if not is_digits(b) or len(b) > 2 or int(b) < 1 or int(b) > 30:\n            print(\"Type a whole number from 1 to 30, or never.\")\n            return []\n        nb = int(b)\n    return [int(t), nb]\n\nvotes = [True, True, True]\ntimeout = 5\nback = 10\nprint(\"== Two-phase commit ==\")\nprint(\"A coordinator asks every participant to prepare, logs commit only if all vote yes, then tells each one. A participant that voted yes waits in doubt, its data locked, until it hears.\")\nrunning = True\nwhile running:\n    print(\"\")\n    print(\"1) the transaction  2) one run  3) every crash point  4) votes  5) timing  6) quit\")\n    choice = plain(input(\"choice> \"))\n    if choice == \"1\":\n        show(votes, timeout, back)\n    elif choice == \"2\":\n        one_run(votes, timeout, back)\n    elif choice == \"3\":\n        every(votes, timeout, back)\n    elif choice == \"4\":\n        v = ask_votes()\n        if len(v) > 0:\n            votes = v\n            show(votes, timeout, back)\n    elif choice == \"5\":\n        x = ask_timing(timeout, back)\n        if len(x) > 0:\n            timeout = x[0]\n            back = x[1]\n            show(votes, timeout, back)\n    elif choice == \"6\":\n        running = False\n    else:\n        print(\"Pick a number from 1 to 6.\")\nprint(\"Bye.\")\n"
    },
    {
      "name": "twopc.eml",
      "eml": "# P119 two-phase commit - a coordinator and its participants as state\n# machines, one coordinator step a tick: prepare each participant (it votes,\n# and a yes locks its data), log the decision, tell each participant that\n# voted yes. A crash of the coordinator after any step leaves participants\n# that voted yes in doubt, their data locked. As in the corpus case\n# two-phase-commit-window, what a participant does then decides between\n# blocking and a split outcome:\n#   wait            keeps waiting for the coordinator;\n#   ask peers       asks the coordinator if it is up, else the other\n#                   participants: one that knows the outcome, or one never\n#                   asked (which then aborts on its own), settles it;\n#   presume abort   aborts once the timeout has passed;\n#   presume commit  commits once the timeout has passed.\n# A coordinator that comes back with no decision logged decides abort.\n\ndef strategies():\n    return [\"wait\", \"ask peers\", \"presume abort\", \"presume commit\"]\n\ndef name(i):\n    return \"P\" + str(i + 1)\n\ndef run(votes, strategy, timeout, back, crash, horizon):\n    # votes: a yes or no per participant; back: ticks the coordinator stays\n    # down after a crash (0: it never comes back); crash: the number of\n    # steps after which it crashes (-1: none).\n    # [events as [tick, text], final states, ticks locked, still locked,\n    #  the coordinator's logged decision (\"\" if none), steps taken, the\n    #  tick each participant finished (-1 if not), a label per step, the\n    #  tick each voted yes (-1 if it never did)]\n    len(votes) => n\n    [] => state\n    [] => since\n    [] => until\n    [] => noted\n    for v in votes:\n        state + [\"idle\"] => state\n        since + [0 - 1] => since\n        until + [0 - 1] => until\n        noted + [\"\"] => noted\n    [] => events\n    [] => labels\n    \"prepare\" => phase\n    0 => nxt\n    \"\" => decision\n    False => logged\n    [] => told\n    0 => tell_at\n    False => done\n    0 => steps\n    False => down\n    False => crashed_once\n    0 - 1 => up_at\n    if crash == 0:\n        True => down\n        True => crashed_once\n        events + [[0, \"the coordinator crashes before its first step\"]] => events\n        if back > 0:\n            back + 1 => up_at\n    for t in [1:horizon]:\n        if down and up_at == t:\n            False => down\n            \"tell\" => phase\n            0 => tell_at\n            if not logged:\n                events + [[t, \"the coordinator is back; with no decision logged, it decides abort\"]] => events\n                \"abort\" => decision\n                True => logged\n                steps + 1 => steps\n                labels + [\"decide abort on its return\"] => labels\n            else:\n                events + [[t, \"the coordinator is back with \" + decision + \" logged, and tells everyone again\"]] => events\n        elif not down and not done:\n            steps + 1 => steps\n            if phase == \"prepare\":\n                nxt => i\n                nxt + 1 => nxt\n                labels + [\"prepare \" + name(i)] => labels\n                if state[i] == \"idle\" and votes[i]:\n                    \"prepared\" => state[i]\n                    t => since[i]\n                    told + [i] => told\n                    events + [[t, \"prepare \" + name(i) + \": it votes yes and locks its data\"]] => events\n                    if nxt == n:\n                        \"decide\" => phase\n                else:\n                    if state[i] == \"idle\":\n                        \"aborted\" => state[i]\n                        t => until[i]\n                    events + [[t, \"prepare \" + name(i) + \": it votes no\"]] => events\n                    \"decide\" => phase\n                    \"abort\" => decision\n            elif phase == \"decide\":\n                if decision == \"\":\n                    \"commit\" => decision\n                True => logged\n                labels + [\"log \" + decision] => labels\n                events + [[t, \"the coordinator logs \" + decision]] => events\n                \"tell\" => phase\n                0 => tell_at\n            else:\n                told[tell_at] => i\n                tell_at + 1 => tell_at\n                labels + [\"tell \" + name(i)] => labels\n                if state[i] == \"prepared\":\n                    past(decision) => state[i]\n                    t => until[i]\n                    events + [[t, \"tell \" + name(i) + \" \" + decision + \": it \" + verb(decision) + \" and unlocks\"]] => events\n                elif state[i] == past(decision):\n                    events + [[t, \"tell \" + name(i) + \" \" + decision + \": it had already \" + past(decision)]] => events\n                else:\n                    events + [[t, \"tell \" + name(i) + \" \" + decision + \": but it had already \" + state[i]]] => events\n            if crash > 0 and steps == crash and not crashed_once:\n                True => down\n                True => crashed_once\n                events + [[t, \"the coordinator crashes\"]] => events\n                if back > 0:\n                    t + back + 1 => up_at\n        if not down and phase == \"tell\" and tell_at >= len(told):\n            True => done\n        \"\" => known\n        if logged:\n            decision => known\n        for i in [0:n - 1]:\n            if state[i] == \"prepared\" and t - since[i] >= timeout:\n                learn(state, votes, i, strategy, down, known, n) => r\n                if r[0] != \"\":\n                    r[0] => state[i]\n                    t => until[i]\n                    if r[1] >= 0:\n                        \"aborted\" => state[r[1]]\n                        t => until[r[1]]\n                        events + [[t, name(r[1]) + \", never asked, aborts on its own\"]] => events\n                    events + [[t, name(i) + \" in doubt: \" + r[2]]] => events\n                elif noted[i] != r[2]:\n                    r[2] => noted[i]\n                    events + [[t, name(i) + \" in doubt: \" + r[2]]] => events\n    [] => locked\n    [] => still\n    for i in [0:n - 1]:\n        if since[i] < 0:\n            locked + [0] => locked\n            still + [False] => still\n        elif until[i] < 0:\n            locked + [horizon - since[i]] => locked\n            still + [True] => still\n        else:\n            locked + [until[i] - since[i]] => locked\n            still + [False] => still\n    \"\" => final\n    if logged:\n        decision => final\n    return [events, state, locked, still, final, steps, until, labels, since]\n\ndef verb(d):\n    if d == \"commit\":\n        return \"commits\"\n    return \"aborts\"\n\ndef past(d):\n    if d == \"commit\":\n        return \"committed\"\n    return \"aborted\"\n\ndef learn(state, votes, i, strategy, down, decision, n):\n    # what participant i in doubt does now: [new state or \"\", a participant\n    # never asked that aborts on its own or -1, what it says]\n    if strategy == \"presume abort\":\n        return [\"aborted\", 0 - 1, \"presumes abort and unlocks\"]\n    if strategy == \"presume commit\":\n        return [\"committed\", 0 - 1, \"presumes commit and unlocks\"]\n    if strategy == \"wait\":\n        return [\"\", 0 - 1, \"waits, its data locked\"]\n    if not down:\n        if decision != \"\":\n            return [past(decision), 0 - 1, \"asks the coordinator: \" + decision]\n        return [\"\", 0 - 1, \"asks the coordinator: no decision yet, so it waits\"]\n    for j in [0:n - 1]:\n        if j != i and (state[j] == \"committed\" or state[j] == \"aborted\"):\n            return [state[j], 0 - 1, \"asks \" + name(j) + \", which has \" + state[j] + \", and \" + verb(present(state[j])) + \" too\"]\n    for j in [0:n - 1]:\n        if j != i and state[j] == \"idle\":\n            return [\"aborted\", j, \"asks \" + name(j) + \", and aborts too\"]\n    return [\"\", 0 - 1, \"asks the others, all in doubt too, so it waits, its data locked\"]\n\ndef present(s):\n    if s == \"committed\":\n        return \"commit\"\n    return \"abort\"\n\ndef outcome(r):\n    # [split, against the coordinator, in doubt at the end, committed though\n    #  someone never voted yes]\n    False => c\n    False => a\n    False => doubt\n    False => against\n    False => no_yes\n    for i in [0:len(r[1]) - 1]:\n        if r[8][i] < 0:\n            True => no_yes\n        r[1][i] => s\n        if s == \"committed\":\n            True => c\n        if s == \"aborted\":\n            True => a\n        if s == \"prepared\":\n            True => doubt\n        if r[4] != \"\" and (s == \"committed\" or s == \"aborted\") and s != past(r[4]):\n            True => against\n    return [c and a, against, doubt, c and no_yes]\n\ndef wrong(o):\n    return o[0] or o[1] or o[3]\n\ndef letters(r):\n    \"\" => out\n    for s in r[1]:\n        if out != \"\":\n            out + \" \" => out\n        if s == \"committed\":\n            out + \"c\" => out\n        elif s == \"aborted\":\n            out + \"a\" => out\n        elif s == \"prepared\":\n            out + \"?\" => out\n        else:\n            out + \"-\" => out\n    return out\n\ndef crash_points(votes, timeout, back, horizon):\n    # the number of steps the coordinator takes with no crash\n    return run(votes, \"wait\", timeout, back, 0 - 1, horizon)[5]\n\ndef patterns(n):\n    [[]] => out\n    for k in [1:n]:\n        [] => more\n        for p in out:\n            more + [p + [True]] => more\n            more + [p + [False]] => more\n        more => out\n    return out\n\ndef census(n, strategy, timeout, back, horizon):\n    # over every vote pattern and every crash point (and none): [runs,\n    # wrong (split or against the coordinator), in doubt at the end, most\n    # ticks locked]\n    0 => runs\n    0 => bad\n    0 => doubt\n    0 => most\n    for votes in patterns(n):\n        for c in [0 - 1:crash_points(votes, timeout, back, horizon)]:\n            run(votes, strategy, timeout, back, c, horizon) => r\n            outcome(r) => o\n            runs + 1 => runs\n            if wrong(o):\n                bad + 1 => bad\n            if o[2]:\n                doubt + 1 => doubt\n            for x in r[2]:\n                if x > most:\n                    x => most\n    return [runs, bad, doubt, most]\n",
      "python": "def strategies():\n    return [\"wait\", \"ask peers\", \"presume abort\", \"presume commit\"]\n\ndef name(i):\n    return \"P\" + str(i + 1)\n\ndef run(votes, strategy, timeout, back, crash, horizon):\n    n = len(votes)\n    state = []\n    since = []\n    until = []\n    noted = []\n    for v in votes:\n        state = state + [\"idle\"]\n        since = since + [0 - 1]\n        until = until + [0 - 1]\n        noted = noted + [\"\"]\n    events = []\n    labels = []\n    phase = \"prepare\"\n    nxt = 0\n    decision = \"\"\n    logged = False\n    told = []\n    tell_at = 0\n    done = False\n    steps = 0\n    down = False\n    crashed_once = False\n    up_at = 0 - 1\n    if crash == 0:\n        down = True\n        crashed_once = True\n        events = events + [[0, \"the coordinator crashes before its first step\"]]\n        if back > 0:\n            up_at = back + 1\n    for t in range(1, horizon+1):\n        if down and up_at == t:\n            down = False\n            phase = \"tell\"\n            tell_at = 0\n            if not logged:\n                events = events + [[t, \"the coordinator is back; with no decision logged, it decides abort\"]]\n                decision = \"abort\"\n                logged = True\n                steps = steps + 1\n                labels = labels + [\"decide abort on its return\"]\n            else:\n                events = events + [[t, \"the coordinator is back with \" + decision + \" logged, and tells everyone again\"]]\n        elif not down and not done:\n            steps = steps + 1\n            if phase == \"prepare\":\n                i = nxt\n                nxt = nxt + 1\n                labels = labels + [\"prepare \" + name(i)]\n                if state[i] == \"idle\" and votes[i]:\n                    state[i] = \"prepared\"\n                    since[i] = t\n                    told = told + [i]\n                    events = events + [[t, \"prepare \" + name(i) + \": it votes yes and locks its data\"]]\n                    if nxt == n:\n                        phase = \"decide\"\n                else:\n                    if state[i] == \"idle\":\n                        state[i] = \"aborted\"\n                        until[i] = t\n                    events = events + [[t, \"prepare \" + name(i) + \": it votes no\"]]\n                    phase = \"decide\"\n                    decision = \"abort\"\n            elif phase == \"decide\":\n                if decision == \"\":\n                    decision = \"commit\"\n                logged = True\n                labels = labels + [\"log \" + decision]\n                events = events + [[t, \"the coordinator logs \" + decision]]\n                phase = \"tell\"\n                tell_at = 0\n            else:\n                i = told[tell_at]\n                tell_at = tell_at + 1\n                labels = labels + [\"tell \" + name(i)]\n                if state[i] == \"prepared\":\n                    state[i] = past(decision)\n                    until[i] = t\n                    events = events + [[t, \"tell \" + name(i) + \" \" + decision + \": it \" + verb(decision) + \" and unlocks\"]]\n                elif state[i] == past(decision):\n                    events = events + [[t, \"tell \" + name(i) + \" \" + decision + \": it had already \" + past(decision)]]\n                else:\n                    events = events + [[t, \"tell \" + name(i) + \" \" + decision + \": but it had already \" + state[i]]]\n            if crash > 0 and steps == crash and not crashed_once:\n                down = True\n                crashed_once = True\n                events = events + [[t, \"the coordinator crashes\"]]\n                if back > 0:\n                    up_at = t + back + 1\n        if not down and phase == \"tell\" and tell_at >= len(told):\n            done = True\n        known = \"\"\n        if logged:\n            known = decision\n        for i in range(0, n):\n            if state[i] == \"prepared\" and t - since[i] >= timeout:\n                r = learn(state, votes, i, strategy, down, known, n)\n                if r[0] != \"\":\n                    state[i] = r[0]\n                    until[i] = t\n                    if r[1] >= 0:\n                        state[r[1]] = \"aborted\"\n                        until[r[1]] = t\n                        events = events + [[t, name(r[1]) + \", never asked, aborts on its own\"]]\n                    events = events + [[t, name(i) + \" in doubt: \" + r[2]]]\n                elif noted[i] != r[2]:\n                    noted[i] = r[2]\n                    events = events + [[t, name(i) + \" in doubt: \" + r[2]]]\n    locked = []\n    still = []\n    for i in range(0, n):\n        if since[i] < 0:\n            locked = locked + [0]\n            still = still + [False]\n        elif until[i] < 0:\n            locked = locked + [horizon - since[i]]\n            still = still + [True]\n        else:\n            locked = locked + [until[i] - since[i]]\n            still = still + [False]\n    final = \"\"\n    if logged:\n        final = decision\n    return [events, state, locked, still, final, steps, until, labels, since]\n\ndef verb(d):\n    if d == \"commit\":\n        return \"commits\"\n    return \"aborts\"\n\ndef past(d):\n    if d == \"commit\":\n        return \"committed\"\n    return \"aborted\"\n\ndef learn(state, votes, i, strategy, down, decision, n):\n    if strategy == \"presume abort\":\n        return [\"aborted\", 0 - 1, \"presumes abort and unlocks\"]\n    if strategy == \"presume commit\":\n        return [\"committed\", 0 - 1, \"presumes commit and unlocks\"]\n    if strategy == \"wait\":\n        return [\"\", 0 - 1, \"waits, its data locked\"]\n    if not down:\n        if decision != \"\":\n            return [past(decision), 0 - 1, \"asks the coordinator: \" + decision]\n        return [\"\", 0 - 1, \"asks the coordinator: no decision yet, so it waits\"]\n    for j in range(0, n):\n        if j != i and (state[j] == \"committed\" or state[j] == \"aborted\"):\n            return [state[j], 0 - 1, \"asks \" + name(j) + \", which has \" + state[j] + \", and \" + verb(present(state[j])) + \" too\"]\n    for j in range(0, n):\n        if j != i and state[j] == \"idle\":\n            return [\"aborted\", j, \"asks \" + name(j) + \", and aborts too\"]\n    return [\"\", 0 - 1, \"asks the others, all in doubt too, so it waits, its data locked\"]\n\ndef present(s):\n    if s == \"committed\":\n        return \"commit\"\n    return \"abort\"\n\ndef outcome(r):\n    c = False\n    a = False\n    doubt = False\n    against = False\n    no_yes = False\n    for i in range(0, len(r[1])):\n        if r[8][i] < 0:\n            no_yes = True\n        s = r[1][i]\n        if s == \"committed\":\n            c = True\n        if s == \"aborted\":\n            a = True\n        if s == \"prepared\":\n            doubt = True\n        if r[4] != \"\" and (s == \"committed\" or s == \"aborted\") and s != past(r[4]):\n            against = True\n    return [c and a, against, doubt, c and no_yes]\n\ndef wrong(o):\n    return o[0] or o[1] or o[3]\n\ndef letters(r):\n    out = \"\"\n    for s in r[1]:\n        if out != \"\":\n            out = out + \" \"\n        if s == \"committed\":\n            out = out + \"c\"\n        elif s == \"aborted\":\n            out = out + \"a\"\n        elif s == \"prepared\":\n            out = out + \"?\"\n        else:\n            out = out + \"-\"\n    return out\n\ndef crash_points(votes, timeout, back, horizon):\n    return run(votes, \"wait\", timeout, back, 0 - 1, horizon)[5]\n\ndef patterns(n):\n    out = [[]]\n    for k in range(1, n+1):\n        more = []\n        for p in out:\n            more = more + [p + [True]]\n            more = more + [p + [False]]\n        out = more\n    return out\n\ndef census(n, strategy, timeout, back, horizon):\n    runs = 0\n    bad = 0\n    doubt = 0\n    most = 0\n    for votes in patterns(n):\n        for c in range(0 - 1, crash_points(votes, timeout, back, horizon)+1):\n            r = run(votes, strategy, timeout, back, c, horizon)\n            o = outcome(r)\n            runs = runs + 1\n            if wrong(o):\n                bad = bad + 1\n            if o[2]:\n                doubt = doubt + 1\n            for x in r[2]:\n                if x > most:\n                    most = x\n    return [runs, bad, doubt, most]\n"
    }
  ],
  "sessions": [
    {
      "name": "bad-input",
      "input": "0\nx\n2\n\n2\nfast\n2\nwait\n\n2\nwait\n8\n2\nwait\nx\n2\nwait\n-1\n4\n\n4\nyes\n4\nyes yes yes yes yes\n4\nyes maybe\n4\nYES yes\n5\n\n5\n0\n5\n10\n5\nx\n5\n5\n\n5\n5\n0\n5\n5\n31\n5\n5\nsometime\n6\n",
      "screen": "== Two-phase commit ==\nA coordinator asks every participant to prepare, logs commit only if all vote yes, then tells each one. A participant that voted yes waits in doubt, its data locked, until it hears.\n\n1) the transaction  2) one run  3) every crash point  4) votes  5) timing  6) quit\nchoice> 0\nPick a number from 1 to 6.\n\n1) the transaction  2) one run  3) every crash point  4) votes  5) timing  6) quit\nchoice> x\nPick a number from 1 to 6.\n\n1) the transaction  2) one run  3) every crash point  4) votes  5) timing  6) quit\nchoice> 2\nstrategy (wait, ask peers, presume abort or presume commit)> \nCancelled.\n\n1) the transaction  2) one run  3) every crash point  4) votes  5) timing  6) quit\nchoice> 2\nstrategy (wait, ask peers, presume abort or presume commit)> fast\nType wait, ask peers, presume abort or presume commit.\n\n1) the transaction  2) one run  3) every crash point  4) votes  5) timing  6) quit\nchoice> 2\nstrategy (wait, ask peers, presume abort or presume commit)> wait\ncrash after step (0 to 7, or none)> \nCancelled.\n\n1) the transaction  2) one run  3) every crash point  4) votes  5) timing  6) quit\nchoice> 2\nstrategy (wait, ask peers, presume abort or presume commit)> wait\ncrash after step (0 to 7, or none)> 8\nType a step from 0 to 7, or none.\n\n1) the transaction  2) one run  3) every crash point  4) votes  5) timing  6) quit\nchoice> 2\nstrategy (wait, ask peers, presume abort or presume commit)> wait\ncrash after step (0 to 7, or none)> x\nType a step from 0 to 7, or none.\n\n1) the transaction  2) one run  3) every crash point  4) votes  5) timing  6) quit\nchoice> 2\nstrategy (wait, ask peers, presume abort or presume commit)> wait\ncrash after step (0 to 7, or none)> -1\nType a step from 0 to 7, or none.\n\n1) the transaction  2) one run  3) every crash point  4) votes  5) timing  6) quit\nchoice> 4\nvotes (2 to 4 of yes or no, such as yes yes no)> \nCancelled.\n\n1) the transaction  2) one run  3) every crash point  4) votes  5) timing  6) quit\nchoice> 4\nvotes (2 to 4 of yes or no, such as yes yes no)> yes\nType 2 to 4 votes, each yes or no.\n\n1) the transaction  2) one run  3) every crash point  4) votes  5) timing  6) quit\nchoice> 4\nvotes (2 to 4 of yes or no, such as yes yes no)> yes yes yes yes yes\nType 2 to 4 votes, each yes or no.\n\n1) the transaction  2) one run  3) every crash point  4) votes  5) timing  6) quit\nchoice> 4\nvotes (2 to 4 of yes or no, such as yes yes no)> yes maybe\nType 2 to 4 votes, each yes or no.\n\n1) the transaction  2) one run  3) every crash point  4) votes  5) timing  6) quit\nchoice> 4\nvotes (2 to 4 of yes or no, such as yes yes no)> YES yes\nType 2 to 4 votes, each yes or no.\n\n1) the transaction  2) one run  3) every crash point  4) votes  5) timing  6) quit\nchoice> 5\nin doubt for how long before acting (1 to 9 ticks)> \nCancelled.\n\n1) the transaction  2) one run  3) every crash point  4) votes  5) timing  6) quit\nchoice> 5\nin doubt for how long before acting (1 to 9 ticks)> 0\nType a whole number from 1 to 9.\n\n1) the transaction  2) one run  3) every crash point  4) votes  5) timing  6) quit\nchoice> 5\nin doubt for how long before acting (1 to 9 ticks)> 10\nType a whole number from 1 to 9.\n\n1) the transaction  2) one run  3) every crash point  4) votes  5) timing  6) quit\nchoice> 5\nin doubt for how long before acting (1 to 9 ticks)> x\nType a whole number from 1 to 9.\n\n1) the transaction  2) one run  3) every crash point  4) votes  5) timing  6) quit\nchoice> 5\nin doubt for how long before acting (1 to 9 ticks)> 5\ncoordinator back after (1 to 30 ticks, or never)> \nCancelled.\n\n1) the transaction  2) one run  3) every crash point  4) votes  5) timing  6) quit\nchoice> 5\nin doubt for how long before acting (1 to 9 ticks)> 5\ncoordinator back after (1 to 30 ticks, or never)> 0\nType a whole number from 1 to 30, or never.\n\n1) the transaction  2) one run  3) every crash point  4) votes  5) timing  6) quit\nchoice> 5\nin doubt for how long before acting (1 to 9 ticks)> 5\ncoordinator back after (1 to 30 ticks, or never)> 31\nType a whole number from 1 to 30, or never.\n\n1) the transaction  2) one run  3) every crash point  4) votes  5) timing  6) quit\nchoice> 5\nin doubt for how long before acting (1 to 9 ticks)> 5\ncoordinator back after (1 to 30 ticks, or never)> sometime\nType a whole number from 1 to 30, or never.\n\n1) the transaction  2) one run  3) every crash point  4) votes  5) timing  6) quit\nchoice> 6\nBye.\n",
      "interpreter": "equal"
    },
    {
      "name": "basic",
      "input": "1\n3\n2\nwait\n3\n2\npresume abort\n4\n2\nask peers\n2\n2\nask peers\n4\n5\n5\nnever\n3\n2\nask peers\n3\n4\nyes no yes yes\n2\npresume commit\n1\n3\n4\nyes yes yes\n5\n3\n10\n3\n2\npresume abort\nnone\n6\n",
      "screen": "== Two-phase commit ==\nA coordinator asks every participant to prepare, logs commit only if all vote yes, then tells each one. A participant that voted yes waits in doubt, its data locked, until it hears.\n\n1) the transaction  2) one run  3) every crash point  4) votes  5) timing  6) quit\nchoice> 1\nParticipants P1, P2 and P3 vote yes, yes and yes.\nWith no crash, the coordinator takes 7 steps, one a tick: 1 prepare P1, 2 prepare P2, 3 prepare P3, 4 log commit, 5 tell P1, 6 tell P2, 7 tell P3.\nA participant in doubt for 5 ticks acts on its strategy; a crashed coordinator is back after 10 ticks.\n\n1) the transaction  2) one run  3) every crash point  4) votes  5) timing  6) quit\nchoice> 3\nEvery crash point, votes yes, yes and yes; a participant in doubt acts after 5 ticks; the coordinator is back after 10 ticks:\n  crash after                 wait       ask peers  presume abort  presume commit\n  none                        c c c      c c c      c c c          c c c\n  0 before any step           - - -      - - -      - - -          - - -\n  1 prepare P1                a - -      a a -      a - -          c - - !\n  2 prepare P2                a a -      a a a      a a -          c c - !\n  3 prepare P3                a a a      a a a      a a a          c c c !\n  4 log commit                c c c      c c c      a a a !        c c c\n  5 tell P1                   c c c      c c c      c a a !        c c c\n  6 tell P2                   c c c      c c c      c c a !        c c c\n  7 tell P3                   c c c      c c c      c c c          c c c\n  c committed, a aborted, ? still in doubt at tick 40, - never asked; ! wrong: split, against the coordinator, or committed without every yes.\n  wait:           wrong at 0 of 9 crash points, still in doubt at 0, data locked at most 17 ticks.\n  ask peers:      wrong at 0 of 9 crash points, still in doubt at 0, data locked at most 14 ticks.\n  presume abort:  wrong at 3 of 9 crash points, still in doubt at 0, data locked at most 5 ticks.\n  presume commit: wrong at 3 of 9 crash points, still in doubt at 0, data locked at most 5 ticks.\nEvery pattern of 3 votes, each with every crash point and none:\n  strategy          runs  wrong  in doubt at the end  most ticks locked\n  wait                45      0                    0                 17\n  ask peers           45      0                    0                 14\n  presume abort       45      3                    0                  5\n  presume commit      45     14                    0                  5\n\n1) the transaction  2) one run  3) every crash point  4) votes  5) timing  6) quit\nchoice> 2\nstrategy (wait, ask peers, presume abort or presume commit)> wait\ncrash after step (0 to 7, or none)> 3\nwait, the coordinator crashing after step 3 (it is back after 10 ticks):\n  tick  1  prepare P1: it votes yes and locks its data\n  tick  2  prepare P2: it votes yes and locks its data\n  tick  3  prepare P3: it votes yes and locks its data\n  tick  3  the coordinator crashes\n  tick  6  P1 in doubt: waits, its data locked\n  tick  7  P2 in doubt: waits, its data locked\n  tick  8  P3 in doubt: waits, its data locked\n  tick 14  the coordinator is back; with no decision logged, it decides abort\n  tick 15  tell P1 abort: it aborts and unlocks\n  tick 16  tell P2 abort: it aborts and unlocks\n  tick 17  tell P3 abort: it aborts and unlocks\n  P1 aborted at tick 15, its data locked for 14 ticks.\n  P2 aborted at tick 16, its data locked for 14 ticks.\n  P3 aborted at tick 17, its data locked for 14 ticks.\nAtomic: everyone who finished aborted, as the coordinator logged.\n\n1) the transaction  2) one run  3) every crash point  4) votes  5) timing  6) quit\nchoice> 2\nstrategy (wait, ask peers, presume abort or presume commit)> presume abort\ncrash after step (0 to 7, or none)> 4\npresume abort, the coordinator crashing after step 4 (it is back after 10 ticks):\n  tick  1  prepare P1: it votes yes and locks its data\n  tick  2  prepare P2: it votes yes and locks its data\n  tick  3  prepare P3: it votes yes and locks its data\n  tick  4  the coordinator logs commit\n  tick  4  the coordinator crashes\n  tick  6  P1 in doubt: presumes abort and unlocks\n  tick  7  P2 in doubt: presumes abort and unlocks\n  tick  8  P3 in doubt: presumes abort and unlocks\n  tick 15  the coordinator is back with commit logged, and tells everyone again\n  tick 16  tell P1 commit: but it had already aborted\n  tick 17  tell P2 commit: but it had already aborted\n  tick 18  tell P3 commit: but it had already aborted\n  P1 aborted at tick 6, its data locked for 5 ticks.\n  P2 aborted at tick 7, its data locked for 5 ticks.\n  P3 aborted at tick 8, its data locked for 5 ticks.\nAgainst the coordinator, which logged commit: P1, P2 and P3 aborted.\n\n1) the transaction  2) one run  3) every crash point  4) votes  5) timing  6) quit\nchoice> 2\nstrategy (wait, ask peers, presume abort or presume commit)> ask peers\ncrash after step (0 to 7, or none)> 2\nask peers, the coordinator crashing after step 2 (it is back after 10 ticks):\n  tick  1  prepare P1: it votes yes and locks its data\n  tick  2  prepare P2: it votes yes and locks its data\n  tick  2  the coordinator crashes\n  tick  6  P3, never asked, aborts on its own\n  tick  6  P1 in doubt: asks P3, and aborts too\n  tick  7  P2 in doubt: asks P1, which has aborted, and aborts too\n  tick 13  the coordinator is back; with no decision logged, it decides abort\n  tick 14  tell P1 abort: it had already aborted\n  tick 15  tell P2 abort: it had already aborted\n  P1 aborted at tick 6, its data locked for 5 ticks.\n  P2 aborted at tick 7, its data locked for 5 ticks.\n  P3 aborted at tick 6, never locked.\nAtomic: everyone who finished aborted, as the coordinator logged.\n\n1) the transaction  2) one run  3) every crash point  4) votes  5) timing  6) quit\nchoice> 2\nstrategy (wait, ask peers, presume abort or presume commit)> ask peers\ncrash after step (0 to 7, or none)> 4\nask peers, the coordinator crashing after step 4 (it is back after 10 ticks):\n  tick  1  prepare P1: it votes yes and locks its data\n  tick  2  prepare P2: it votes yes and locks its data\n  tick  3  prepare P3: it votes yes and locks its data\n  tick  4  the coordinator logs commit\n  tick  4  the coordinator crashes\n  tick  6  P1 in doubt: asks the others, all in doubt too, so it waits, its data locked\n  tick  7  P2 in doubt: asks the others, all in doubt too, so it waits, its data locked\n  tick  8  P3 in doubt: asks the others, all in doubt too, so it waits, its data locked\n  tick 15  the coordinator is back with commit logged, and tells everyone again\n  tick 15  P1 in doubt: asks the coordinator: commit\n  tick 15  P2 in doubt: asks the coordinator: commit\n  tick 15  P3 in doubt: asks the coordinator: commit\n  tick 16  tell P1 commit: it had already committed\n  tick 17  tell P2 commit: it had already committed\n  tick 18  tell P3 commit: it had already committed\n  P1 committed at tick 15, its data locked for 14 ticks.\n  P2 committed at tick 15, its data locked for 13 ticks.\n  P3 committed at tick 15, its data locked for 12 ticks.\nAtomic: everyone who finished committed, as the coordinator logged.\n\n1) the transaction  2) one run  3) every crash point  4) votes  5) timing  6) quit\nchoice> 5\nin doubt for how long before acting (1 to 9 ticks)> 5\ncoordinator back after (1 to 30 ticks, or never)> never\nParticipants P1, P2 and P3 vote yes, yes and yes.\nWith no crash, the coordinator takes 7 steps, one a tick: 1 prepare P1, 2 prepare P2, 3 prepare P3, 4 log commit, 5 tell P1, 6 tell P2, 7 tell P3.\nA participant in doubt for 5 ticks acts on its strategy; a crashed coordinator never comes back.\n\n1) the transaction  2) one run  3) every crash point  4) votes  5) timing  6) quit\nchoice> 3\nEvery crash point, votes yes, yes and yes; a participant in doubt acts after 5 ticks; the coordinator never comes back:\n  crash after                 wait       ask peers  presume abort  presume commit\n  none                        c c c      c c c      c c c          c c c\n  0 before any step           - - -      - - -      - - -          - - -\n  1 prepare P1                ? - -      a a -      a - -          c - - !\n  2 prepare P2                ? ? -      a a a      a a -          c c - !\n  3 prepare P3                ? ? ?      ? ? ?      a a a          c c c\n  4 log commit                ? ? ?      ? ? ?      a a a !        c c c\n  5 tell P1                   c ? ?      c c c      c a a !        c c c\n  6 tell P2                   c c ?      c c c      c c a !        c c c\n  7 tell P3                   c c c      c c c      c c c          c c c\n  c committed, a aborted, ? still in doubt at tick 40, - never asked; ! wrong: split, against the coordinator, or committed without every yes.\n  wait:           wrong at 0 of 9 crash points, still in doubt at 6, data locked at most 39 ticks.\n  ask peers:      wrong at 0 of 9 crash points, still in doubt at 2, data locked at most 39 ticks.\n  presume abort:  wrong at 3 of 9 crash points, still in doubt at 0, data locked at most 5 ticks.\n  presume commit: wrong at 2 of 9 crash points, still in doubt at 0, data locked at most 5 ticks.\nAsk peers is still in doubt only at crash points 3 and 4: every participant had voted yes, and none had heard the decision.\nEvery pattern of 3 votes, each with every crash point and none:\n  strategy          runs  wrong  in doubt at the end  most ticks locked\n  wait                45      0                   17                 39\n  ask peers           45      0                    2                 39\n  presume abort       45      3                    0                  5\n  presume commit      45     13                    0                  5\n\n1) the transaction  2) one run  3) every crash point  4) votes  5) timing  6) quit\nchoice> 2\nstrategy (wait, ask peers, presume abort or presume commit)> ask peers\ncrash after step (0 to 7, or none)> 3\nask peers, the coordinator crashing after step 3 (it never comes back):\n  tick  1  prepare P1: it votes yes and locks its data\n  tick  2  prepare P2: it votes yes and locks its data\n  tick  3  prepare P3: it votes yes and locks its data\n  tick  3  the coordinator crashes\n  tick  6  P1 in doubt: asks the others, all in doubt too, so it waits, its data locked\n  tick  7  P2 in doubt: asks the others, all in doubt too, so it waits, its data locked\n  tick  8  P3 in doubt: asks the others, all in doubt too, so it waits, its data locked\n  P1 still in doubt at tick 40, its data locked for 39 ticks.\n  P2 still in doubt at tick 40, its data locked for 38 ticks.\n  P3 still in doubt at tick 40, its data locked for 37 ticks.\nStill in doubt at tick 40, data locked: P1, P2 and P3.\n\n1) the transaction  2) one run  3) every crash point  4) votes  5) timing  6) quit\nchoice> 4\nvotes (2 to 4 of yes or no, such as yes yes no)> yes no yes yes\nParticipants P1, P2, P3 and P4 vote yes, no, yes and yes.\nWith no crash, the coordinator takes 4 steps, one a tick: 1 prepare P1, 2 prepare P2, 3 log abort, 4 tell P1.\nA participant in doubt for 5 ticks acts on its strategy; a crashed coordinator never comes back.\n\n1) the transaction  2) one run  3) every crash point  4) votes  5) timing  6) quit\nchoice> 2\nstrategy (wait, ask peers, presume abort or presume commit)> presume commit\ncrash after step (0 to 4, or none)> 1\npresume commit, the coordinator crashing after step 1 (it never comes back):\n  tick  1  prepare P1: it votes yes and locks its data\n  tick  1  the coordinator crashes\n  tick  6  P1 in doubt: presumes commit and unlocks\n  P1 committed at tick 6, its data locked for 5 ticks.\n  P2 was never asked.\n  P3 was never asked.\n  P4 was never asked.\nCommitted without every yes: P1 committed, though P2, P3 and P4 never voted yes.\n\n1) the transaction  2) one run  3) every crash point  4) votes  5) timing  6) quit\nchoice> 3\nEvery crash point, votes yes, no, yes and yes; a participant in doubt acts after 5 ticks; the coordinator never comes back:\n  crash after                 wait       ask peers  presume abort  presume commit\n  none                        a a - -    a a - -    a a - -        a a - -\n  0 before any step           - - - -    - - - -    - - - -        - - - -\n  1 prepare P1                ? - - -    a a - -    a - - -        c - - - !\n  2 prepare P2                ? a - -    a a - -    a a - -        c a - - !\n  3 log abort                 ? a - -    a a - -    a a - -        c a - - !\n  4 tell P1                   a a - -    a a - -    a a - -        a a - -\n  c committed, a aborted, ? still in doubt at tick 40, - never asked; ! wrong: split, against the coordinator, or committed without every yes.\n  wait:           wrong at 0 of 6 crash points, still in doubt at 3, data locked at most 39 ticks.\n  ask peers:      wrong at 0 of 6 crash points, still in doubt at 0, data locked at most 5 ticks.\n  presume abort:  wrong at 0 of 6 crash points, still in doubt at 0, data locked at most 5 ticks.\n  presume commit: wrong at 3 of 6 crash points, still in doubt at 0, data locked at most 5 ticks.\nEvery pattern of 4 votes, each with every crash point and none:\n  strategy          runs  wrong  in doubt at the end  most ticks locked\n  wait                93      0                   37                 39\n  ask peers           93      0                    2                 39\n  presume abort       93      4                    0                  5\n  presume commit      93     32                    0                  5\n\n1) the transaction  2) one run  3) every crash point  4) votes  5) timing  6) quit\nchoice> 4\nvotes (2 to 4 of yes or no, such as yes yes no)> yes yes yes\nParticipants P1, P2 and P3 vote yes, yes and yes.\nWith no crash, the coordinator takes 7 steps, one a tick: 1 prepare P1, 2 prepare P2, 3 prepare P3, 4 log commit, 5 tell P1, 6 tell P2, 7 tell P3.\nA participant in doubt for 5 ticks acts on its strategy; a crashed coordinator never comes back.\n\n1) the transaction  2) one run  3) every crash point  4) votes  5) timing  6) quit\nchoice> 5\nin doubt for how long before acting (1 to 9 ticks)> 3\ncoordinator back after (1 to 30 ticks, or never)> 10\nParticipants P1, P2 and P3 vote yes, yes and yes.\nWith no crash, the coordinator takes 7 steps, one a tick: 1 prepare P1, 2 prepare P2, 3 prepare P3, 4 log commit, 5 tell P1, 6 tell P2, 7 tell P3.\nA participant in doubt for 3 ticks acts on its strategy; a crashed coordinator is back after 10 ticks.\n\n1) the transaction  2) one run  3) every crash point  4) votes  5) timing  6) quit\nchoice> 3\nEvery crash point, votes yes, yes and yes; a participant in doubt acts after 3 ticks; the coordinator is back after 10 ticks:\n  crash after                 wait       ask peers  presume abort  presume commit\n  none                        c c c      c c c      a a a !        c c c\n  0 before any step           - - -      - - -      - - -          - - -\n  1 prepare P1                a - -      a a -      a - -          c - - !\n  2 prepare P2                a a -      a a a      a a -          c c - !\n  3 prepare P3                a a a      a a a      a a a          c c c !\n  4 log commit                c c c      c c c      a a a !        c c c\n  5 tell P1                   c c c      c c c      a a a !        c c c\n  6 tell P2                   c c c      c c c      a a a !        c c c\n  7 tell P3                   c c c      c c c      a a a !        c c c\n  c committed, a aborted, ? still in doubt at tick 40, - never asked; ! wrong: split, against the coordinator, or committed without every yes.\n  wait:           wrong at 0 of 9 crash points, still in doubt at 0, data locked at most 17 ticks.\n  ask peers:      wrong at 0 of 9 crash points, still in doubt at 0, data locked at most 14 ticks.\n  presume abort:  wrong at 5 of 9 crash points, still in doubt at 0, data locked at most 3 ticks.\n  presume commit: wrong at 3 of 9 crash points, still in doubt at 0, data locked at most 3 ticks.\nEvery pattern of 3 votes, each with every crash point and none:\n  strategy          runs  wrong  in doubt at the end  most ticks locked\n  wait                45      0                    0                 17\n  ask peers           45      0                    0                 14\n  presume abort       45      5                    0                  3\n  presume commit      45     16                    0                  3\n\n1) the transaction  2) one run  3) every crash point  4) votes  5) timing  6) quit\nchoice> 2\nstrategy (wait, ask peers, presume abort or presume commit)> presume abort\ncrash after step (0 to 7, or none)> none\npresume abort, no crash:\n  tick  1  prepare P1: it votes yes and locks its data\n  tick  2  prepare P2: it votes yes and locks its data\n  tick  3  prepare P3: it votes yes and locks its data\n  tick  4  the coordinator logs commit\n  tick  4  P1 in doubt: presumes abort and unlocks\n  tick  5  tell P1 commit: but it had already aborted\n  tick  5  P2 in doubt: presumes abort and unlocks\n  tick  6  tell P2 commit: but it had already aborted\n  tick  6  P3 in doubt: presumes abort and unlocks\n  tick  7  tell P3 commit: but it had already aborted\n  P1 aborted at tick 4, its data locked for 3 ticks.\n  P2 aborted at tick 5, its data locked for 3 ticks.\n  P3 aborted at tick 6, its data locked for 3 ticks.\nAgainst the coordinator, which logged commit: P1, P2 and P3 aborted.\n\n1) the transaction  2) one run  3) every crash point  4) votes  5) timing  6) quit\nchoice> 6\nBye.\n",
      "interpreter": "equal"
    }
  ],
  "builtOn": [
    {
      "slug": "two-phase-commit-window",
      "caseId": "259-two-phase-commit-window",
      "title": "Two-Phase Commit — the in-doubt window"
    }
  ],
  "updated": "2026-10-16"
}
