Allow to cancel a task in the task queue via the Future,

executing every task in a separate thread
This commit is contained in:
Dominik Jain authored and Dominik Jain committed 2025-10-30 17:15:26 +01:00
1 parent c1f8ff9fa8
commit d59c748929
2 files changed
+87 -7

No files matched your search

+20 -7
View File
@@ -211,18 +211,31 @@ class SerenaAgent:
"""
Execute the task, setting the result or exception on the future.
"""
try:
with LogTime(self.name, logger=log):
result = self._function()
self.future.set_result(result)
except Exception as e:
log.error(f"Error during execution of {self.name}: {e}", exc_info=e)
self.future.set_exception(e)
def run_task():
try:
with LogTime(self.name, logger=log):
result = self._function()
if not self.future.done():
self.future.set_result(result)
except Exception as e:
if not self.future.done():
log.error(f"Error during execution of {self.name}: {e}", exc_info=e)
self.future.set_exception(e)
thread = Thread(target=run_task, name=self.name)
thread.start()
def _process_task_queue(self) -> None:
while True:
log.debug("Waiting for task ...")
task: SerenaAgent.Task = self._task_executor_queue.get()
task.execute()
try:
# wait for task completion
task.future.result()
except:
pass
def get_language_server_manager(self) -> LanguageServerManager | None:
if self._active_project is not None:
+67
View File
@@ -61,6 +61,14 @@ def serena_agent(request: pytest.FixtureRequest, serena_config):
return SerenaAgent(project=project_name, serena_config=serena_config)
@pytest.fixture
def basic_serena_agent(serena_config):
"""
Fixture for a basic SerenaAgent without a project
"""
return SerenaAgent(project=None, serena_config=serena_config)
class TestSerenaAgent:
@pytest.mark.parametrize(
"serena_agent,symbol_name,expected_kind,expected_file",
@@ -284,3 +292,62 @@ class TestSerenaAgent:
symbols = json.loads(result)
assert not symbols, f"Expected to find no symbols for {name_path}. Symbols found: {symbols}"
@staticmethod
def _create_task(delay: float, name: str, exception: bool = False):
def fn():
time.sleep(delay)
if exception:
raise ValueError(f"Task {name} failed")
return True
fn.__name__ = name
return fn
def test_task_queue_sequence(self, basic_serena_agent):
"""
Tests that a sequence of tasks is executed correctly
"""
future1 = basic_serena_agent.issue_task(self._create_task(1, "task1"))
future2 = basic_serena_agent.issue_task(self._create_task(1, "task2"))
assert future1.result() is True
assert future2.result() is True
def test_task_queue_exception(self, basic_serena_agent):
"""
Tests that tasks that raise exceptions are handled correctly, i.e. that
* the exception is propagated,
* subsequent tasks are still executed.
"""
future1 = basic_serena_agent.issue_task(self._create_task(1, "task1", exception=True))
future2 = basic_serena_agent.issue_task(self._create_task(1, "task2"))
have_exception = False
try:
assert future1.result()
except Exception as e:
assert isinstance(e, ValueError)
have_exception = True
assert have_exception
assert future2.result() is True
def test_task_queue_cancel(self, basic_serena_agent):
"""
Tests that tasks that are cancelled are handled correctly, i.e. that
* subsequent tasks are executed as soon as cancellation ensues.
* the cancelled task raises CancelledError when result() is called.
"""
start_time = time.time()
future1 = basic_serena_agent.issue_task(self._create_task(10, "task1"))
future2 = basic_serena_agent.issue_task(self._create_task(1, "task2"))
time.sleep(1)
future1.cancel()
assert future2.result() is True
end_time = time.time()
assert (end_time - start_time) < 9, "Cancelled task did not stop in time"
have_cancelled_error = False
try:
future1.result()
except Exception as e:
assert e.__class__.__name__ == "CancelledError"
have_cancelled_error = True
assert have_cancelled_error