{
  "id": "P128",
  "slug": "connection-pool",
  "key": "P128-connection-pool",
  "title": "Connection pool",
  "summary": "Jobs leaving their bodies at the end, by break, by return or by raise, giving their connections back by hand or by a with block; and when users borrowing one connection after another can leave the whole pool held.",
  "entry": "main.eml",
  "ui": "terminal",
  "readme": "# P128 - Connection pool\n\nA fixed number of connections, borrowed by jobs and given back - or not.\nJobs leave their bodies in four ways, and users borrowing one connection\nafter another can leave the whole pool held and nobody able to finish.\n\n- `main.eml` - the menu, jobs under both ways of giving back, the\n  deadlock table, and one case\n- `pool.eml` - the pool and its with-block lease, the jobs, and the search\n  over every order of borrowing\n\nHow each part works:\n\n- As in the corpus case `connection-pool-release`, a body can leave at its\n  end, by break, by return or by raise. Given back by hand at the end of\n  the body, the connection is kept on the other three ways out, and the\n  pool runs dry: later jobs find none free and would wait forever. Given\n  back by a with block, it comes back on all four.\n- As in the corpus case `pool-reentrancy-deadlock`, a user that needs\n  several connections borrows them one at a time and holds each until it\n  has them all. Some order of borrowing leaves every connection held by\n  users that each need one more exactly when users × (need - 1) is at\n  least the pool: 4 users needing 2 each deadlock a pool of 4, 3 do not.\n  Needing one at a time never deadlocks. One user needing more than the\n  pool holds can never finish.\n- Every reachable state of who holds how many is searched, users with\n  the same count taken as one.\n\nWhat is checked: menu choices 1 to 4; a pool from 1 to 6 for jobs and 1\nto 8 for a case; up to 12 jobs, each end, break, return or raise; a need\nfrom 1 to 4 and 1 to 8 users. An empty answer cancels.\n\nSessions: `sessions/basic.in` gives:\n- end, break, return, raise, end and end on a pool of 3: by hand the\n  pool is empty after the fourth job;\n- the deadlock table;\n- a pool of 4 with 4 users needing 2 (a deadlock) and 3 users (none).\n\n`sessions/bad-input.in` gives:\n- menu choices 0 and x;\n- jobs: a cancel, a pool of 7, then a pool of 3 with a cancel, loop and 13\n  jobs, and three ends on a pool of 2;\n- cases: a cancel, a pool of 9, a need of 5, 9 users, and 8 users needing\n  1 from a pool of 1.\n\nBuilt on the verified corpus cases `connection-pool-release` (a with block\nreleases on every way out) and `pool-reentrancy-deadlock` (holding one\nconnection while borrowing the next).\n",
  "modules": [
    {
      "name": "main.eml",
      "eml": "# P128 connection pool: jobs leaving in four ways under give-back by hand\n# and by a with block, and when borrowing a second connection deadlocks.\nimport pool\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 ask_int(prompt, low, high):\n    plain(input(prompt)) => t\n    if t == \"\":\n        \"Cancelled.\" ^0\n        return 0 - 1\n    if len(t) > 2:\n        (\"Type a whole number from \" + str(low) + \" to \" + str(high) + \".\") ^0\n        return 0 - 1\n    for c in t:\n        if c < \"0\" or c > \"9\":\n            (\"Type a whole number from \" + str(low) + \" to \" + str(high) + \".\") ^0\n            return 0 - 1\n    if int(t) < low or int(t) > high:\n        (\"Type a whole number from \" + str(low) + \" to \" + str(high) + \".\") ^0\n        return 0 - 1\n    return int(t)\n\ndef jobs():\n    ask_int(\"pool size (1 to 6)> \", 1, 6) => size\n    if size < 0:\n        return\n    \"Each job leaves its body at the end, by break, by return or by raise.\" ^0\n    plain(input(\"jobs (such as end break return raise end, up to 12)> \")) => t\n    if t == \"\":\n        \"Cancelled.\" ^0\n        return\n    [] => js\n    \"\" => w\n    for c in t + \" \":\n        if c == \" \":\n            if w != \"\":\n                if w != \"end\" and w != \"break\" and w != \"return\" and w != \"raise\":\n                    \"Each job is end, break, return or raise.\" ^0\n                    return\n                js + [w] => js\n            \"\" => w\n        else:\n            w + c => w\n    if len(js) > 12:\n        \"Up to 12 jobs.\" ^0\n        return\n    pool.run_jobs(size, js, \"by hand\") => a\n    pool.run_jobs(size, js, \"with\") => b\n    (\"Pool of \" + str(size) + \"; connections out after each job:\") ^0\n    \"  job  leaves   given back by hand              with a with block\" ^0\n    for k in [0:len(js) - 1]:\n        (\"  \" + lpad(str(k + 1), 3) + \"  \" + pad(js[k], 7) + \"  \" + pad(cell(a[k]), 30) + \"  \" + cell(b[k])) ^0\n    0 => stuck\n    for r in a:\n        if not r[2]:\n            stuck + 1 => stuck\n    a[len(a) - 1][1] => left\n    if left == 0:\n        \"By hand: every connection given back.\" ^0\n    else:\n        (\"By hand: \" + str(left) + \" of \" + str(size) + \" never given back, by the jobs that left early.\") ^0\n    if stuck > 0:\n        (\"  \" + str(stuck) + \" later jobs found no connection free.\") ^0\n    \"With a with block: every connection given back, however the body left.\" ^0\n\ndef cell(r):\n    if not r[2]:\n        return \"none free: waits forever\"\n    return str(r[1]) + \" out\"\n\ndef table():\n    \"Users borrowing one connection after another, holding each until they have all they need:\" ^0\n    \"the fewest users for which some order of borrowing deadlocks (- for none up to 12).\" ^0\n    \"  needs   pool 2  pool 3  pool 4  pool 5  pool 6\" ^0\n    True => formula\n    for need in [1:4]:\n        \"  \" + pad(str(need), 6) => line\n        for size in [2:6]:\n            pool.first_deadlock(size, need, 12) => c\n            if c == 0:\n                line + lpad(\"-\", 8) => line\n                if need > 1:\n                    False => formula\n            else:\n                line + lpad(str(c), 8) => line\n                if need == 1 or c * (need - 1) < size or (c - 1) * (need - 1) >= size:\n                    False => formula\n        line ^0\n    if formula:\n        \"Every count is the smallest number of users that, holding need - 1 each, can hold the whole pool; one connection at a time never deadlocks.\" ^0\n\ndef one():\n    ask_int(\"pool size (1 to 8)> \", 1, 8) => size\n    if size < 0:\n        return\n    ask_int(\"connections each user needs (1 to 4)> \", 1, 4) => need\n    if need < 0:\n        return\n    ask_int(\"users (1 to 8)> \", 1, 8) => users\n    if users < 0:\n        return\n    pool.deadlock_possible(size, need, users) => r\n    if not r[0]:\n        (\"No order of borrowing deadlocks: \" + str(users) + \" users needing \" + str(need) + \" each, pool of \" + str(size) + \".\") ^0\n        return\n    \"\" => held\n    for h in r[1]:\n        if held != \"\":\n            held + \", \" => held\n        held + str(h) => held\n    (\"Deadlock: every connection held, the waiting users holding \" + held + \" of the \" + str(need) + \" each needs, and none can borrow another.\") ^0\n\n\"== Connection pool ==\" ^0\n\"A fixed number of connections, borrowed by jobs and given back - or not.\" ^0\nTrue => running\nwhile running:\n    \"\" ^0\n    \"1) jobs  2) deadlock table  3) one deadlock  4) quit\" ^0\n    plain(input(\"choice> \")) => choice\n    if choice == \"1\":\n        jobs()\n    elif choice == \"2\":\n        table()\n    elif choice == \"3\":\n        one()\n    elif choice == \"4\":\n        False => running\n    else:\n        \"Pick a number from 1 to 4.\" ^0\n\"Bye.\" ^0\n",
      "python": "import pool\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 ask_int(prompt, low, high):\n    t = plain(input(prompt))\n    if t == \"\":\n        print(\"Cancelled.\")\n        return 0 - 1\n    if len(t) > 2:\n        print(\"Type a whole number from \" + str(low) + \" to \" + str(high) + \".\")\n        return 0 - 1\n    for c in t:\n        if c < \"0\" or c > \"9\":\n            print(\"Type a whole number from \" + str(low) + \" to \" + str(high) + \".\")\n            return 0 - 1\n    if int(t) < low or int(t) > high:\n        print(\"Type a whole number from \" + str(low) + \" to \" + str(high) + \".\")\n        return 0 - 1\n    return int(t)\n\ndef jobs():\n    size = ask_int(\"pool size (1 to 6)> \", 1, 6)\n    if size < 0:\n        return\n    print(\"Each job leaves its body at the end, by break, by return or by raise.\")\n    t = plain(input(\"jobs (such as end break return raise end, up to 12)> \"))\n    if t == \"\":\n        print(\"Cancelled.\")\n        return\n    js = []\n    w = \"\"\n    for c in t + \" \":\n        if c == \" \":\n            if w != \"\":\n                if w != \"end\" and w != \"break\" and w != \"return\" and w != \"raise\":\n                    print(\"Each job is end, break, return or raise.\")\n                    return\n                js = js + [w]\n            w = \"\"\n        else:\n            w = w + c\n    if len(js) > 12:\n        print(\"Up to 12 jobs.\")\n        return\n    a = pool.run_jobs(size, js, \"by hand\")\n    b = pool.run_jobs(size, js, \"with\")\n    print(\"Pool of \" + str(size) + \"; connections out after each job:\")\n    print(\"  job  leaves   given back by hand              with a with block\")\n    for k in range(0, len(js)):\n        print(\"  \" + lpad(str(k + 1), 3) + \"  \" + pad(js[k], 7) + \"  \" + pad(cell(a[k]), 30) + \"  \" + cell(b[k]))\n    stuck = 0\n    for r in a:\n        if not r[2]:\n            stuck = stuck + 1\n    left = a[len(a) - 1][1]\n    if left == 0:\n        print(\"By hand: every connection given back.\")\n    else:\n        print(\"By hand: \" + str(left) + \" of \" + str(size) + \" never given back, by the jobs that left early.\")\n    if stuck > 0:\n        print(\"  \" + str(stuck) + \" later jobs found no connection free.\")\n    print(\"With a with block: every connection given back, however the body left.\")\n\ndef cell(r):\n    if not r[2]:\n        return \"none free: waits forever\"\n    return str(r[1]) + \" out\"\n\ndef table():\n    print(\"Users borrowing one connection after another, holding each until they have all they need:\")\n    print(\"the fewest users for which some order of borrowing deadlocks (- for none up to 12).\")\n    print(\"  needs   pool 2  pool 3  pool 4  pool 5  pool 6\")\n    formula = True\n    for need in range(1, 5):\n        line = \"  \" + pad(str(need), 6)\n        for size in range(2, 7):\n            c = pool.first_deadlock(size, need, 12)\n            if c == 0:\n                line = line + lpad(\"-\", 8)\n                if need > 1:\n                    formula = False\n            else:\n                line = line + lpad(str(c), 8)\n                if need == 1 or c * (need - 1) < size or (c - 1) * (need - 1) >= size:\n                    formula = False\n        print(line)\n    if formula:\n        print(\"Every count is the smallest number of users that, holding need - 1 each, can hold the whole pool; one connection at a time never deadlocks.\")\n\ndef one():\n    size = ask_int(\"pool size (1 to 8)> \", 1, 8)\n    if size < 0:\n        return\n    need = ask_int(\"connections each user needs (1 to 4)> \", 1, 4)\n    if need < 0:\n        return\n    users = ask_int(\"users (1 to 8)> \", 1, 8)\n    if users < 0:\n        return\n    r = pool.deadlock_possible(size, need, users)\n    if not r[0]:\n        print(\"No order of borrowing deadlocks: \" + str(users) + \" users needing \" + str(need) + \" each, pool of \" + str(size) + \".\")\n        return\n    held = \"\"\n    for h in r[1]:\n        if held != \"\":\n            held = held + \", \"\n        held = held + str(h)\n    print(\"Deadlock: every connection held, the waiting users holding \" + held + \" of the \" + str(need) + \" each needs, and none can borrow another.\")\n\nprint(\"== Connection pool ==\")\nprint(\"A fixed number of connections, borrowed by jobs and given back - or not.\")\nrunning = True\nwhile running:\n    print(\"\")\n    print(\"1) jobs  2) deadlock table  3) one deadlock  4) quit\")\n    choice = plain(input(\"choice> \"))\n    if choice == \"1\":\n        jobs()\n    elif choice == \"2\":\n        table()\n    elif choice == \"3\":\n        one()\n    elif choice == \"4\":\n        running = False\n    else:\n        print(\"Pick a number from 1 to 4.\")\nprint(\"Bye.\")\n"
    },
    {
      "name": "pool.eml",
      "eml": "# P128 connection pool - a fixed number of connections, borrowed and given\n# back. As in the corpus case connection-pool-release, a body can leave in\n# four ways - at its end, by break, by return, by raise - and only a with\n# block gives the connection back on all four; giving it back by hand at the\n# end of the body leaks it on the other three. As in pool-reentrancy-\n# deadlock, a request that holds one connection while it borrows the next\n# can leave every connection held and nobody able to finish.\n\nclass Pool:\n    def __init__(self, size):\n        size => self.size\n        0 => self.out\n        0 => self.leaked\n    def take(self):\n        if self.out >= self.size:\n            return False\n        self.out + 1 => self.out\n        return True\n    def give(self):\n        self.out - 1 => self.out\n    def lease(self):\n        return Lease(self)\n\nclass Lease:\n    def __init__(self, pool):\n        pool => self.pool\n    def __enter__(self):\n        return self.pool\n    def __exit__(self, exc_type, exc_value, tb):\n        self.pool.give()\n        return False\n\ndef body_by_hand(pool, how):\n    # the job: borrow, work, give back at the end - unless it leaves first\n    for attempt in [1:2]:\n        if how == \"break\":\n            break\n        if how == \"return\":\n            return \"returned\"\n        if how == \"raise\":\n            raise ValueError(\"failed\")\n        pool.give()\n        return \"done\"\n    return \"broke out\"\n\ndef by_hand(pool, how):\n    # [what happened, whether it ran]\n    if not pool.take():\n        return [\"no connection free: it would wait forever\", False]\n    try:\n        body_by_hand(pool, how) => r\n    except ValueError:\n        \"raised\" => r\n    return [r, True]\n\ndef body_with(pool, how):\n    for attempt in [1:2]:\n        with pool.lease():\n            if how == \"break\":\n                break\n            if how == \"return\":\n                return \"returned\"\n            if how == \"raise\":\n                raise ValueError(\"failed\")\n        return \"done\"\n    return \"broke out\"\n\ndef with_block(pool, how):\n    if not pool.take():\n        return [\"no connection free: it would wait forever\", False]\n    try:\n        body_with(pool, how) => r\n    except ValueError:\n        \"raised\" => r\n    return [r, True]\n\ndef run_jobs(size, jobs, style):\n    # per job [how it left, connections out after, ran]\n    Pool(size) => p\n    [] => out\n    for how in jobs:\n        if style == \"by hand\":\n            by_hand(p, how) => r\n        else:\n            with_block(p, how) => r\n        out + [[r[0], p.out, r[1]]] => out\n    return out\n\ndef deadlock_possible(size, need, users):\n    # can some order of borrowing leave all connections held, each user\n    # holding fewer than it needs? Every state of how many each holds is\n    # searched; a user that reaches its need finishes and gives all back.\n    [0] * users => start\n    {} => seen\n    1 => seen[str(start)]\n    [start] => todo\n    while len(todo) > 0:\n        todo[len(todo) - 1] => st\n        todo[0:len(todo) - 1] => todo\n        0 => held\n        for h in st:\n            held + h => held\n        size - held => free\n        if free == 0 and len(st) > 0:\n            return [True, st]\n        if free > 0:\n            for i in [0:len(st) - 1]:\n                if i == 0 or st[i] != st[i - 1]:\n                    [] => nxt\n                    for j in [0:len(st) - 1]:\n                        if j == i:\n                            if st[j] + 1 < need:\n                                nxt + [st[j] + 1] => nxt\n                        else:\n                            nxt + [st[j]] => nxt\n                    sort(nxt) => nxt\n                    if not (str(nxt) in seen):\n                        1 => seen[str(nxt)]\n                        todo + [nxt] => todo\n    return [False, []]\n\ndef sort(xs):\n    [] => out\n    for x in xs:\n        [] => nxt\n        False => placed\n        for y in out:\n            if not placed and x < y:\n                nxt + [x] => nxt\n                True => placed\n            nxt + [y] => nxt\n        if not placed:\n            nxt + [x] => nxt\n        nxt => out\n    return out\n\ndef first_deadlock(size, need, most):\n    # the fewest users, up to most, for which a deadlock is possible; 0 if none\n    for c in [1:most]:\n        if deadlock_possible(size, need, c)[0]:\n            return c\n    return 0\n",
      "python": "class Pool:\n    def __init__(self, size):\n        self.size = size\n        self.out = 0\n        self.leaked = 0\n    def take(self):\n        if self.out >= self.size:\n            return False\n        self.out = self.out + 1\n        return True\n    def give(self):\n        self.out = self.out - 1\n    def lease(self):\n        return Lease(self)\n\nclass Lease:\n    def __init__(self, pool):\n        self.pool = pool\n    def __enter__(self):\n        return self.pool\n    def __exit__(self, exc_type, exc_value, tb):\n        self.pool.give()\n        return False\n\ndef body_by_hand(pool, how):\n    for attempt in range(1, 3):\n        if how == \"break\":\n            break\n        if how == \"return\":\n            return \"returned\"\n        if how == \"raise\":\n            raise ValueError(\"failed\")\n        pool.give()\n        return \"done\"\n    return \"broke out\"\n\ndef by_hand(pool, how):\n    if not pool.take():\n        return [\"no connection free: it would wait forever\", False]\n    try:\n        r = body_by_hand(pool, how)\n    except ValueError:\n        r = \"raised\"\n    return [r, True]\n\ndef body_with(pool, how):\n    for attempt in range(1, 3):\n        with pool.lease():\n            if how == \"break\":\n                break\n            if how == \"return\":\n                return \"returned\"\n            if how == \"raise\":\n                raise ValueError(\"failed\")\n        return \"done\"\n    return \"broke out\"\n\ndef with_block(pool, how):\n    if not pool.take():\n        return [\"no connection free: it would wait forever\", False]\n    try:\n        r = body_with(pool, how)\n    except ValueError:\n        r = \"raised\"\n    return [r, True]\n\ndef run_jobs(size, jobs, style):\n    p = Pool(size)\n    out = []\n    for how in jobs:\n        if style == \"by hand\":\n            r = by_hand(p, how)\n        else:\n            r = with_block(p, how)\n        out = out + [[r[0], p.out, r[1]]]\n    return out\n\ndef deadlock_possible(size, need, users):\n    start = [0] * users\n    seen = {}\n    seen[str(start)] = 1\n    todo = [start]\n    while len(todo) > 0:\n        st = todo[len(todo) - 1]\n        todo = todo[0:len(todo) - 1]\n        held = 0\n        for h in st:\n            held = held + h\n        free = size - held\n        if free == 0 and len(st) > 0:\n            return [True, st]\n        if free > 0:\n            for i in range(0, len(st)):\n                if i == 0 or st[i] != st[i - 1]:\n                    nxt = []\n                    for j in range(0, len(st)):\n                        if j == i:\n                            if st[j] + 1 < need:\n                                nxt = nxt + [st[j] + 1]\n                        else:\n                            nxt = nxt + [st[j]]\n                    nxt = sort(nxt)\n                    if not str(nxt) in seen:\n                        seen[str(nxt)] = 1\n                        todo = todo + [nxt]\n    return [False, []]\n\ndef sort(xs):\n    out = []\n    for x in xs:\n        nxt = []\n        placed = False\n        for y in out:\n            if not placed and x < y:\n                nxt = nxt + [x]\n                placed = True\n            nxt = nxt + [y]\n        if not placed:\n            nxt = nxt + [x]\n        out = nxt\n    return out\n\ndef first_deadlock(size, need, most):\n    for c in range(1, most+1):\n        if deadlock_possible(size, need, c)[0]:\n            return c\n    return 0\n"
    }
  ],
  "sessions": [
    {
      "name": "bad-input",
      "input": "0\nx\n1\n\n1\n7\n1\n3\n\n1\n3\nend loop\n1\n3\nend end end end end end end end end end end end end\n1\n2\nend end end\n3\n\n3\n9\n3\n4\n5\n3\n4\n2\n9\n3\n1\n1\n8\n4\n",
      "screen": "== Connection pool ==\nA fixed number of connections, borrowed by jobs and given back - or not.\n\n1) jobs  2) deadlock table  3) one deadlock  4) quit\nchoice> 0\nPick a number from 1 to 4.\n\n1) jobs  2) deadlock table  3) one deadlock  4) quit\nchoice> x\nPick a number from 1 to 4.\n\n1) jobs  2) deadlock table  3) one deadlock  4) quit\nchoice> 1\npool size (1 to 6)> \nCancelled.\n\n1) jobs  2) deadlock table  3) one deadlock  4) quit\nchoice> 1\npool size (1 to 6)> 7\nType a whole number from 1 to 6.\n\n1) jobs  2) deadlock table  3) one deadlock  4) quit\nchoice> 1\npool size (1 to 6)> 3\nEach job leaves its body at the end, by break, by return or by raise.\njobs (such as end break return raise end, up to 12)> \nCancelled.\n\n1) jobs  2) deadlock table  3) one deadlock  4) quit\nchoice> 1\npool size (1 to 6)> 3\nEach job leaves its body at the end, by break, by return or by raise.\njobs (such as end break return raise end, up to 12)> end loop\nEach job is end, break, return or raise.\n\n1) jobs  2) deadlock table  3) one deadlock  4) quit\nchoice> 1\npool size (1 to 6)> 3\nEach job leaves its body at the end, by break, by return or by raise.\njobs (such as end break return raise end, up to 12)> end end end end end end end end end end end end end\nUp to 12 jobs.\n\n1) jobs  2) deadlock table  3) one deadlock  4) quit\nchoice> 1\npool size (1 to 6)> 2\nEach job leaves its body at the end, by break, by return or by raise.\njobs (such as end break return raise end, up to 12)> end end end\nPool of 2; connections out after each job:\n  job  leaves   given back by hand              with a with block\n    1  end      0 out                           0 out\n    2  end      0 out                           0 out\n    3  end      0 out                           0 out\nBy hand: every connection given back.\nWith a with block: every connection given back, however the body left.\n\n1) jobs  2) deadlock table  3) one deadlock  4) quit\nchoice> 3\npool size (1 to 8)> \nCancelled.\n\n1) jobs  2) deadlock table  3) one deadlock  4) quit\nchoice> 3\npool size (1 to 8)> 9\nType a whole number from 1 to 8.\n\n1) jobs  2) deadlock table  3) one deadlock  4) quit\nchoice> 3\npool size (1 to 8)> 4\nconnections each user needs (1 to 4)> 5\nType a whole number from 1 to 4.\n\n1) jobs  2) deadlock table  3) one deadlock  4) quit\nchoice> 3\npool size (1 to 8)> 4\nconnections each user needs (1 to 4)> 2\nusers (1 to 8)> 9\nType a whole number from 1 to 8.\n\n1) jobs  2) deadlock table  3) one deadlock  4) quit\nchoice> 3\npool size (1 to 8)> 1\nconnections each user needs (1 to 4)> 1\nusers (1 to 8)> 8\nNo order of borrowing deadlocks: 8 users needing 1 each, pool of 1.\n\n1) jobs  2) deadlock table  3) one deadlock  4) quit\nchoice> 4\nBye.\n",
      "interpreter": "equal"
    },
    {
      "name": "basic",
      "input": "1\n3\nend break return raise end end\n2\n3\n4\n2\n4\n3\n4\n2\n3\n4\n",
      "screen": "== Connection pool ==\nA fixed number of connections, borrowed by jobs and given back - or not.\n\n1) jobs  2) deadlock table  3) one deadlock  4) quit\nchoice> 1\npool size (1 to 6)> 3\nEach job leaves its body at the end, by break, by return or by raise.\njobs (such as end break return raise end, up to 12)> end break return raise end end\nPool of 3; connections out after each job:\n  job  leaves   given back by hand              with a with block\n    1  end      0 out                           0 out\n    2  break    1 out                           0 out\n    3  return   2 out                           0 out\n    4  raise    3 out                           0 out\n    5  end      none free: waits forever        0 out\n    6  end      none free: waits forever        0 out\nBy hand: 3 of 3 never given back, by the jobs that left early.\n  2 later jobs found no connection free.\nWith a with block: every connection given back, however the body left.\n\n1) jobs  2) deadlock table  3) one deadlock  4) quit\nchoice> 2\nUsers borrowing one connection after another, holding each until they have all they need:\nthe fewest users for which some order of borrowing deadlocks (- for none up to 12).\n  needs   pool 2  pool 3  pool 4  pool 5  pool 6\n  1            -       -       -       -       -\n  2            2       3       4       5       6\n  3            1       2       2       3       3\n  4            1       1       2       2       2\nEvery count is the smallest number of users that, holding need - 1 each, can hold the whole pool; one connection at a time never deadlocks.\n\n1) jobs  2) deadlock table  3) one deadlock  4) quit\nchoice> 3\npool size (1 to 8)> 4\nconnections each user needs (1 to 4)> 2\nusers (1 to 8)> 4\nDeadlock: every connection held, the waiting users holding 1, 1, 1, 1 of the 2 each needs, and none can borrow another.\n\n1) jobs  2) deadlock table  3) one deadlock  4) quit\nchoice> 3\npool size (1 to 8)> 4\nconnections each user needs (1 to 4)> 2\nusers (1 to 8)> 3\nNo order of borrowing deadlocks: 3 users needing 2 each, pool of 4.\n\n1) jobs  2) deadlock table  3) one deadlock  4) quit\nchoice> 4\nBye.\n",
      "interpreter": "equal"
    }
  ],
  "builtOn": [
    {
      "slug": "connection-pool-release",
      "caseId": "186-connection-pool-release",
      "title": "Connection pool: release on every exit path"
    },
    {
      "slug": "pool-reentrancy-deadlock",
      "caseId": "285-pool-reentrancy-deadlock",
      "title": "Pool reentrancy deadlock — at capacity means deadlocked"
    }
  ],
  "updated": "2026-10-17"
}
