Skip to content

Commit 35e9712

Browse files
committed
[fix] remember to release pygil before making the lisp thread wait
These should fix (seem to fix) a number of deadlocks on CCL.
1 parent 2b7c757 commit 35e9712

File tree

2 files changed

+29
-41
lines changed

2 files changed

+29
-41
lines changed

src/gil-gc.lisp

+17-4
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,25 @@
2626
(defun pyeval-restore-thread (thread-state)
2727
(foreign-funcall "PyEval_RestoreThread" :pointer thread-state))
2828

29+
(defvar *gil*)
30+
2931
(defmacro with-python-gil (&body body)
30-
(with-gensyms (gil)
31-
`(let* ((,gil (pygil-ensure)))
32+
`(let* ((*gil* (pygil-ensure)))
33+
(unwind-protect (locally ,@body)
34+
(python-may-be-error)
35+
(pygil-release *gil*))))
36+
37+
(defmacro without-python-gil (&body body)
38+
(with-gensyms (count)
39+
`(let ((,count (if (boundp '*gil*)
40+
(loop :while (pygil-held-p)
41+
:for ,count :from 0
42+
:do (pygil-release *gil*)
43+
:finally (return ,count))
44+
0)))
3245
(unwind-protect (locally ,@body)
33-
(python-may-be-error)
34-
(pygil-release ,gil)))))
46+
(loop :repeat ,count
47+
:do (setq *gil* (pygil-ensure)))))))
3548

3649
(define-constant +python-function-reference-type-alist+
3750
'(("Py_Initialize" nil)

src/python-process.lisp

+12-37
Original file line numberDiff line numberDiff line change
@@ -110,15 +110,17 @@ to the stream specified in WITH-PYTHON-*-OUTPUT "
110110
;; This is "START" in the sense that we start reading now.
111111
;; If the thunk has not finished executing and not flushed,
112112
;; then LISTEN will return NIL, and this will end
113-
(bt:wait-on-semaphore with-python-start-semaphore)
113+
(without-python-gil
114+
(bt:wait-on-semaphore with-python-start-semaphore))
114115
(loop :while (listen py-stream)
115116
:do (let ((char (read-char-no-hang py-stream nil)))
116117
(when char
117118
(write-char char with-python-stream))))
118119
;; Signal the WITH-PYTHON-*-OUTPUT to continue
119120
(bt:signal-semaphore with-python-end-semaphore)
120-
(bt:with-recursive-lock-held (with-python-count-lock)
121-
(decf in-with-python-count))
121+
(without-python-gil
122+
(bt:with-recursive-lock-held (with-python-count-lock)
123+
(decf in-with-python-count)))
122124
:else
123125
:do ;; PEEK-CHAR waits for input
124126
(peek-char nil py-stream nil)
@@ -144,13 +146,15 @@ execution of THUNK as a string."
144146
(unwind-protect
145147
(progn
146148
(setf with-python-stream (make-string-output-stream))
147-
(bt:with-recursive-lock-held (with-python-count-lock)
148-
(incf in-with-python-count))
149+
(without-python-gil
150+
(bt:with-recursive-lock-held (with-python-count-lock)
151+
(incf in-with-python-count)))
149152
(pycall (format nil "sys.~A.write" stderr-or-stdout) ".")
150153
(funcall thunk)
151154
(pycall (format nil "sys.~A.flush" stderr-or-stdout))
152155
(bt:signal-semaphore with-python-start-semaphore)
153-
(bt:wait-on-semaphore with-python-end-semaphore)
156+
(without-python-gil
157+
(bt:wait-on-semaphore with-python-end-semaphore))
154158
(setq all-signalled-p t)
155159
(let* ((output (get-output-stream-string with-python-stream))
156160
(len (length output)))
@@ -160,7 +164,8 @@ execution of THUNK as a string."
160164
(progn
161165
(unless all-signalled-p
162166
(bt:signal-semaphore with-python-start-semaphore)
163-
(bt:wait-on-semaphore with-python-end-semaphore))
167+
(without-python-gil
168+
(bt:wait-on-semaphore with-python-end-semaphore)))
164169
(setf with-python-stream old-with-python-stream-value))))))
165170

166171
;;; This is more of a global variable than a dynamic variable.
@@ -371,36 +376,6 @@ from inside PYTHON-MAY-BE-ERROR does not lead to an infinite recursion.")
371376
`(error 'pyerror))
372377
,pointer)))
373378

374-
#+ccl
375-
(defun raw-py (cmd-char &rest code-strings)
376-
"CMD-CHAR should be #\e for eval and #\x for exec.
377-
378-
Unlike PY4CL or PY4CL2, the use of RAW-PY, RAW-PYEVAL and RAW-PYEXEC,
379-
PYEVAL, PYEXEC should be avoided unless necessary.
380-
Instead, use PYCALL, PYVALUE, (SETF PYVALUE), PYSLOT-VALUE, (SETF PYSLOT-VALUE), and PYMETHOD.
381-
382-
RAW-PY, RAW-PYEVAL, RAW-PYEXEC are only provided for backward compatibility."
383-
(python-start-if-not-alive)
384-
(unless (zerop (pyforeign-funcall "PyRun_SimpleString"
385-
:string (apply #'concatenate
386-
'string
387-
(ecase cmd-char
388-
(#\e "_ = ")
389-
(#\x ""))
390-
code-strings)
391-
:int))
392-
(error 'pyerror
393-
:format-control "An unknown python error occurred.
394-
Unfortunately, no more information about the error can be provided
395-
while using RAW-PYEVAL or RAW-PYEXEC on ~A"
396-
:format-arguments (lisp-implementation-version)))
397-
(ecase cmd-char
398-
(#\e (let ((ptr (pyvalue* "_")))
399-
(pyforeign-funcall "Py_IncRef" :pointer ptr)
400-
(pytrack ptr)))
401-
(#\x (values))))
402-
403-
#-ccl
404379
(defun raw-py (cmd-char &rest code-strings)
405380
"CMD-CHAR should be #\e for eval and #\x for exec.
406381

0 commit comments

Comments
 (0)