Skip to content

fixed await issues#879

Open
kafkasl wants to merge 2 commits into
mainfrom
jupyuvi-wait
Open

fixed await issues#879
kafkasl wants to merge 2 commits into
mainfrom
jupyuvi-wait

Conversation

@kafkasl
Copy link
Copy Markdown
Contributor

@kafkasl kafkasl commented May 20, 2026

The main goal of this PR is to fix some race conditions when calling JupyUvi.stop. I often get address already busy despite doing something like:

if 'server' in globals(): server.stop()
server.start()

It also addresses sync/async interference during shutdown by splitting cleanly those classes and start/stop methods.

Issues

The main issues were:

  1. Fixed host mismatch on stop checks: stop() now passes self.host into wait_port_free. We were waiting by default to localhost while uvicorn starts on 0.0.0.0. Localhost interface can be free for biding "bindable" to a given port, while 0.0.0.0 (which requires ALL interfaces free) is still busy.
  2. Made wait_port_free fail fast and explicit instead of silently printing: it now raises TimeoutError after timeout. The rationale is that whenever the stop wait fails, it is silently ignored and errors surface later which is more annoying.
  3. Tightened port probe semantics (bind + listen and SO_REUSEADDR) so stop/restart logic tests a server-like
    listenability condition, not just socket creation.

All of this is explored and demonstrated with examples in this dialog https://share.solveit.pub/d/346001068041d03a6fd2c0b328473fa9

Sync vs Async interfaces

In addition, I personally was a bit confused by the sync / async options. So I refactored. I am happy to change this as it introduces some breaking changes in the interface.

The motivation was:

  - JupyUvi = “I want fire-and-forget server startup in notebooks or scripts” (threaded, synchronous API).
  - JupyUviAsync = “I’m already inside an event loop” (async start/stop, no extra thread).

Mixing the two lead to another subtle bug:

nb_serve_async runs in the notebook loop, but it used a blocking sync stop path (time.sleep vs asyncio.sleep) which prevented the shutdown coroutine work from progressing thus blocking uvicorn's shutdown work.

My solution was to split sync / async classes, and make the async.stop use asyncio.sleep.

Notes

There's some extra output changes in the notebook due to the recent changes in SolveIT output rendering, I assume that is fine, but happy to surgically remove them if it is a bother

@kafkasl kafkasl self-assigned this May 20, 2026
@kafkasl kafkasl added the bug Something isn't working label May 20, 2026
Comment thread nbs/api/06_jupyter.ipynb
"outputs": [],
"source": [
"# Run the notebook locally to see the HTMX iframe in action\n",
"HTMX()"
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I have commented this because it is designed to be run locally only, and completely blocks solveit. Happy to revert if you disagree

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

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant