Commit 571cb737 authored by Kirill Smelkov's avatar Kirill Smelkov

fixup! X bigfile/_file_zodb: Fix logic around ZSync usage

Don't use regular mutex to protect _zsyncReg updates as this can
deadlock because one of _zsyncReg mutators (on_zconn_dealloc) is invoked
by automatic GC that can be triggered any time.
parent 066d7203
...@@ -58,7 +58,6 @@ from cpython cimport PyCapsule_New ...@@ -58,7 +58,6 @@ from cpython cimport PyCapsule_New
from ZODB.Connection import Connection as ZConnection from ZODB.Connection import Connection as ZConnection
from ZODB.utils import u64 from ZODB.utils import u64
from wendelin.lib.zodb import zconn_at from wendelin.lib.zodb import zconn_at
from golang import sync as pysync
import weakref import weakref
...@@ -159,8 +158,7 @@ cdef wcfs.PyConn pywconnOf(zconn): ...@@ -159,8 +158,7 @@ cdef wcfs.PyConn pywconnOf(zconn):
# back into DB pool). # back into DB pool).
# #
# ZSync cares itself to stay alive as long as zconn stays alive. # ZSync cares itself to stay alive as long as zconn stays alive.
_zsyncRegMu = pysync.Mutex() _zsyncReg = {} # id(zsync) -> zsync (protected by GIL)
_zsyncReg = {} # id(zsync) -> zsync
class ZSync: class ZSync:
# .zconn_ref weakref[zodb.Connection] # .zconn_ref weakref[zodb.Connection]
# .wconn (py) wcfs.Connection # .wconn (py) wcfs.Connection
...@@ -178,13 +176,19 @@ class ZSync: ...@@ -178,13 +176,19 @@ class ZSync:
zconn.onResyncCallback(zsync) zconn.onResyncCallback(zsync)
# keep zsync in _zsyncReg for zsync to stay alive independently of the caller. # keep zsync in _zsyncReg for zsync to stay alive independently of the caller.
with _zsyncRegMu: #
# NOTE we cannot use regular mutex to protect _zsyncReg updates because
# the other _zsyncReg mutator (on_zconn_dealloc) is invoked by
# automatic GC that can be triggered any time.
#
# on CPython dict updates are "atomic" - they happen without releasing GIL.
if 1: # = `with gil:` (GIL already held in python code)
_zsyncReg[id(zsync)] = zsync _zsyncReg[id(zsync)] = zsync
# .zconn dealloc -> wconn.close; release zsync # .zconn dealloc -> wconn.close; release zsync
def on_zconn_dealloc(zsync, _): def on_zconn_dealloc(zsync, _):
zsync.wconn.close() zsync.wconn.close()
with _zsyncRegMu: if 1: # = `with gil:` (see note in __init__)
del _zsyncReg[id(zsync)] del _zsyncReg[id(zsync)]
# DB resyncs .zconn onto new database view. # DB resyncs .zconn onto new database view.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment