-
Notifications
You must be signed in to change notification settings - Fork 3
Cache: Terminate processes when closing executor #447
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
4d01b06
9307b01
2632aba
ad441a5
54924b5
5a18e97
cf5b211
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,39 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import subprocess | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import time | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from typing import Optional | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def execute_in_subprocess( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| command: list, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| task_dependent_lst: list = [], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cwd: Optional[str] = None, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) -> subprocess.Popen: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+6
to
+10
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix mutable default argument and add error handling The function has a few potential issues that should be addressed: Apply this diff to fix the mutable default argument and add error handling: def execute_in_subprocess(
command: list,
- task_dependent_lst: list = [],
+ task_dependent_lst: Optional[list] = None,
cwd: Optional[str] = None,
) -> subprocess.Popen:📝 Committable suggestion
Suggested change
🧰 Tools🪛 Ruff
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Execute a command in a subprocess. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Args: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| command (list): The command to be executed. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| task_dependent_lst (list): A list of subprocesses that the current subprocess depends on. Defaults to []. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cwd (str/None): current working directory where the parallel python task is executed | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Returns: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| subprocess.Popen: The subprocess object. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| while len(task_dependent_lst) > 0: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| task_dependent_lst = [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| task for task in task_dependent_lst if task.poll() is None | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return subprocess.Popen(command, universal_newlines=True, cwd=cwd) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add error handling for subprocess creation The subprocess creation should be wrapped in a try-except block to handle potential errors. Here's a suggested implementation: - return subprocess.Popen(command, universal_newlines=True, cwd=cwd)
+ try:
+ return subprocess.Popen(
+ command,
+ universal_newlines=True,
+ cwd=cwd,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE
+ )
+ except (OSError, subprocess.SubprocessError) as e:
+ raise RuntimeError(f"Failed to execute command {command}: {str(e)}")📝 Committable suggestion
Suggested change
Comment on lines
+23
to
+27
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add timeout and error handling for dependent tasks The while loop could potentially run indefinitely if dependent tasks never complete. Consider adding a timeout mechanism and proper error handling. Here's a suggested implementation: + if task_dependent_lst is None:
+ task_dependent_lst = []
+ start_time = time.time()
+ timeout = 3600 # 1 hour timeout, adjust as needed
while len(task_dependent_lst) > 0:
+ if time.time() - start_time > timeout:
+ raise TimeoutError("Dependent tasks did not complete within the timeout period")
task_dependent_lst = [
task for task in task_dependent_lst if task.poll() is None
]
+ time.sleep(0.1) # Add small delay to prevent CPU spinning
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def terminate_subprocess(task): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Terminate a subprocess and wait for it to complete. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Args: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| task (subprocess.Popen): The subprocess.Popen instance to terminate | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| task.terminate() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| while task.poll() is None: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| time.sleep(0.1) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+30
to
+39
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Enhance subprocess termination with graceful shutdown and timeout The current implementation could be improved with proper type hints, graceful shutdown attempt, timeout mechanism, and error handling. Here's a suggested implementation: -def terminate_subprocess(task):
+def terminate_subprocess(task: subprocess.Popen, timeout: float = 10.0) -> None:
"""
Terminate a subprocess and wait for it to complete.
Args:
task (subprocess.Popen): The subprocess.Popen instance to terminate
+ timeout (float): Maximum time to wait for process termination in seconds
+
+ Raises:
+ TimeoutError: If the process doesn't terminate within the timeout period
"""
- task.terminate()
- while task.poll() is None:
- time.sleep(0.1)
+ try:
+ # First attempt graceful shutdown
+ task.terminate()
+ start_time = time.time()
+
+ while task.poll() is None:
+ if time.time() - start_time > timeout:
+ # Force kill if graceful shutdown fails
+ task.kill()
+ if task.poll() is None:
+ raise TimeoutError(f"Failed to terminate process {task.pid} within {timeout} seconds")
+ time.sleep(0.1)
+ except Exception as e:
+ raise RuntimeError(f"Error while terminating process {task.pid}: {str(e)}")📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add error handling and logging to termination logic.
The current implementation might leave processes running if termination fails for any task.
Consider this more robust implementation:
📝 Committable suggestion