Skip to content

Improve JupyUvi.stop and async waiting#879

Merged
jph00 merged 2 commits into
mainfrom
jupyuvi-wait
Jun 9, 2026
Merged

Improve JupyUvi.stop and async waiting#879
jph00 merged 2 commits into
mainfrom
jupyuvi-wait

Conversation

@kafkasl

@kafkasl kafkasl commented May 20, 2026

Copy link
Copy Markdown
Contributor

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

@kafkasl kafkasl requested a review from jph00 June 3, 2026 11:20
@jph00 jph00 merged commit 8d66f21 into main Jun 9, 2026
2 checks passed
@jph00 jph00 deleted the jupyuvi-wait branch June 9, 2026 14:03
@jph00 jph00 added enhancement New feature or request and removed bug Something isn't working labels Jun 9, 2026
@jph00 jph00 changed the title fixed await issues Improve JupyUvi.stop and async waiting Jun 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants