Skip to content

Commit 6830cac

Browse files
committed
gio: Add bindings for Cancellable::connect and disconnect
1 parent 01cedf4 commit 6830cac

File tree

5 files changed

+126
-54
lines changed

5 files changed

+126
-54
lines changed

gio/Gir.toml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,15 @@ generate_builder = true
327327
name = "Gio.Cancellable"
328328
status = "generate"
329329
concurrency = "send+sync"
330+
manual_traits = ["CancellableExtManual"]
331+
[[object.function]]
332+
name = "connect"
333+
# Needs manual bindings for closures
334+
ignore = true
335+
[[object.function]]
336+
name = "disconnect"
337+
# Needs manual bindings for CancelledHandlerId
338+
ignore = true
330339
[[object.function]]
331340
name = "reset"
332341
#undefined behaviour
@@ -335,6 +344,10 @@ concurrency = "send+sync"
335344
name = "source_new"
336345
# Needs manual bindings for setting callback, etc
337346
ignore = true
347+
[[object.signal]]
348+
name = "cancelled"
349+
# Racy, use 'connect' and 'disconnect' instead
350+
ignore = true
338351

339352
[[object]]
340353
name = "Gio.CharsetConverter"

gio/src/auto/cancellable.rs

Lines changed: 0 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,9 @@
22
// from gir-files (https://github.com/gtk-rs/gir-files)
33
// DO NOT EDIT
44

5-
use glib::object::Cast;
65
use glib::object::IsA;
7-
use glib::signal::connect_raw;
8-
use glib::signal::SignalHandlerId;
96
use glib::translate::*;
10-
use std::boxed::Box as Box_;
117
use std::fmt;
12-
use std::mem::transmute;
138
use std::ptr;
149

1510
glib::wrapper! {
@@ -49,12 +44,6 @@ pub trait CancellableExt: 'static {
4944
#[doc(alias = "g_cancellable_cancel")]
5045
fn cancel(&self);
5146

52-
//#[doc(alias = "g_cancellable_connect")]
53-
//fn connect<P: Fn() + Send + Sync + 'static>(&self, callback: P, data: /*Unimplemented*/Option<Fundamental: Pointer>) -> libc::c_ulong;
54-
55-
#[doc(alias = "g_cancellable_disconnect")]
56-
fn disconnect(&self, handler_id: libc::c_ulong);
57-
5847
#[doc(alias = "g_cancellable_get_fd")]
5948
#[doc(alias = "get_fd")]
6049
fn fd(&self) -> i32;
@@ -76,9 +65,6 @@ pub trait CancellableExt: 'static {
7665

7766
#[doc(alias = "g_cancellable_set_error_if_cancelled")]
7867
fn set_error_if_cancelled(&self) -> Result<(), glib::Error>;
79-
80-
#[doc(alias = "cancelled")]
81-
fn connect_cancelled<F: Fn(&Self) + Send + Sync + 'static>(&self, f: F) -> SignalHandlerId;
8268
}
8369

8470
impl<O: IsA<Cancellable>> CancellableExt for O {
@@ -88,16 +74,6 @@ impl<O: IsA<Cancellable>> CancellableExt for O {
8874
}
8975
}
9076

91-
//fn connect<P: Fn() + Send + Sync + 'static>(&self, callback: P, data: /*Unimplemented*/Option<Fundamental: Pointer>) -> libc::c_ulong {
92-
// unsafe { TODO: call ffi:g_cancellable_connect() }
93-
//}
94-
95-
fn disconnect(&self, handler_id: libc::c_ulong) {
96-
unsafe {
97-
ffi::g_cancellable_disconnect(self.as_ref().to_glib_none().0, handler_id);
98-
}
99-
}
100-
10177
fn fd(&self) -> i32 {
10278
unsafe { ffi::g_cancellable_get_fd(self.as_ref().to_glib_none().0) }
10379
}
@@ -147,30 +123,6 @@ impl<O: IsA<Cancellable>> CancellableExt for O {
147123
}
148124
}
149125
}
150-
151-
fn connect_cancelled<F: Fn(&Self) + Send + Sync + 'static>(&self, f: F) -> SignalHandlerId {
152-
unsafe extern "C" fn cancelled_trampoline<
153-
P: IsA<Cancellable>,
154-
F: Fn(&P) + Send + Sync + 'static,
155-
>(
156-
this: *mut ffi::GCancellable,
157-
f: glib::ffi::gpointer,
158-
) {
159-
let f: &F = &*(f as *const F);
160-
f(Cancellable::from_glib_borrow(this).unsafe_cast_ref())
161-
}
162-
unsafe {
163-
let f: Box_<F> = Box_::new(f);
164-
connect_raw(
165-
self.as_ptr() as *mut _,
166-
b"cancelled\0".as_ptr() as *const _,
167-
Some(transmute::<_, unsafe extern "C" fn()>(
168-
cancelled_trampoline::<Self, F> as *const (),
169-
)),
170-
Box_::into_raw(f),
171-
)
172-
}
173-
}
174126
}
175127

176128
impl fmt::Display for Cancellable {

gio/src/cancellable.rs

Lines changed: 112 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,117 @@
11
// Take a look at the license at the top of the repository in the LICENSE file.
22

33
use crate::prelude::*;
4+
use crate::Cancellable;
5+
use glib::{translate::*, IsA};
6+
use std::num::NonZeroU64;
47

5-
#[test]
6-
fn check_callback() {
7-
let c = crate::Cancellable::new();
8-
c.connect_cancelled(|_| {});
9-
c.cancel(); // if it doesn't crash at this point, then we're good to go!
8+
// rustdoc-stripper-ignore-next
9+
/// The id of a cancelled handler that is returned by `CancellableExtManual::connect`. This type is
10+
/// analogous to [`glib::SignalHandlerId`].
11+
#[derive(Debug, Eq, PartialEq)]
12+
#[repr(transparent)]
13+
pub struct CancelledHandlerId(NonZeroU64);
14+
15+
impl CancelledHandlerId {
16+
// rustdoc-stripper-ignore-next
17+
/// Returns the internal signal handler ID.
18+
pub unsafe fn as_raw(&self) -> libc::c_ulong {
19+
self.0.get() as libc::c_ulong
20+
}
21+
}
22+
23+
impl TryFromGlib<libc::c_ulong> for CancelledHandlerId {
24+
type Error = GlibNoneError;
25+
#[inline]
26+
unsafe fn try_from_glib(val: libc::c_ulong) -> Result<Self, GlibNoneError> {
27+
NonZeroU64::new(val as u64).map(Self).ok_or(GlibNoneError)
28+
}
29+
}
30+
31+
pub trait CancellableExtManual {
32+
// rustdoc-stripper-ignore-next
33+
/// Convenience function to connect to the `signal::Cancellable::cancelled` signal. Also
34+
/// handles the race condition that may happen if the cancellable is cancelled right before
35+
/// connecting. If the operation is cancelled from another thread, `callback` will be called
36+
/// in the thread that cancelled the operation, not the thread that is running the operation.
37+
/// This may be the main thread, so the callback should not do something that can block.
38+
///
39+
/// `callback` is called at most once, either directly at the time of the connect if `self` is
40+
/// already cancelled, or when `self` is cancelled in some thread.
41+
///
42+
/// Since GLib 2.40, the lock protecting `self` is not held when `callback` is invoked. This
43+
/// lifts a restriction in place for earlier GLib versions which now makes it easier to write
44+
/// cleanup code that unconditionally invokes e.g.
45+
/// [`CancellableExt::cancel()`][crate::prelude::CancellableExt::cancel()].
46+
///
47+
/// # Returns
48+
///
49+
/// The id of the signal handler or `None` if `self` has already been cancelled.
50+
#[doc(alias = "g_cancellable_connect")]
51+
fn connect_cancelled<F: FnOnce(&Self) + Send + 'static>(
52+
&self,
53+
callback: F,
54+
) -> Option<CancelledHandlerId>;
55+
// rustdoc-stripper-ignore-next
56+
/// Disconnects a handler from a cancellable instance. Additionally, in the event that a signal
57+
/// handler is currently running, this call will block until the handler has finished. Calling
58+
/// this function from a callback registered with [`Self::connect_cancelled`] will therefore
59+
/// result in a deadlock.
60+
///
61+
/// This avoids a race condition where a thread cancels at the same time as the cancellable
62+
/// operation is finished and the signal handler is removed.
63+
#[doc(alias = "g_cancellable_disconnect")]
64+
fn disconnect_cancelled(&self, id: CancelledHandlerId);
65+
}
66+
67+
impl<O: IsA<Cancellable>> CancellableExtManual for O {
68+
#[doc(alias = "g_cancellable_connect")]
69+
fn connect_cancelled<F: FnOnce(&Self) + Send + 'static>(
70+
&self,
71+
callback: F,
72+
) -> Option<CancelledHandlerId> {
73+
unsafe extern "C" fn connect_trampoline<P: IsA<Cancellable>, F: FnOnce(&P)>(
74+
this: *mut ffi::GCancellable,
75+
callback: glib::ffi::gpointer,
76+
) {
77+
let callback: &mut Option<F> = &mut *(callback as *mut Option<F>);
78+
let callback = callback
79+
.take()
80+
.expect("Cancellable::cancel() closure called multiple times");
81+
callback(Cancellable::from_glib_borrow(this).unsafe_cast_ref())
82+
}
83+
84+
unsafe extern "C" fn destroy_closure<F>(ptr: glib::ffi::gpointer) {
85+
Box::<Option<F>>::from_raw(ptr as *mut _);
86+
}
87+
88+
let callback: Box<Option<F>> = Box::new(Some(callback));
89+
unsafe {
90+
from_glib(ffi::g_cancellable_connect(
91+
self.as_ptr() as *mut _,
92+
Some(std::mem::transmute::<_, unsafe extern "C" fn()>(
93+
connect_trampoline::<Self, F> as *const (),
94+
)),
95+
Box::into_raw(callback) as *mut _,
96+
Some(destroy_closure::<F>),
97+
))
98+
}
99+
}
100+
#[doc(alias = "g_cancellable_disconnect")]
101+
fn disconnect_cancelled(&self, id: CancelledHandlerId) {
102+
unsafe { ffi::g_cancellable_disconnect(self.as_ptr() as *mut _, id.as_raw()) };
103+
}
104+
}
105+
106+
#[cfg(test)]
107+
mod tests {
108+
use super::*;
109+
110+
#[test]
111+
fn cancellable_callback() {
112+
let c = Cancellable::new();
113+
let id = c.connect_cancelled(|_| {});
114+
c.cancel(); // if it doesn't crash at this point, then we're good to go!
115+
c.disconnect_cancelled(id.unwrap());
116+
}
10117
}

gio/src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ pub use glib;
1414

1515
mod app_info;
1616
mod application;
17-
#[cfg(test)]
1817
mod cancellable;
1918
mod converter;
2019
mod data_input_stream;

gio/src/prelude.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ pub use crate::auto::traits::*;
1111
#[cfg(any(feature = "v2_60", feature = "dox"))]
1212
pub use crate::app_info::AppInfoExtManual;
1313
pub use crate::application::*;
14+
pub use crate::cancellable::*;
1415
pub use crate::converter::*;
1516
pub use crate::data_input_stream::DataInputStreamExtManual;
1617
pub use crate::dbus_proxy::DBusProxyExtManual;

0 commit comments

Comments
 (0)