Use atexit to add another layer of protection

Hopefully should make process leaks vanishingly rare. Basically only
on crashes.
This commit is contained in:
Kovid Goyal 2024-08-17 08:52:42 +05:30
parent f82da06184
commit cfe9c58db8
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C

View File

@ -1,11 +1,12 @@
#!/usr/bin/env python #!/usr/bin/env python
# License: GPLv3 Copyright: 2024, Kovid Goyal <kovid at kovidgoyal.net> # License: GPLv3 Copyright: 2024, Kovid Goyal <kovid at kovidgoyal.net>
import atexit
import json import json
import os import os
import shutil import shutil
import subprocess import subprocess
import time import weakref
from contextlib import suppress from contextlib import suppress
from io import BytesIO from io import BytesIO
from queue import Queue from queue import Queue
@ -101,6 +102,12 @@ class FakeResponse:
self._data.close() self._data.close()
def shutdown_browser(bref):
br = bref()
if br is not None:
br.shutdown()
class Browser: class Browser:
def __init__(self, user_agent: str = '', headers: tuple[tuple[str, str], ...] = (), verify_ssl_certificates: bool = True, start_worker: bool = False): def __init__(self, user_agent: str = '', headers: tuple[tuple[str, str], ...] = (), verify_ssl_certificates: bool = True, start_worker: bool = False):
@ -113,6 +120,7 @@ class Browser:
self.user_agent = user_agent self.user_agent = user_agent
self.lock = RLock() self.lock = RLock()
self.shutting_down = False self.shutting_down = False
atexit.register(shutdown_browser, weakref.ref(self))
if start_worker: if start_worker:
self._ensure_state() self._ensure_state()
@ -226,16 +234,18 @@ class Browser:
def shutdown(self): def shutdown(self):
self.shutting_down = True self.shutting_down = True
import shutil import shutil
import time
if self.worker: if self.worker:
w, self.worker = self.worker, None
with suppress(OSError): with suppress(OSError):
self.worker.stdin.close() w.stdin.close()
with suppress(OSError): with suppress(OSError):
self.worker.stdout.close() w.stdout.close()
give_up_at = time.monotonic() + 1.5 give_up_at = time.monotonic() + 1.5
while time.monotonic() < give_up_at and self.worker.poll() is None: while time.monotonic() < give_up_at and w.poll() is None:
time.sleep(0.01) time.sleep(0.01)
if self.worker.poll() is None: if w.poll() is None:
self.worker.kill() w.kill()
if self.tdir: if self.tdir:
with suppress(OSError): with suppress(OSError):
shutil.rmtree(self.tdir) shutil.rmtree(self.tdir)