Skip to content

Commit a5ba8d0

Browse files
author
yolo h8cker 93
committed
Fix(matrix): Ensure every game worker has only one game running
1 parent 98918a0 commit a5ba8d0

1 file changed

Lines changed: 47 additions & 25 deletions

File tree

codeclash/analysis/matrix.py

Lines changed: 47 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
import json
33
import time
44
import uuid
5-
from concurrent.futures import ThreadPoolExecutor, as_completed
65
from pathlib import Path
7-
from threading import Lock
6+
from queue import Queue
7+
from threading import Lock, Thread
88

99
from codeclash.agents.dummy_agent import Dummy
1010
from codeclash.agents.utils import GameContext
@@ -211,16 +211,35 @@ def _evaluate_matrix_cell_parallel(
211211
result = stats.to_dict()
212212
self.logger.debug(f"Result: {result}")
213213

214-
# Save the result immediately after computation
215214
with self._save_lock:
216215
self.matrices[matrix_id][str(i)][str(j)] = result
217216
self.output_file.write_text(json.dumps(self._metadata, indent=2))
218-
self.logger.debug(f"Saved result for {player1_name} round {i} vs {player2_name} round {j}")
219217

220218
return (i, j, result)
221219

220+
def _worker_thread(self, worker_id: int, task_queue: Queue):
221+
"""Worker thread that processes tasks using a specific game worker."""
222+
game_worker = self.game_pool[worker_id]
223+
self.logger.debug(f"Worker {worker_id} started")
224+
225+
while True:
226+
try:
227+
task = task_queue.get(timeout=1) # Short timeout to allow clean exit
228+
except:
229+
# Queue is empty and no more tasks coming
230+
break
231+
232+
try:
233+
self._evaluate_matrix_cell_parallel(game_worker, **task)
234+
except Exception as e:
235+
self.logger.error(f"Worker {worker_id} failed on task {task}: {e}")
236+
finally:
237+
task_queue.task_done()
238+
239+
self.logger.debug(f"Worker {worker_id} stopped")
240+
222241
def _evaluate_matrix(self, player1_name: str, player2_name: str):
223-
"""Evaluate a matrix between two players using parallel execution."""
242+
"""Evaluate a matrix between two players using manual thread management."""
224243
symmetric = player1_name == player2_name
225244
matrix_id = f"{player1_name}_vs_{player2_name}"
226245
self.logger.info(f"Evaluating {matrix_id} matrix: {player1_name} vs {player2_name}")
@@ -230,9 +249,9 @@ def _evaluate_matrix(self, player1_name: str, player2_name: str):
230249
for i in range(self.rounds + 1):
231250
self.matrices[matrix_id].setdefault(str(i), {})
232251

233-
# Collect all tasks to be executed
234-
tasks = []
235-
game_worker_index = 0
252+
# Create task queue and populate it directly
253+
task_queue = Queue()
254+
task_count = 0
236255

237256
for i in range(self.rounds + 1):
238257
j_range = range(i) if symmetric else range(self.rounds + 1)
@@ -243,27 +262,30 @@ def _evaluate_matrix(self, player1_name: str, player2_name: str):
243262
continue
244263
except KeyError:
245264
pass
265+
task_queue.put(
266+
{"player1_name": player1_name, "player2_name": player2_name, "i": i, "j": j, "matrix_id": matrix_id}
267+
)
268+
task_count += 1
246269

247-
# Assign game worker in round-robin fashion
248-
game_worker = self.game_pool[game_worker_index % len(self.game_pool)]
249-
game_worker_index += 1
250-
251-
tasks.append((game_worker, player1_name, player2_name, i, j, matrix_id))
252-
253-
if not tasks:
270+
if task_count == 0:
254271
self.logger.info(f"All matrix cells for {matrix_id} already completed")
255272
return
256273

257-
self.logger.info(f"Executing {len(tasks)} matrix cells in parallel with {self.max_workers} workers")
258-
259-
# Execute tasks in parallel
260-
with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
261-
# Submit all tasks
262-
futures = [executor.submit(self._evaluate_matrix_cell_parallel, *task) for task in tasks]
263-
264-
# Wait for all tasks to complete
265-
for future in as_completed(futures):
266-
future.result() # This will raise any exceptions that occurred
274+
self.logger.info(f"Executing {task_count} matrix cells using {self.max_workers} dedicated workers")
275+
276+
# Start worker threads - each bound to a specific game worker
277+
self.logger.debug("Starting worker threads")
278+
workers = []
279+
for worker_id in range(self.max_workers):
280+
worker = Thread(target=self._worker_thread, args=(worker_id, task_queue))
281+
worker.start()
282+
workers.append(worker)
283+
284+
self.logger.debug("Waiting for tasks to complete")
285+
task_queue.join()
286+
self.logger.debug("Workers finished")
287+
for worker in workers:
288+
worker.join()
267289

268290
self.logger.info(f"Completed matrix evaluation for {matrix_id}")
269291

0 commit comments

Comments
 (0)