This fixes GitHub issue #4072 where an async task that errors would keep
its thread alive because the Future was never completed.
The issue was in the _execute_task_async method which didn't handle
exceptions from _execute_core. When an exception was raised, the
future.set_result() was never called, leaving the Future in an incomplete
state. This caused future.result() to block forever.
The fix wraps the _execute_core call in a try-except block and calls
future.set_exception(e) when an exception occurs, ensuring the Future
is always properly completed.
Added tests:
- test_execute_async_basic: Basic threaded async execution
- test_execute_async_exception_completes_future: Regression test for #4072
- test_execute_async_exception_sets_end_time: Verify end_time is set on error
- test_execute_async_exception_does_not_hang: Verify no hang on error
Co-Authored-By: João <joao@crewai.com>