On Fri, Jul 21, 2023 at 10:20:49AM +0000, Tage Johansson wrote:
On 7/21/2023 12:05 PM, Richard W.M. Jones wrote:
>On Fri, Jul 21, 2023 at 09:58:52AM +0000, Tage Johansson wrote:
>>On 7/19/2023 4:47 PM, Richard W.M. Jones wrote:
>>
>> On Wed, Jul 19, 2023 at 09:09:53AM +0000, Tage Johansson wrote:
>>
>> Create another handle type: `AsyncHandle`, which makes use of
Rust's
>> builtin asynchronous functions (see
>> <
https://doc.rust-lang.org/std/keyword.async.html>) and runs on
top of
>> the Tokio runtime (see <
https://docs.rs/tokio>). For every
asynchronous
>> command, like `aio_connect()`, a corresponding `async` method is
created
>> on the handle. In this case it would be:
>> async fn connect(...) -> Result<(), ...>
>> When called, it will poll the file descriptor until the command is
>> complete, and then return with a result. All the synchronous
>> counterparts (like `nbd_connect()`) are excluded from this handle type
>> as they are unnecessary and since they might interfear with the polling
>> made by the Tokio runtime. For more details about how the asynchronous
>> commands are executed, please see the comments in
>> rust/src/async_handle.rs.
>>
>> This is an interesting (and good) approach, layering a more natural
>> API for Rust users on top of the "low level" API.
>>
>>
>> ---
>> generator/Rust.ml | 232 +++++++++++++++++++++++++++++++++++++++
>> generator/Rust.mli | 2 +
>> generator/generator.ml | 1 +
>> rust/Cargo.toml | 1 +
>> rust/Makefile.am | 1 +
>> rust/src/async_handle.rs | 222 +++++++++++++++++++++++++++++++++++++
>> rust/src/lib.rs | 4 +
>> 7 files changed, 463 insertions(+)
>> create mode 100644 rust/src/async_handle.rs
>>
>> diff --git a/generator/Rust.ml b/generator/Rust.ml
>> index 96095a9..1048831 100644
>> --- a/generator/Rust.ml
>> +++ b/generator/Rust.ml
>> @@ -601,3 +601,235 @@ let generate_rust_bindings () =
>> pr "impl Handle {\n";
>> List.iter print_rust_handle_method handle_calls;
>> pr "}\n\n"
>> +
>> +(*********************************************************)
>> +(* The rest of the file conserns the asynchronous API. *)
>> +(* *)
>> +(* See the comments in rust/src/async_handle.rs for more *)
>> +(* information about how it works. *)
>> +(*********************************************************)
>> +
>> +let excluded_handle_calls : NameSet.t =
>> + NameSet.of_list
>> + [
>> + "aio_get_fd";
>> + "aio_get_direction";
>> + "aio_notify_read";
>> + "aio_notify_write";
>> + "clear_debug_callback";
>> + "get_debug";
>> + "poll";
>> + "poll2";
>> + "set_debug";
>> + "set_debug_callback";
>> + ]
>>
>> This is a "code smell" since all information that is specific to
APIs
>> should (usually) go in generator/API.ml.
>>
>> It's reasonable for aio_get_{fd,direction} aio_notify_{read,write}
>> since those are very special APIs, but I don't understand why the poll
>> and debug functions are listed here. What's the real meaning of this
>> list of functions, ie. why can each one not be included in the tokio
>> bindings?
>>
>>
>>The debug functions are used for setting up logging with the log crate in rust/
>>src/handle.rs:
>Interesting .. Is this used for all libnbd Rust handles?
Yes, both for `libnbd::Handle` and `libnbd::AsyncHandle`.
`libnbd::AsyncHandle` is built ontop of `libnbd::Handle`.
>>```Rust
>>
>>impl Handle {
>> pub fn new() -> Result<Self> {
>> let handle = unsafe { sys::nbd_create() };
>> if handle.is_null() {
>> Err(unsafe { Error::Fatal(ErrorKind::get_error().into()) })
>> } else {
>> // Set a debug callback communicating with any logging
>> // implementation as defined by the log crate.
>> #[allow(unused_mut)]
>> let mut nbd = Handle { handle };
>> #[cfg(feature = "log")]
>> {
>> nbd.set_debug_callback(|func_name, msg| {
>> log::debug!(
>> target: func_name.to_string_lossy().as_ref(),
>> "{}",
>> msg.to_string_lossy()
>> );
>> 0
>> })?;
>> nbd.set_debug(true)?;
>> }
>> Ok(nbd)
>> }
>> }
>>
>>```
>>
>>
>>So if the user would set another debug callback this functionality would stop
>>working. And since the documentation is automaticly generated for
>>`nbd_set_debug_callback()`, the user might not expect that logging just stops
>>working because they set a debug callback. But I guess that I can add a note
>>about that to the documentation of the `Handle` struct and include the debug
>>callbacks, if you want?
>I'm a bit unclear if the debugging is necessary or not. In the other
>bindings we don't set up a log handler by default, it just works the
>same way as the C API.
>
>>Regarding the other methods, it's a bit more complicated. The
>>problem is that calling any `aio_notify_*` method might cause any
>>ongoing command to finish. For example, the `connect()` method on
>>`AsyncHandle` will not return until `aio_is_connecting()` returns
>>false. In order not to have to check that in a busy loop, the
>>function is woken up only when the polling task makes a call to any
>>`aio_notify_*` function. So whenever the polling task calls
>>`aio_notify_*`, it notifies the waiting command that the handle
>>might has changed state. But if the user calls `aio_notify_*`
>>(directly or indirectly) while `aio_connect` is in flight, it might
>>happen that the connection completes without the call to `connect()`
>>gets notified about that.
>>
>>This means that any function that calls any `aio_notify_*` function
>>should not be implemented on `AsyncHandle`. A better approach than
>>the current would probably be to add a field to the `call`-structure
>>in API.ml. Something like `notifies_fd : bool` (sure someone can
>>come up with a better name). This would be set to true for any
>>function that calls `aio_notify_*`, including `aio_notify_read()`,
>>`poll()`, and all synchronous commands like `nbd_connect() `. What
>>do you think about that?
>Yup, it was already clear to me why these wouldn't work after I
>thought about it a bit more.
>
>My issue for maintenance is that if we add a new "poll-like" function
>to the API then the Rust bindings would need a special change.
Sorry, but what do you mean by "would need a special change"?
Say we added "poll3" or something, we'd need to also remember to
change this list in the Rust bindings.
Rich.
Best regards,
Tage
>>I have written some more explonations about how the asynchronous
>>handle works in rust/src/async_handle.rs, but it is somewhat
>>complicated so please ask if there are more unclear things.
>Sure, thanks!
>
>Rich.
>
--
Richard Jones, Virtualization Group, Red Hat
http://people.redhat.com/~rjones
Read my programming and virtualization blog:
http://rwmj.wordpress.com
nbdkit - Flexible, fast NBD server with plugins
https://gitlab.com/nbdkit/nbdkit