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

[FEATURE] Make it possible to write code without excessive usage of document callbacks #9669

Open
p-himik opened this issue Feb 15, 2020 · 1 comment

Comments

@p-himik
Copy link
Contributor

p-himik commented Feb 15, 2020

Is your feature request related to a problem? Please describe.
Consider this simple code:

import asyncio

from bokeh.io import curdoc
from bokeh.layouts import column
from bokeh.models import Div, Button

d = Div(text="")
b = Button(label="Click me")


async def cb():
    d.text = "started"
    await asyncio.sleep(5)
    d.text = "finished"


def on_click(event):
    curdoc().add_next_tick_callback(cb)


b.on_click(on_click)

curdoc().add_root(column(d, b))

It looks great - there's nothing to simplify (except for maybe that unused event argument, but that's another story).
The issue is that when you press the button, there's no "started" text displayed. The document stays locked for the whole duration of cb, no updates get out during the time of its execution.

Describe the solution you'd like
The solution is to not lock the document for the duration of the document callbacks. Hard to say how exactly it could be implemented though, but it would be great to make the above code work.

Another, a bit more cumbersome, solution is to manually trigger sending of patches. Something like

async def cb():
    d.text = "started"
    curdoc().synchronize()
    await asyncio.sleep(5)
    d.text = "finished"

Yet another, an even more cumbersome one, is to not lock the callback and manually lock everything that needs locking:

@without_document_lock
async def cb():
    with await curdoc().lock():
        d.text = "started"
    await asyncio.sleep(5)
    with await curdoc().lock():
        d.text = "finished"

Describe alternatives you've considered
The only solution right now is to find the points in code where the document updates should be sent, split the code and those points, and reorganize the pieces in such a way so that you constantly call doc.add_next_tick_callback() on them to make sure that the document synchronization happens between the callbacks, where the document is not locked.

@p-himik p-himik added the TRIAGE label Feb 15, 2020
@p-himik
Copy link
Contributor Author

p-himik commented Feb 16, 2020

It seems that locking is used when:

  1. The document is pulled
  2. The document is pushed
  3. The document is patched
  4. The session is discarded
  5. A document callback is called

I can see how locking might be useful for (1), (2), (3), and (4). The irony is only (5) can be asynchronous. (1-4) don't use async at all, only the code that calls such operations, before locking.
For (5) scenarios with both locking and not locking could be useful. However, I would argue that if a user uses async code then by default there should be no locking since it's a status quo everywhere else. At least, I have never seen potentially critical sections guarded by locks by default. With that being said, I would much rather have something like an optional with await document.lock() being available to the users that really need it.

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

No branches or pull requests

2 participants