Skip to content

Conversation

Julow
Copy link
Contributor

@Julow Julow commented Jul 18, 2025

This changes the client-side API of Eliom_comet and Eliom_bus to be more compatible with direct-style programming:

  • Add a new callback-based API, Eliom_bus.register, which avoids using Lwt_stream internally.
    Eliom_bus.stream is re-implemented against the register API.
    Eliom_bus.original_stream is not re-implemented yet.

  • Eliom_comet.Channel is an abstract type instead of a Lwt_stream.
    Lwt_stream is difficult to migrate to other concurrency libraries because of its vast API and its many usecases.
    It's also callback-based but it's made to be easy to construct a Lwt_stream on top of it.

This might increase performances as well.

This was successfully tested against a small application that uses these APIs: Julow/ocsigen-tictactoe#1

Some work remains to be done:

  • API to unregister callbacks. It was common to cancel a listening loop on a channel using Lwt.cancel or Lwt.pick.
  • Simplify the implementation of Eliom_bus. For example, the callback list could possibly be removed and implemented directly against Eliom_comet.register. This change also opens the door for performance improvements and code simplifications in Eliom_comet.
  • Investigate the lack of Eliom_bus.original_stream. A comment in Eliom_comet says that messages start receiving only after the load phase, which makes it seems like it is exactly equal to Eliom_bus.stream. It seems that the new API have the same behavior during the load phase and will not lose more messages than the old one when registering callbacks outside of the load phase. This remains to be checked.

Julow added 8 commits July 15, 2025 16:11
`Lwt_stream` is difficult to migrate to other concurrency libraries
because of its vast API and its many usecases.

Its usage in `Eliom_comet` can easily be changed to a callback-based
approach. This might increase performances as well.
Add a new callback-based API, `Eliom_bus.register`, which avoids using
`Lwt_stream` internally.

`Eliom_bus.stream` is re-implemented against the register API.
`Eliom_bus.original_stream` is not re-implemented yet.
`Eliom_comet.register` and `Eliom_bus.register` propagate errors from
the server using an `option` type.
`Eliom_bus` ensures that the callback won't be called again after being
called with `None`.

This "end of stream or error" signal was present before through
`Lwt_stream`, which could propagate the `Closed` exception.

In both `Eliom_comet` and `Eliom_bus`, the callbacks are released when
the channel closes to allow memory to be collected.
Implement the callback-based API that can be used with values of type
[Eliom_comet.Channel.t] that can be passed from the server.
This simplifies the handling of messages in `Eliom_bus` by registering
all callbacks directly into the underlying `Eliom_comet`.

`Eliom_comet.register_wrapped` is removed in favor of
`Eliom_comet.register` in the previous API. This changes requires
signaling when the channel should be aware from `Eliom_bus`.
With the same intention as the previous commit, this simplifies the
implementation of `Eliom_bus`.

This removes the internal function `Eliom_comet.close` in favor of
`Eliom_comet.Channel.close`, which is much easier to implement.
Callbacks are no longer an ever-growing list of functions.
The changes are:

- Callbacks are grouped by channel ID in a Hashtbl. This dramatically
  reduces the number of callback that need to be considered.

- The channel position is stored along side the callback instead of in a
  closure. This will make writing a `unregister` function easier.

- `hd_error_callbacks` is removed. This was needed only to workaround
  the internal complexity.

Some code is moved for ordering. The `position` type and related code
are wrapped in a `Position` module to improve readability.
Add `Eliom_comet.Channel.unregister` and `Eliom_bus.unregister` to
unregister callbacks.
@Julow
Copy link
Contributor Author

Julow commented Jul 22, 2025

In the last 4 commits, I improve the code a lot and added the unregistration API. The test-case is uptodate: Julow/ocsigen-tictactoe#1

No request will be sent. *)

val close : 'a t -> unit
(** [close c] closes the channel c. This function should be only use
Copy link
Member

Choose a reason for hiding this comment

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

should only be used?

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 don't think this docstring is true anymore, there is no way to close the channel without also using Eliom_bus.
I made the close function public and fixed a potential memory leak in my last commit.

Julow added 2 commits July 24, 2025 11:34
There were no way to close a channel when using `Eliom_comet` alone.
This exposes the `close` function, which was internally used by
`Eliom_bus`. It's also changed to make sure that registered callbacks
are unregistered to avoid memory leaks.
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

Successfully merging this pull request may close these issues.

2 participants