Skip to content

Make Tkagg blit thread safe #18565

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

Merged
merged 19 commits into from
Nov 2, 2020

Conversation

richardsheridan
Copy link
Contributor

@richardsheridan richardsheridan commented Sep 24, 2020

PR Summary

Closes #13293. (possibly?) Use tk.call to invoke tkinter's cross-thread signalling when necessary to safely blit from threads. A small amount of architectural changes were made in the hopes of attaining actual CPU parallelism with threads, but that was unsuccessful because of how Tk_PhotoPutBlock is written upstream.

Nevertheless this will prevent some silent crashes.

Downsides: I haven't done any work to prevent data races, and users that misuse the Tcl event system may run into issues discussed in python/cpython#21299

Needs a test.

PR Checklist

  • Has pytest style unit tests (and pytest passes).
  • Is Flake 8 compliant (run flake8 on changed files to check).
  • New features are documented, with examples if plot related.
  • Documentation is sphinx and numpydoc compliant (the docs should build without error).
  • Conforms to Matplotlib style conventions (install flake8-docstrings and pydocstyle<4 and run flake8 --docstring-convention=all).
  • New features have an entry in doc/users/next_whats_new/ (follow instructions in README.rst there).
  • API changes documented in doc/api/next_api_changes/ (follow instructions in README.rst there).

@richardsheridan
Copy link
Contributor Author

@xzores can you install matplotlib from this PR and see if it prevents your crashes?

pip install git+https://github.com/richardsheridan/matplotlib.git@tk_blit_thread_safe

@xzores
Copy link

xzores commented Sep 24, 2020

pip install git+https://github.com/richardsheridan/matplotlib.git@tk_blit_thread_safe

I found another way. I don't really want to undo my code, so I will have no way to test.

@richardsheridan richardsheridan changed the title Tk blit thread safe Make Tkagg blit thread safe Sep 24, 2020
@tacaswell
Copy link
Member

I'm always happy to be wrong about this sort of thing 😄

I assume that mpl_tk_blit can go away now?

@tacaswell tacaswell added this to the v3.4.0 milestone Sep 25, 2020
@anntzer
Copy link
Contributor

anntzer commented Sep 25, 2020

Indeed, this appears to work. But I think the logic should go into _backend_tk.blit instead, so that it also works for tkcairo (and mplcairo.tk :))?

@richardsheridan
Copy link
Contributor Author

richardsheridan commented Sep 25, 2020

It was a bit tricky to ensure that the right arguments connect with the right call to _tkagg.blit. Unfortunately PhotoImage is a kind of incomplete widget that doesn't have a register method or parent widget attribute, so it is necessary to pass in some other, unrelated widget.If anyone can think of a smarter way to get to the root widget, I'm open to it! nm i thought of one

@richardsheridan richardsheridan marked this pull request as ready for review September 25, 2020 20:35
@richardsheridan
Copy link
Contributor Author

My test creates some stderr spam from a ttk widget being destroyed late, but I can't for the life of me figure out how to fix it. It would also be nice to get coverage on the code where bbox is not none, but I don't think it should block this PR.

@richardsheridan
Copy link
Contributor Author

The stderr spam will probably look familiar to some:

can't invoke "event" command: application has been destroyed
    while executing
"event generate $w <<ThemeChanged>>"
    (procedure "ttk::ThemeChanged" line 6)
    invoked from within
"ttk::ThemeChanged"

It can be silenced by adding an update_idletasks as per #9956 but I will try a bit more to see if we can avoid that route.

@richardsheridan
Copy link
Contributor Author

Some interesting failures on travis. I'm not sure what webagg has to do with anything, but some segfaults happen on py38 that don't happen on py37. Possibly I can guard that section of the test from running on the deprecated backend = 'wx'?

current status: doesn't crash but also doesn't draw
when called from a thread
current status: Prone to races and hard-to-diagnose timeouts

(see python/cpython#21299)
current status: Relies on tkinter arcana, untested
current status: Relies on tkinter arcana, untested
drawing from a thread produces an intolerable flicker unless blank and blit are called from the same event in the main thread
@richardsheridan
Copy link
Contributor Author

@tacaswell @anntzer This PR is consistently green on CI now, if you want to have another look.

I left it up to some other enterprising hacker to fix wx or macosx if they need it and make a separate PR.

@@ -44,6 +44,28 @@ def _restore_foreground_window_at_end():
_c_internal_utils.Win32_SetForegroundWindow(foreground)


_blit_args = {}
# Initialize to a non-empty string that is not a Tcl command
_blit_tcl_name = "29345091836409813"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not directly initialize this to the final name here? e.g. "mpl_blit_" + uuid.uuid4().hex

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love this idea, I always thought the initial name was tacky but for some reason I never came up with this fix!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a quick question, didn't we have a bug report come by recently because we were using one of the uuid functions, which was using a "weak" shasum algorithm, so their security-restricted system couldn't compute it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In a pinch we could hardcode a uuid, they are universally unique after all...

@anntzer
Copy link
Contributor

anntzer commented Oct 27, 2020

Just some minor points, but mostly looks good.

Copy link
Contributor

@anntzer anntzer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

conditional on handling the comments above.

richardsheridan and others added 2 commits October 27, 2020 09:55
Co-authored-by: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com>
@timhoffm timhoffm merged commit 59bfcfa into matplotlib:master Nov 2, 2020
@richardsheridan richardsheridan deleted the tk_blit_thread_safe branch November 2, 2020 16:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

FigureCanvasTkAgg.draw() silently crashes in threaded application
7 participants