Skip to content

Commit 1da62d7

Browse files
linesightclaude
andcommitted
Use async CreateBrowser() + pump loop on Linux to avoid CreateBrowserSync() deadlock
CefBrowserHost::CreateBrowserSync() creates an internal nested RunLoop that cannot drive the in-process renderer thread when --single-process is active and Python owns the UI thread via CefDoMessageLoopWork(). The result is a ~60s timeout and a null browser return. Fix: on Linux, call the async CefBrowserHost::CreateBrowser() instead and pump CefDoMessageLoopWork() in a Python loop until OnAfterCreated fires and populates g_pyBrowsers. Then retrieve the CefBrowser ref from there. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 89f54a0 commit 1da62d7

2 files changed

Lines changed: 36 additions & 2 deletions

File tree

src/cefpython.pyx

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -806,11 +806,36 @@ def CreateBrowserSync(windowInfo=None,
806806
cdef CefRefPtr[CefDictionaryValue] extra_info
807807

808808
# CEF browser creation.
809-
with nogil:
810-
cefBrowser = cef_browser_static.CreateBrowserSync(
809+
IF UNAME_SYSNAME == "Linux":
810+
# CefBrowserHost::CreateBrowserSync() deadlocks when combined with
811+
# --single-process + external pump mode (CefDoMessageLoopWork). The
812+
# nested RunLoop it creates cannot drive the in-process renderer
813+
# thread initialisation. Use async CreateBrowser() and keep pumping
814+
# the message loop until OnAfterCreated fires and populates
815+
# g_pyBrowsers, then retrieve the CefBrowser ref from there.
816+
cdef set _before_browser_ids = set(g_pyBrowsers.keys())
817+
cef_browser_static.CreateBrowser(
811818
cefWindowInfo, <CefRefPtr[CefClient]?>clientHandler,
812819
cefNavigateUrl, cefBrowserSettings, extra_info,
813820
cefRequestContext)
821+
cdef PyBrowser _linux_pyBrowser = None
822+
cdef int _i
823+
for _i in range(6000): # up to 60 s at 0.01 s/iter
824+
with nogil:
825+
CefDoMessageLoopWork()
826+
_new_browser_ids = set(g_pyBrowsers.keys()) - _before_browser_ids
827+
if _new_browser_ids:
828+
_linux_pyBrowser = GetPyBrowserById(min(_new_browser_ids))
829+
if _linux_pyBrowser is not None:
830+
cefBrowser = _linux_pyBrowser.cefBrowser
831+
break
832+
time.sleep(0.01)
833+
ELSE:
834+
with nogil:
835+
cefBrowser = cef_browser_static.CreateBrowserSync(
836+
cefWindowInfo, <CefRefPtr[CefClient]?>clientHandler,
837+
cefNavigateUrl, cefBrowserSettings, extra_info,
838+
cefRequestContext)
814839

815840
if not cefBrowser or not cefBrowser.get():
816841
Debug("CefBrowser::CreateBrowserSync() failed")

src/extern/cef/cef_browser_static.pxd

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
include "platform_cimports.pxi"
66

7+
from libcpp cimport bool as cpp_bool
78
from cef_ptr cimport CefRefPtr
89
# noinspection PyUnresolvedReferences
910
from cef_client cimport CefClient
@@ -18,6 +19,14 @@ from cef_string cimport CefString
1819
# Specifying namespace allows to import a static method.
1920
cdef extern from "include/cef_browser.h" namespace "CefBrowserHost":
2021

22+
cdef cpp_bool CreateBrowser(
23+
CefWindowInfo&,
24+
CefRefPtr[CefClient],
25+
CefString&,
26+
CefBrowserSettings&,
27+
CefRefPtr[CefDictionaryValue],
28+
CefRefPtr[CefRequestContext]) nogil
29+
2130
cdef CefRefPtr[CefBrowser] CreateBrowserSync(
2231
CefWindowInfo&,
2332
CefRefPtr[CefClient],

0 commit comments

Comments
 (0)