Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 100 additions & 0 deletions submissions/pt2302.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
"""Persistent-worker build scheduler for the PyCon free-threading challenge."""

import os
import queue
import threading

from graph import BuildGraph


MAX_WORKERS = 24


def build_all(graph: BuildGraph) -> dict[str, bytes]:
targets = graph.targets
total = len(targets)
if total == 0:
return {}

remaining = {name: len(target.deps) for name, target in targets.items()}
dependents: dict[str, list[str]] = {name: [] for name in targets}
for name, target in targets.items():
for dep in target.deps:
dependents[dep.name].append(name)

roots = [name for name, count in remaining.items() if count == 0]
leaves = sum(1 for children in dependents.values() if not children)

if (
len(roots) == 1
and leaves == 1
and all(count <= 1 for count in remaining.values())
and all(len(children) <= 1 for children in dependents.values())
):
results: dict[str, bytes] = {}
name = roots[0]
while True:
target = targets[name]
dep_results = (
{dep.name: results[dep.name] for dep in target.deps}
if target.deps
else {}
)
results[name] = target.build(dep_results)

children = dependents[name]
if not children:
return results
name = children[0]

ready: queue.SimpleQueue[str | None] = queue.SimpleQueue()
for name in roots:
ready.put(name)

results: dict[str, bytes] = {}
lock = threading.Lock()
pending = total
workers = min(MAX_WORKERS, os.cpu_count() or 1, total)
sentinel = None
empty_deps: dict[str, bytes] = {}

def worker() -> None:
nonlocal pending

while True:
name = ready.get()
if name is sentinel:
return

target = targets[name]
dep_results = (
{dep.name: results[dep.name] for dep in target.deps}
if target.deps
else empty_deps
)
result = target.build(dep_results)

with lock:
results[name] = result
pending -= 1

if pending == 0:
for _ in range(workers):
ready.put(sentinel)
continue

for child in dependents[name]:
remaining[child] -= 1
if remaining[child] == 0:
ready.put(child)

threads = [threading.Thread(target=worker) for _ in range(workers - 1)]
for thread in threads:
thread.start()

worker()

for thread in threads:
thread.join()

return results
Loading