Skip to content

use async process management to avoid blocking event loop on sea startup and shutdown#335

Open
VrushankGunjur wants to merge 1 commit intobrowserbase:mainfrom
VrushankGunjur:fix-eloop-blocking-sea-lifecycle
Open

use async process management to avoid blocking event loop on sea startup and shutdown#335
VrushankGunjur wants to merge 1 commit intobrowserbase:mainfrom
VrushankGunjur:fix-eloop-blocking-sea-lifecycle

Conversation

@VrushankGunjur
Copy link
Copy Markdown

@VrushankGunjur VrushankGunjur commented Apr 3, 2026

Summary by cubic

Switch SEA process start/stop to fully async to prevent blocking the event loop during startup and shutdown. Improves reliability with timeouts and cross‑platform, process‑group aware termination.

  • Bug Fixes
    • Use asyncio.create_subprocess_exec with start_new_session=True; track running child as _async_proc.
    • Async shutdown: SIGTERM/terminate + wait_for(3s), then SIGKILL/kill + wait_for(3s) fallback; cross-platform.
    • Non-blocking atexit terminator that signals the process group without waiting.
    • Prevents stalls in ensure_running_async and aclose; sync path unchanged.

Written for commit c58d9f7. Summary will update on new commits. Review in cubic

@pirate
Copy link
Copy Markdown
Member

pirate commented Apr 3, 2026

good idea, the code moved into src/_custom recently but we can incorporate these changes @monadoid

@VrushankGunjur VrushankGunjur force-pushed the fix-eloop-blocking-sea-lifecycle branch from 6b90438 to c58d9f7 Compare April 3, 2026 19:53
@VrushankGunjur
Copy link
Copy Markdown
Author

gotcha, ty

@VrushankGunjur VrushankGunjur marked this pull request as ready for review April 3, 2026 20:19
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 1 file

Confidence score: 3/5

  • There is a concrete lifecycle risk in src/stagehand/_custom/sea_server.py: split sync/async state (_proc vs _async_proc) can start duplicate SEA processes and leave partial cleanup when both APIs are used on one manager.
  • Given the high confidence (9/10) and user-facing runtime impact (process leaks/conflicting starts), this is more than a minor issue and introduces meaningful regression risk.
  • If callers consistently use only one API style, impact may be narrower, but mixed sync/async usage appears unsafe in the current design.
  • Pay close attention to src/stagehand/_custom/sea_server.py - unify lifecycle ownership/state to prevent duplicate starts and incomplete shutdown.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/stagehand/_custom/sea_server.py">

<violation number="1" location="src/stagehand/_custom/sea_server.py:220">
P1: Sync/async lifecycle state is bifurcated (`_proc` vs `_async_proc`), allowing duplicate SEA process starts and incomplete cleanup when both APIs are used on one manager.</violation>
</file>
Architecture diagram
sequenceDiagram
    participant App as Caller
    participant SEA as SeaServer
    participant Proc as Async Subprocess
    participant Health as Health Check (HTTP)
    participant OS as OS / Process Group

    Note over App,OS: Startup Flow (ensure_running_async)

    App->>SEA: ensure_running_async()
    SEA->>SEA: Acquire async lock
    alt Process not running
        SEA->>Proc: NEW: asyncio.create_subprocess_exec()
        Note right of Proc: start_new_session=True (New PGID)
        SEA->>SEA: CHANGED: Register async-compatible atexit
        loop Until Ready or Timeout
            SEA->>Health: _wait_ready_async()
            Health-->>SEA: 200 OK
        end
    end
    SEA-->>App: base_url

    Note over App,OS: Shutdown Flow (aclose)

    App->>SEA: aclose()
    SEA->>SEA: Acquire async lock
    alt Process exists
        SEA->>OS: NEW: Send SIGTERM to PGID (or proc.terminate)
        SEA->>Proc: NEW: await asyncio.wait_for(proc.wait(), 3s)
        
        alt Timeout (Process still alive)
            Proc-->>SEA: TimeoutError
            SEA->>OS: NEW: Send SIGKILL to PGID (or proc.kill)
            SEA->>Proc: NEW: await asyncio.wait_for(proc.wait(), 3s)
        else Success
            Proc-->>SEA: Process exited
        end
    end
    SEA-->>App: Done

    Note over SEA,OS: Emergency Cleanup (atexit)
    OS->>SEA: Process exiting (atexit)
    SEA->>OS: NEW: Non-blocking SIGTERM to PGID
Loading

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.

async def ensure_running_async(self) -> str:
async with self._async_lock:
if self._proc is not None and self._proc.poll() is None and self._base_url is not None:
if self._async_proc is not None and self._async_proc.returncode is None and self._base_url is not None:
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Sync/async lifecycle state is bifurcated (_proc vs _async_proc), allowing duplicate SEA process starts and incomplete cleanup when both APIs are used on one manager.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/stagehand/_custom/sea_server.py, line 220:

<comment>Sync/async lifecycle state is bifurcated (`_proc` vs `_async_proc`), allowing duplicate SEA process starts and incomplete cleanup when both APIs are used on one manager.</comment>

<file context>
@@ -177,12 +217,12 @@ def ensure_running_sync(self) -> str:
     async def ensure_running_async(self) -> str:
         async with self._async_lock:
-            if self._proc is not None and self._proc.poll() is None and self._base_url is not None:
+            if self._async_proc is not None and self._async_proc.returncode is None and self._base_url is not None:
                 return self._base_url
 
</file context>
Fix with Cubic

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants