Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

plt.plot hangs #11

Open
jcguu95 opened this issue Feb 20, 2024 · 8 comments
Open

plt.plot hangs #11

jcguu95 opened this issue Feb 20, 2024 · 8 comments

Comments

@jcguu95
Copy link

jcguu95 commented Feb 20, 2024

In the following example given in README, my lisp hangs for more than 1 minute when I call (pycall "plt.plot" [elided]). I could not locate the cause. But I can tell that #'import-module works well, since if I skip pycall and just do (pycall "plt.show"), it works as expected. On the other hand, I have also tried to plt.plot in a python repl; it works fine as well.

PY4CL2-CFFI> (import-module "matplotlib.pyplot" :as "plt")
T
PY4CL2-CFFI> (pycall "plt.plot"
                     (iota 10)
                     (mapcar (lambda (x) (* x x))
                             (iota 10)))
#(#<PYTHON-OBJECT :type <class 'matplotlib.lines.Line2D'>
  Line2D(_line0)
 {1006670F83}>)
PY4CL2-CFFI> (pycall "plt.show")
#<PYTHON-OBJECT :type <class 'NoneType'>
  None
 {1006672273}>
@digikar99
Copy link
Owner

Can you confirm that running it straight from the REPL works fine, but running it from an emacs buffer causes lisp to hang? Can you check the output in *inferior-lisp* buffer?

I can confirm this issue and the cause seems to be simply that matplotlib does not play nice with multithreaded environments. In the future, I might provide an option to have a single-threaded python calling, so all calls to python (except perhaps the finalizers) will be made by the same thread that initiated it. This would avoid the issue.

For the time being, you can set (setf swank:*communication-style* nil) in ~/.swank.lisp and see if the issue is resolved.

@jcguu95
Copy link
Author

jcguu95 commented Feb 20, 2024

Thanks for the input. So after setting communication-style on the fly in the lisp repl, and call ply.plot (again in the lisp repl) hangs the lisp too. Here's the message in *inferior-lisp*

2024-02-20 06:52:25.299 sbcl[34754:6081902] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'NSWindow drag regions should only be invalidated on the Main Thread!'
*** First throw call stack:
(
)
libc++abi: terminating with uncaught exception of type NSException
fatal error encountered in SBCL pid 34754 pthread 0x16f95f000:
maximum interrupt nesting depth (8) exceeded

Error opening /dev/tty: Device not configured
Welcome to LDB, a low-level debugger for the Lisp runtime environment.
ldb> 

@digikar99
Copy link
Owner

So after setting communication-style on the fly in the lisp repl
...
NSWindow drag regions should only be invalidated on the Main Thread!

I'm not sure if the variable takes effect if set on the fly. Can you try setting it from ~/.swank.lisp?

@jcguu95
Copy link
Author

jcguu95 commented Feb 20, 2024

Setting it in ~/.slynk.lisp did solve the issue. Thank you!

@jcguu95 jcguu95 closed this as completed Feb 20, 2024
@digikar99
Copy link
Owner

I will leave the issue open until an option is added to call python from a single thread. It doesn't look too complicated; plus, changing the swank/slynk communication style doesn't look like a great solution. We want the freedom of lisp not the restrictions of python!

@digikar99 digikar99 reopened this Feb 20, 2024
@jcguu95
Copy link
Author

jcguu95 commented Feb 20, 2024

I guess calling python from a single thread has its drawback too, right? That's why you want to make it an option, but not default. If that's the case, how would the user know ahead of hanging that if they should switch to single-thread mode?

@digikar99
Copy link
Owner

I might end up making it the default; I will need to check how jupyter notebooks or other python environments behave though.

@digikar99
Copy link
Owner

Recent commits up to d3df012 (and any more recent) should have added preliminary support for single-threaded mode. This is essentially an alternate system/package "py4cl2-cffi/single-threaded".

This shadows a number of exported symbols from "py4cl2-cffi" and provides their single-threaded counterparts. The intended usage is that one calls (py4cl2-cffi/single-threaded:pystart) instead of (py4cl2-cffi:pystart). Following that, function and macro calls for symbols in the package py4cl2-cffi/single-threaded would communicate to python using a single thread.

Either of the two below should work now:

(defpackage :my-package
  (:use :cl)
  (:local-nicknames (:py :py4cl2-cffi/single-threaded)))

(in-package :my-package)

;; Option 1: Use directly
(py:import-module "matplotlib.pyplot" :as "plt")
(py:pycall "plt.plot" '(1 2 3) '(1 2 3))
(py:pycall "plt.show")

;; Option 2: Use through a lisp function and package
(py:defpymodule "matplotlib.pyplot" nil :lisp-package "PLT")
(plt:plot '(2 3 4) '(32 43 23))
(plt:show)

I'll add more tests in the future.

Barring any bugs, it should be backward compatible with any exisiting use of py4cl2-cffi, so long as the first call to (pystart) happens through (py4cl2-cffi/single-threaded:pystart) instead of (py4cl2-cffi:pystart).

I'm not making this default, because this has a slight performance penalty. In addition, multithreaded usage cannot be done away completely. Lisp garbage collection relies on finalizers which, at least on SBCL, can be called through any thread.

If that's the case, how would the user know ahead of hanging that if they should switch to single-thread mode?

Currently, the only option to know this is to know beforehand how one's python packages behave in multithreaded environments.

I will need to check how jupyter notebooks or other python environments behave though.

This still remains.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants