X Tutup
Skip to content

Commit a97b3c0

Browse files
committed
CreateBrowserSync, Shutdown and others are now called without
python global interpreter lock (GIL), to avoid deadlocks, see Issue 102. Creating a seperate client handler for each browser, earlier a global client handler was used. Enabled cefpython debug messages in the wx examples.
1 parent cf028aa commit a97b3c0

File tree

6 files changed

+86
-67
lines changed

6 files changed

+86
-67
lines changed

cefpython/cef3/linux/binaries_32bit/wxpython.py

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
# An example of embedding CEF browser in wxPython on Linux.
22

3-
# Important:
4-
# On Linux importing the cefpython module must be
5-
# the very first in your application. This is because CEF makes
6-
# a global tcmalloc hook for memory allocation/deallocation.
3+
# Important:
4+
# On Linux importing the cefpython module must be
5+
# the very first in your application. This is because CEF makes
6+
# a global tcmalloc hook for memory allocation/deallocation.
77
# See Issue 73 that is to provide CEF builds with tcmalloc disabled:
88
# https://code.google.com/p/cefpython/issues/detail?id=73
99

@@ -162,7 +162,7 @@ def PyPrint(message):
162162
class JavascriptExternal:
163163
mainBrowser = None
164164
stringVisitor = None
165-
165+
166166
def __init__(self, mainBrowser):
167167
self.mainBrowser = mainBrowser
168168

@@ -284,12 +284,12 @@ def Visit(self, cookie, count, total, deleteCookie):
284284
return True
285285

286286
class ClientHandler:
287-
287+
288288
# -------------------------------------------------------------------------
289289
# DisplayHandler
290290
# -------------------------------------------------------------------------
291291

292-
def OnLoadingStateChange(self, browser, isLoading, canGoBack,
292+
def OnLoadingStateChange(self, browser, isLoading, canGoBack,
293293
canGoForward):
294294
print("DisplayHandler::OnLoadingStateChange()")
295295
print("isLoading = %s, canGoBack = %s, canGoForward = %s" \
@@ -304,7 +304,7 @@ def OnTitleChange(self, browser, title):
304304
print("title = %s" % title)
305305

306306
def OnTooltip(self, browser, textOut):
307-
# OnTooltip not yet implemented (both Linux and Windows),
307+
# OnTooltip not yet implemented (both Linux and Windows),
308308
# will be fixed in next CEF release, see Issue 783:
309309
# https://code.google.com/p/chromiumembedded/issues/detail?id=783
310310
print("DisplayHandler::OnTooltip()")
@@ -332,7 +332,7 @@ def OnConsoleMessage(self, browser, message, source, line):
332332
# KeyboardHandler
333333
# -------------------------------------------------------------------------
334334

335-
def OnPreKeyEvent(self, browser, event, eventHandle,
335+
def OnPreKeyEvent(self, browser, event, eventHandle,
336336
isKeyboardShortcutOut):
337337
print("KeyboardHandler::OnPreKeyEvent()")
338338

@@ -516,8 +516,11 @@ def OnExit(self):
516516
self.timer.Stop()
517517

518518
if __name__ == '__main__':
519+
# Intercept python exceptions. Exit app immediately when exception
520+
# happens on any of the threads.
519521
sys.excepthook = ExceptHook
520-
cefpython.g_debug = False
522+
# Set to False to disable cefpython debug messages in console.
523+
cefpython.g_debug = True
521524
cefpython.g_debugFile = GetApplicationPath("debug.log")
522525
settings = {
523526
"log_severity": cefpython.LOGSEVERITY_INFO, # LOGSEVERITY_VERBOSE

cefpython/cef3/linux/binaries_64bit/wxpython.py

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
# An example of embedding CEF browser in wxPython on Linux.
22

3-
# Important:
4-
# On Linux importing the cefpython module must be
5-
# the very first in your application. This is because CEF makes
6-
# a global tcmalloc hook for memory allocation/deallocation.
3+
# Important:
4+
# On Linux importing the cefpython module must be
5+
# the very first in your application. This is because CEF makes
6+
# a global tcmalloc hook for memory allocation/deallocation.
77
# See Issue 73 that is to provide CEF builds with tcmalloc disabled:
88
# https://code.google.com/p/cefpython/issues/detail?id=73
99

@@ -162,7 +162,7 @@ def PyPrint(message):
162162
class JavascriptExternal:
163163
mainBrowser = None
164164
stringVisitor = None
165-
165+
166166
def __init__(self, mainBrowser):
167167
self.mainBrowser = mainBrowser
168168

@@ -289,7 +289,7 @@ class ClientHandler:
289289
# DisplayHandler
290290
# -------------------------------------------------------------------------
291291

292-
def OnLoadingStateChange(self, browser, isLoading, canGoBack,
292+
def OnLoadingStateChange(self, browser, isLoading, canGoBack,
293293
canGoForward):
294294
print("DisplayHandler::OnLoadingStateChange()")
295295
print("isLoading = %s, canGoBack = %s, canGoForward = %s" \
@@ -304,7 +304,7 @@ def OnTitleChange(self, browser, title):
304304
print("title = %s" % title)
305305

306306
def OnTooltip(self, browser, textOut):
307-
# OnTooltip not yet implemented (both Linux and Windows),
307+
# OnTooltip not yet implemented (both Linux and Windows),
308308
# will be fixed in next CEF release, see Issue 783:
309309
# https://code.google.com/p/chromiumembedded/issues/detail?id=783
310310
print("DisplayHandler::OnTooltip()")
@@ -332,7 +332,7 @@ def OnConsoleMessage(self, browser, message, source, line):
332332
# KeyboardHandler
333333
# -------------------------------------------------------------------------
334334

335-
def OnPreKeyEvent(self, browser, event, eventHandle,
335+
def OnPreKeyEvent(self, browser, event, eventHandle,
336336
isKeyboardShortcutOut):
337337
print("KeyboardHandler::OnPreKeyEvent()")
338338

@@ -433,7 +433,7 @@ def _OnCertificateError(self, certError, requestUrl, callback):
433433
# -------------------------------------------------------------------------
434434
# LoadHandler
435435
# -------------------------------------------------------------------------
436-
436+
437437
def OnLoadStart(self, browser, frame):
438438
print("LoadHandler::OnLoadStart()")
439439
print("frame url = %s" % frame.GetUrl()[:70])
@@ -516,8 +516,11 @@ def OnExit(self):
516516
self.timer.Stop()
517517

518518
if __name__ == '__main__':
519+
# Intercept python exceptions. Exit app immediately when exception
520+
# happens on any of the threads.
519521
sys.excepthook = ExceptHook
520-
cefpython.g_debug = False
522+
# Set to False to disable cefpython debug messages in console.
523+
cefpython.g_debug = True
521524
cefpython.g_debugFile = GetApplicationPath("debug.log")
522525
settings = {
523526
"log_severity": cefpython.LOGSEVERITY_INFO, # LOGSEVERITY_VERBOSE

cefpython/cef3/windows/binaries/wxpython.py

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,11 @@
3636
See comment by Robin Dunn:
3737
https://groups.google.com/d/msg/wxpython-users/hcNdMEx8u48/MD5Jgbm_k1kJ
3838
-------------------------------------------------------------------------------
39-
EVT_IDLE events are not sent continuously while the application is idle.
40-
They are sent (normally once) when the app *becomes* idle, which
41-
usually means when the event queue has just been emptied. If you want
42-
EVT_IDLE events to be sent continuously then you need to call
43-
event.RequestMore() from the handler. Be careful however as that will
39+
EVT_IDLE events are not sent continuously while the application is idle.
40+
They are sent (normally once) when the app *becomes* idle, which
41+
usually means when the event queue has just been emptied. If you want
42+
EVT_IDLE events to be sent continuously then you need to call
43+
event.RequestMore() from the handler. Be careful however as that will
4444
cause your application to consume 100% of the CPU if there is no limits.
4545
-------------------------------------------------------------------------------
4646
"""
@@ -196,7 +196,7 @@ def PyPrint(message):
196196
class JavascriptExternal:
197197
mainBrowser = None
198198
stringVisitor = None
199-
199+
200200
def __init__(self, mainBrowser):
201201
self.mainBrowser = mainBrowser
202202

@@ -313,7 +313,7 @@ class ClientHandler:
313313
# DisplayHandler
314314
# -------------------------------------------------------------------------
315315

316-
def OnLoadingStateChange(self, browser, isLoading, canGoBack,
316+
def OnLoadingStateChange(self, browser, isLoading, canGoBack,
317317
canGoForward):
318318
print("DisplayHandler::OnLoadingStateChange()")
319319
print(" isLoading = %s, canGoBack = %s, canGoForward = %s" \
@@ -328,7 +328,7 @@ def OnTitleChange(self, browser, title):
328328
print(" title = %s" % title)
329329

330330
def OnTooltip(self, browser, textOut):
331-
# OnTooltip not yet implemented (both Linux and Windows),
331+
# OnTooltip not yet implemented (both Linux and Windows),
332332
# will be fixed in next CEF release, see Issue 783:
333333
# https://code.google.com/p/chromiumembedded/issues/detail?id=783
334334
print("DisplayHandler::OnTooltip()")
@@ -356,7 +356,7 @@ def OnConsoleMessage(self, browser, message, source, line):
356356
# KeyboardHandler
357357
# -------------------------------------------------------------------------
358358

359-
def OnPreKeyEvent(self, browser, event, eventHandle,
359+
def OnPreKeyEvent(self, browser, event, eventHandle,
360360
isKeyboardShortcutOut):
361361
print("KeyboardHandler::OnPreKeyEvent()")
362362

@@ -544,8 +544,11 @@ def OnExit(self):
544544
self.timer.Stop()
545545

546546
if __name__ == '__main__':
547+
# Intercept python exceptions. Exit app immediately when exception
548+
# happens on any of the threads.
547549
sys.excepthook = ExceptHook
548-
cefpython.g_debug = False
550+
# Set to False to disable cefpython debug messages in console.
551+
cefpython.g_debug = True
549552
cefpython.g_debugFile = GetApplicationPath("debug.log")
550553
settings = {}
551554
settings["log_file"] = GetApplicationPath("debug.log") # "" to disable

cefpython/cefpython.pyx

Lines changed: 37 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -53,16 +53,16 @@
5353
#
5454
# - Declaring C++ classes in Cython. Storing python callbacks
5555
# in a C++ class using Py_INCREF, Py_DECREF. Calling from
56-
# C++ using PyObject_CallMethod.
56+
# C++ using PyObject_CallMethod.
5757
# | http://stackoverflow.com/a/17070382/623622
58-
# Disadvantage: when calling python callback from the C++ class
58+
# Disadvantage: when calling python callback from the C++ class
5959
# declared in Cython there is no easy way to propagate the python
6060
# exceptions when they occur during execution of the callback.
6161
#
6262
# - | cdef char* other_c_string = py_string
63-
# This is a very fast operation after which other_c_string points
64-
# to the byte string buffer of the Python string itself. It is
65-
# tied to the life time of the Python string. When the Python
63+
# This is a very fast operation after which other_c_string points
64+
# to the byte string buffer of the Python string itself. It is
65+
# tied to the life time of the Python string. When the Python
6666
# string is garbage collected, the pointer becomes invalid.
6767
#
6868
# - Do not define cpdef functions returning "cpp_bool":
@@ -172,22 +172,20 @@ IF CEF_VERSION == 3:
172172
include "response_cef3.pyx"
173173
include "web_request_cef3.pyx"
174174

175+
# Linux issue:
176+
# ------------
175177
# Try not to run any of the CEF code until Initialize() is called.
176-
# Do not allocate any memory on the heap until Initialize() is called,
177-
# that's why we're not instantiating the ClientHandler class here in
178-
# the declaration. CEF hooks up its own tcmalloc globally when the
179-
# library is loaded, but the memory allocation implementation may still
180-
# be changed by another library (wx/gtk) before Initialize() is called.
181-
cdef CefRefPtr[ClientHandler] g_clientHandler
178+
# Do not allocate any memory on the heap until Initialize() is called.
179+
# CEF hooks up its own tcmalloc globally when the library is loaded,
180+
# but the memory allocation implementation may still be changed by
181+
# another library (wx/gtk) before Initialize() is called.
182+
# See Issue 73:
183+
# https://code.google.com/p/cefpython/issues/detail?id=73
182184

183185
def Initialize(applicationSettings=None):
184186
Debug("-" * 60)
185187
Debug("Initialize() called")
186188

187-
global g_clientHandler
188-
if not g_clientHandler.get():
189-
g_clientHandler = <CefRefPtr[ClientHandler]?>new ClientHandler()
190-
191189
cdef CefRefPtr[CefApp] cefApp
192190

193191
IF CEF_VERSION == 3:
@@ -198,7 +196,9 @@ def Initialize(applicationSettings=None):
198196
ELIF UNAME_SYSNAME == "Linux":
199197
# TODO: use the CefMainArgs(int argc, char** argv) constructor.
200198
cdef CefMainArgs cefMainArgs
201-
cdef int exitCode = CefExecuteProcess(cefMainArgs, cefApp)
199+
cdef int exitCode = 1
200+
with nogil:
201+
exitCode = CefExecuteProcess(cefMainArgs, cefApp)
202202
Debug("CefExecuteProcess(): exitCode = %s" % exitCode)
203203
if exitCode >= 0:
204204
sys.exit(exitCode)
@@ -224,11 +224,14 @@ def Initialize(applicationSettings=None):
224224
Debug("CefInitialize()")
225225
cdef cpp_bool ret
226226
IF CEF_VERSION == 1:
227-
ret = CefInitialize(cefApplicationSettings, cefApp)
227+
with nogil:
228+
ret = CefInitialize(cefApplicationSettings, cefApp)
228229
ELIF CEF_VERSION == 3:
229-
ret = CefInitialize(cefMainArgs, cefApplicationSettings, cefApp)
230+
with nogil:
231+
ret = CefInitialize(cefMainArgs, cefApplicationSettings, cefApp)
230232

231-
if not ret: Debug("CefInitialize() failed")
233+
if not ret:
234+
Debug("CefInitialize() failed")
232235
return ret
233236

234237
def CreateBrowserSync(windowInfo, browserSettings, navigateUrl):
@@ -251,10 +254,13 @@ def CreateBrowserSync(windowInfo, browserSettings, navigateUrl):
251254
PyToCefString(navigateUrl, cefNavigateUrl)
252255

253256
Debug("CefBrowser::CreateBrowserSync()")
254-
global g_clientHandler
255-
cdef CefRefPtr[CefBrowser] cefBrowser = cef_browser_static.CreateBrowserSync(
256-
cefWindowInfo, <CefRefPtr[CefClient]?>g_clientHandler, cefNavigateUrl,
257-
cefBrowserSettings)
257+
cdef CefRefPtr[ClientHandler] clientHandler =\
258+
<CefRefPtr[ClientHandler]?>new ClientHandler()
259+
cdef CefRefPtr[CefBrowser] cefBrowser
260+
with nogil:
261+
cefBrowser = cef_browser_static.CreateBrowserSync(
262+
cefWindowInfo, <CefRefPtr[CefClient]?>clientHandler,
263+
cefNavigateUrl, cefBrowserSettings)
258264

259265
if <void*>cefBrowser == NULL:
260266
Debug("CefBrowser::CreateBrowserSync() failed")
@@ -269,7 +275,7 @@ def CreateBrowserSync(windowInfo, browserSettings, navigateUrl):
269275
# Test whether process message sent before renderer thread is created
270276
# will be delivered - OK.
271277
# Debug("Sending 'CreateBrowserSync() done' message to the Renderer")
272-
# pyBrowser.SendProcessMessage(cef_types.PID_RENDERER,
278+
# pyBrowser.SendProcessMessage(cef_types.PID_RENDERER,
273279
# "CreateBrowserSync() done")
274280

275281
return pyBrowser
@@ -299,14 +305,18 @@ def SingleMessageLoop():
299305

300306
def QuitMessageLoop():
301307
Debug("QuitMessageLoop()")
302-
CefQuitMessageLoop()
308+
with nogil:
309+
CefQuitMessageLoop()
303310

304311
def Shutdown():
305312
Debug("Shutdown()")
306-
CefShutdown()
313+
with nogil:
314+
CefShutdown()
307315

308316
def SetOsModalLoop(py_bool modalLoop):
309-
CefSetOSModalLoop(bool(modalLoop))
317+
cdef cpp_bool cefModalLoop = bool(modalLoop)
318+
with nogil:
319+
CefSetOSModalLoop(cefModalLoop)
310320

311321
cpdef py_void SetGlobalClientCallback(py_string name, object callback):
312322
global g_globalClientCallbacks

cefpython/cython_includes/cef_app.pxd

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,18 @@ cdef extern from "include/cef_app.h":
2424
pass
2525

2626
IF CEF_VERSION == 3:
27-
cdef int CefExecuteProcess(CefMainArgs& args, CefRefPtr[CefApp] application)
27+
cdef int CefExecuteProcess(CefMainArgs& args, CefRefPtr[CefApp] application) nogil
2828

2929
IF CEF_VERSION == 1:
30-
cdef cpp_bool CefInitialize(CefSettings&, CefRefPtr[CefApp])
30+
cdef cpp_bool CefInitialize(CefSettings&, CefRefPtr[CefApp]) nogil
3131
ELIF CEF_VERSION == 3:
32-
cdef cpp_bool CefInitialize(CefMainArgs&, CefSettings&, CefRefPtr[CefApp])
32+
cdef cpp_bool CefInitialize(CefMainArgs&, CefSettings&, CefRefPtr[CefApp]) nogil
3333

3434
cdef void CefRunMessageLoop() nogil
3535
cdef void CefDoMessageLoopWork() nogil
36-
cdef void CefQuitMessageLoop()
37-
cdef void CefShutdown()
36+
cdef void CefQuitMessageLoop() nogil
37+
cdef void CefShutdown() nogil
3838

3939
IF CEF_VERSION == 3:
40-
cdef void CefSetOSModalLoop(cpp_bool osModalLoop)
40+
cdef void CefSetOSModalLoop(cpp_bool osModalLoop) nogil
4141

cefpython/cython_includes/cef_browser_static.pxd

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,13 @@ IF CEF_VERSION == 1:
2424
CefWindowInfo&,
2525
CefRefPtr[CefClient],
2626
CefString&,
27-
CefBrowserSettings&)
27+
CefBrowserSettings&) nogil
2828

2929
cdef CefRefPtr[CefBrowser] CreateBrowserSync(
3030
CefWindowInfo&,
3131
CefRefPtr[CefClient],
3232
CefString&,
33-
CefBrowserSettings&)
33+
CefBrowserSettings&) nogil
3434

3535
ELIF CEF_VERSION == 3:
3636

@@ -41,10 +41,10 @@ ELIF CEF_VERSION == 3:
4141
CefWindowInfo&,
4242
CefRefPtr[CefClient],
4343
CefString&,
44-
CefBrowserSettings&)
44+
CefBrowserSettings&) nogil
4545

4646
cdef CefRefPtr[CefBrowser] CreateBrowserSync(
4747
CefWindowInfo&,
4848
CefRefPtr[CefClient],
4949
CefString&,
50-
CefBrowserSettings&)
50+
CefBrowserSettings&) nogil

0 commit comments

Comments
 (0)
X Tutup