Submit and Run

Step 2: Submit and Run

Now the pool itself: a queue of pending jobs and a way to run them. Each submit stashes a job and hands back a future; run drains the queue, executing jobs and fulfilling their futures.

  • submit(fn, *args, **kwargs): enqueue the job, return its Future
  • run(): process every queued job in FIFO order, calling fn(*args, **kwargs) and storing the result on its future

Nothing executes until run() is called. That's what makes it a pool and not a plain function call.

Hints
Starter code
from collections import deque


class Future:
    def __init__(self):
        self._done = False
        self._result = None
        self._exc = None

    def set_result(self, value):
        self._result = value
        self._done = True

    def set_exception(self, exc):
        self._exc = exc
        self._done = True

    def done(self):
        return self._done

    def result(self):
        if not self._done:
            raise RuntimeError("future is not done")
        if self._exc is not None:
            raise self._exc
        return self._result


class ThreadPool:
    def __init__(self, num_workers):
        self.num_workers = num_workers
        self._queue = deque()   # (future, fn, args, kwargs)

    def submit(self, fn, *args, **kwargs):
        """Enqueue a job and return its Future."""
        # TODO:
        #   fut = Future()
        #   self._queue.append((fut, fn, args, kwargs))
        #   return fut
        pass

    def run(self):
        """Run every queued job in FIFO order, fulfilling each future."""
        # TODO: while self._queue:
        #   fut, fn, args, kwargs = self._queue.popleft()
        #   fut.set_result(fn(*args, **kwargs))
        pass
Write it on a laptop

The editor and the test runner need a wide screen. Open this page on a desktop browser and your progress will be waiting.