[Libguestfs] [PATCH] Rust bindings: Implement Event features
Martin Kletzander
mkletzan at redhat.com
Tue Jul 30 08:52:53 UTC 2019
On Tue, Jul 30, 2019 at 02:51:37PM +0900, Hiroyuki Katsura wrote:
>This patch includes:
>
>- Event callback handlers
>- Tests related to events(410-430)
>---
> generator/rust.ml | 38 ++++++-
> rust/src/base.rs | 24 +++--
> rust/src/error.rs | 8 +-
> rust/src/event.rs | 158 ++++++++++++++++++++++++++++
> rust/src/lib.rs | 2 +
> rust/tests/040_create_multiple.rs | 2 +-
> rust/tests/410_close_event.rs | 38 +++++++
> rust/tests/420_log_messages.rs | 60 +++++++++++
> rust/tests/430_progress_messages.rs | 61 +++++++++++
> 9 files changed, 381 insertions(+), 10 deletions(-)
> create mode 100644 rust/src/event.rs
> create mode 100644 rust/tests/410_close_event.rs
> create mode 100644 rust/tests/420_log_messages.rs
> create mode 100644 rust/tests/430_progress_messages.rs
>
[...]
>diff --git a/rust/src/event.rs b/rust/src/event.rs
>new file mode 100644
>index 000000000..942feec69
>--- /dev/null
>+++ b/rust/src/event.rs
>@@ -0,0 +1,158 @@
>+/* libguestfs Rust bindings
>+ * Copyright (C) 2019 Hiroyuki Katsura <hiroyuki.katsura.0513 at gmail.com>
>+ *
>+ * This library is free software; you can redistribute it and/or
>+ * modify it under the terms of the GNU Lesser General Public
>+ * License as published by the Free Software Foundation; either
>+ * version 2 of the License, or (at your option) any later version.
>+ *
>+ * This library is distributed in the hope that it will be useful,
>+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
>+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
>+ * Lesser General Public License for more details.
>+ *
>+ * You should have received a copy of the GNU Lesser General Public
>+ * License along with this library; if not, write to the Free Software
>+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
>+ */
>+
>+use crate::base;
>+use crate::error;
>+use crate::guestfs;
>+use std::ffi;
>+use std::os::raw::{c_char, c_void};
>+use std::slice;
>+use std::sync;
>+
>+type GuestfsEventCallback = extern "C" fn(
>+ *const base::guestfs_h,
>+ *const c_void,
>+ u64,
>+ i32,
>+ i32,
>+ *const i8,
>+ usize,
>+ *const u64,
>+ usize,
>+);
>+
>+#[link(name = "guestfs")]
>+extern "C" {
>+ fn guestfs_set_event_callback(
>+ g: *const base::guestfs_h,
>+ cb: GuestfsEventCallback,
>+ event_bitmask: u64,
>+ flags: i32,
>+ opaque: *const c_void,
>+ ) -> i32;
>+ fn guestfs_delete_event_callback(g: *const base::guestfs_h, eh: i32);
>+ fn guestfs_event_to_string(bitmask: u64) -> *const c_char;
>+ fn free(buf: *const c_void);
>+}
>+
>+#[derive(Hash, PartialEq, Eq)]
>+pub struct EventHandle {
>+ eh: i32,
>+}
>+
>+pub type Callback = FnMut(guestfs::Event, EventHandle, &[i8], &[u64]);
>+
>+fn events_to_bitmask(v: &[guestfs::Event]) -> u64 {
>+ let mut r = 0u64;
>+ for x in v.iter() {
>+ r |= x.to_u64();
>+ }
>+ r
>+}
>+
>+pub fn event_to_string(events: &[guestfs::Event]) -> Result<String, error::Error> {
>+ let bitmask = events_to_bitmask(events);
>+
>+ let r = unsafe { guestfs_event_to_string(bitmask) };
>+ if r.is_null() {
>+ Err(error::unix_error("event_to_string"))
>+ } else {
>+ let s = unsafe { ffi::CStr::from_ptr(r) };
>+ let s = s.to_str()?.to_string();
>+ unsafe { free(r as *const c_void) };
>+ Ok(s)
>+ }
>+}
>+
>+/* -- Why Not Box<Callback> but Arc<Callback> (in struct base::Handle)? --
>+ * Assume that there are more than threads. While callback is running,
>+ * if a thread frees the handle, automatically the buffer is freed if Box<Callback>
>+ * is used. Therefore Arc<Callback> is used.
>+ */
>+
>+impl<'a> base::Handle<'a> {
>+ pub fn set_event_callback<C: 'a>(
>+ &mut self,
>+ callback: C,
>+ events: &[guestfs::Event],
>+ ) -> Result<EventHandle, error::Error>
>+ where
>+ C: Fn(guestfs::Event, EventHandle, &[u8], &[u64]),
>+ {
>+ extern "C" fn trampoline<C>(
>+ _g: *const base::guestfs_h,
>+ opaque: *const c_void,
>+ event: u64,
>+ event_handle: i32,
>+ _flags: i32,
>+ buf: *const c_char,
>+ buf_len: usize,
>+ array: *const u64,
>+ array_len: usize,
>+ ) where
>+ C: Fn(guestfs::Event, EventHandle, &[u8], &[u64]),
>+ {
>+ // trampoline function
>+ // c.f. https://s3.amazonaws.com/temp.michaelfbryan.com/callbacks/index.html
>+
>+ let event = match guestfs::Event::from_bitmask(event) {
>+ Some(x) => x,
>+ None => panic!("Failed to parse bitmask: {}", event),
>+ };
>+ let eh = EventHandle { eh: event_handle };
>+ let buf = unsafe { slice::from_raw_parts(buf as *const u8, buf_len) };
>+ let array = unsafe { slice::from_raw_parts(array, array_len) };
>+
>+ let callback_ptr = unsafe { &*(opaque as *const sync::Arc<C>) };
>+ let callback = sync::Arc::clone(&callback_ptr);
>+ callback(event, eh, buf, array)
>+ }
>+ let callback = sync::Arc::<C>::new(callback);
>+ let event_bitmask = events_to_bitmask(events);
>+
>+ let eh = {
>+ // Weak::into_raw is nightly.
>+ // In order to make sure that callback is freed when handle is freed,
>+ // lifetime is explicitly declared.
>+ let ptr: &'a sync::Arc<C> = Box::leak(Box::new(callback.clone()));
So this has a lifetime same as the whole handle? Why do you then need to keep
it in the list of callbacks then? Also you explicitly leak it here, so even
without a lifetime it will not get freed. Are you sure you wanted to do both?
Also if you do Box::into_raw instead of Box::leak, you get a *mut right away
that you do not need to cast twice, I believe. Otherwise it should be the same,
I think.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: not available
URL: <http://listman.redhat.com/archives/libguestfs/attachments/20190730/a731a955/attachment.sig>
More information about the Libguestfs
mailing list