aboutsummaryrefslogtreecommitdiffstats
path: root/idatui/worker_client.py
diff options
context:
space:
mode:
Diffstat (limited to 'idatui/worker_client.py')
-rw-r--r--idatui/worker_client.py20
1 files changed, 19 insertions, 1 deletions
diff --git a/idatui/worker_client.py b/idatui/worker_client.py
index 79a6db9..59c62a1 100644
--- a/idatui/worker_client.py
+++ b/idatui/worker_client.py
@@ -83,11 +83,19 @@ class WorkerClient:
self._sid = uuid.uuid4().hex[:8]
self._lock = threading.Lock() # serialize socket use
self._spawn_lock = threading.Lock()
+ #: Set by close(). A dropped socket is respawned on the next call (the
+ #: worker segfaulted and we want it back); a CLOSED one must not be.
+ #: Teardown and binary-switch both close while @work threads are still
+ #: in flight, so without this, quitting during a decompile spawned a
+ #: fresh idalib worker that re-opened the database nobody was looking
+ #: at any more -- a stray process holding the .i64 we just released.
+ self._closed = False
# -- lifecycle --------------------------------------------------------- #
def connect(self, timeout: float = 1800.0, progress=None) -> "WorkerClient":
"""Spawn the worker (opens + analyzes the DB) and connect once ready."""
with self._spawn_lock:
+ self._closed = False # an explicit reconnect revives this client
if self._sock is not None:
return self
if self._proc is None or self._proc.poll() is not None:
@@ -104,6 +112,11 @@ class WorkerClient:
)
deadline = time.time() + timeout
t0 = time.time()
+ # Poll fast at first, then back off. A flat 0.2s cost every caller a
+ # fifth of a second even when the worker was ready in milliseconds
+ # (a small binary, or a seeded .i64), which is most of the time in
+ # the tests and noticeable on a re-open.
+ delay = 0.005
while time.time() < deadline:
try:
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
@@ -118,7 +131,8 @@ class WorkerClient:
if progress:
progress(f"auto-analyzing {os.path.basename(self._bin)}… "
f"({int(time.time() - t0)}s)")
- time.sleep(0.2)
+ time.sleep(delay)
+ delay = min(delay * 1.6, 0.2)
raise IDAConnectionError("worker did not become ready in time")
@property
@@ -137,6 +151,7 @@ class WorkerClient:
with self._lock:
s = self._sock
self._sock = None
+ self._closed = True
if s is not None:
try:
_send(s, ("__shutdown__", {}))
@@ -161,6 +176,9 @@ class WorkerClient:
# -- the call surface -------------------------------------------------- #
def call(self, tool: str, *, timeout: float | None = None, **args) -> Any:
+ if self._closed:
+ raise IDAConnectionError(
+ f"{tool}: this worker was closed (call connect() to revive it)")
if self._sock is None:
self.connect()
with self._lock: