Skip to content

Commit ef3f231

Browse files
committed
Add subscribe_to_signal
Add a new method subscribe_to_signal which returns a managed signal subscription which unsubscribes from the signal when dropped. Also add a weak ref counterpart to this signal subscription to allow breaking reference cycles. This fits better into Rust's ownership model and makes it much easier to handle the lifetime of a signal subscription in async code. Mark the old signal_subscribe and signal_unsubscribe as deprecated in favour of this new subscribe_to_signal method.
1 parent 93fefd4 commit ef3f231

File tree

2 files changed

+100
-2
lines changed

2 files changed

+100
-2
lines changed

gio/src/dbus_connection.rs

Lines changed: 99 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::{
66
ffi, ActionGroup, DBusConnection, DBusInterfaceInfo, DBusMessage, DBusMethodInvocation,
77
DBusSignalFlags, MenuModel,
88
};
9-
use glib::{prelude::*, translate::*};
9+
use glib::{prelude::*, translate::*, WeakRef};
1010

1111
pub trait DBusMethodCall: Sized {
1212
fn parse_call(
@@ -117,9 +117,70 @@ pub struct ActionGroupExportId(NonZeroU32);
117117
pub struct MenuModelExportId(NonZeroU32);
118118
#[derive(Debug, Eq, PartialEq)]
119119
pub struct FilterId(NonZeroU32);
120+
120121
#[derive(Debug, Eq, PartialEq)]
121122
pub struct SignalSubscriptionId(NonZeroU32);
122123

124+
/// A strong subscription to a D-Bus signal.
125+
///
126+
/// Keep a reference to a D-Bus connection to maintain a subscription on a
127+
/// D-Bus signal even if the connection has no other strong reference.
128+
///
129+
/// When dropped, unsubscribes from signal on the connection, and then drop the
130+
/// reference on the connection. If no other strong reference on the connection
131+
/// exists the connection is closed and destroyed.
132+
#[derive(Debug)]
133+
pub struct SignalSubscription(DBusConnection, Option<SignalSubscriptionId>);
134+
135+
impl SignalSubscription {
136+
/// Downgrade this signal subscription to a weak one.
137+
pub fn downgrade(mut self) -> WeakSignalSubscription {
138+
WeakSignalSubscription(self.0.downgrade(), self.1.take())
139+
}
140+
}
141+
142+
impl Drop for SignalSubscription {
143+
fn drop(&mut self) {
144+
if let Some(id) = self.1.take() {
145+
#[allow(deprecated)]
146+
self.0.signal_unsubscribe(id);
147+
}
148+
}
149+
}
150+
151+
/// A weak subscription to a D-Bus signal.
152+
///
153+
/// Like [`SignalSubscription`] but hold only a weak reference to the D-Bus
154+
/// connection the siganl is subscribed on, i.e. maintain the subscription on
155+
/// the D-Bus signal only as long as some strong reference exists on the
156+
/// corresponding D-Bus connection.
157+
///
158+
/// When dropped, unsubscribes from signal on the connection if it still exists,
159+
/// and then drop the reference on the connection. If no other strong reference
160+
/// on the connection exists the connection is closed and destroyed.
161+
#[derive(Debug)]
162+
pub struct WeakSignalSubscription(WeakRef<DBusConnection>, Option<SignalSubscriptionId>);
163+
164+
impl WeakSignalSubscription {
165+
/// Upgrade this signal subscription to a strong one.
166+
pub fn upgrade(mut self) -> Option<SignalSubscription> {
167+
self.0
168+
.upgrade()
169+
.map(|c| SignalSubscription(c, self.1.take()))
170+
}
171+
}
172+
173+
impl Drop for WeakSignalSubscription {
174+
fn drop(&mut self) {
175+
if let Some(id) = self.1.take() {
176+
if let Some(connection) = self.0.upgrade() {
177+
#[allow(deprecated)]
178+
connection.signal_unsubscribe(id);
179+
}
180+
}
181+
}
182+
}
183+
123184
// rustdoc-stripper-ignore-next
124185
/// Build a registered DBus object, by handling different parts of DBus.
125186
#[must_use = "The builder must be built to be used"]
@@ -437,8 +498,44 @@ impl DBusConnection {
437498
}
438499
}
439500

501+
/// Subscribe to a D-Bus signal.
502+
///
503+
/// See [`Self::signal_subscribe`] for arguments.
504+
///
505+
/// Return a signal subscription which keeps a reference to this D-Bus
506+
/// connection and unsubscribes from the signal when dropped.
507+
///
508+
/// To avoid reference cycles you may wish to downgrade the returned
509+
/// subscription to a weak one with [`SignalSubscription::downgrade`].
510+
#[must_use]
511+
pub fn subscribe_to_signal<
512+
P: Fn(&DBusConnection, &str, &str, &str, &str, &glib::Variant) + 'static,
513+
>(
514+
&self,
515+
sender: Option<&str>,
516+
interface_name: Option<&str>,
517+
member: Option<&str>,
518+
object_path: Option<&str>,
519+
arg0: Option<&str>,
520+
flags: DBusSignalFlags,
521+
callback: P,
522+
) -> SignalSubscription {
523+
#[allow(deprecated)]
524+
let id = self.signal_subscribe(
525+
sender,
526+
interface_name,
527+
member,
528+
object_path,
529+
arg0,
530+
flags,
531+
callback,
532+
);
533+
SignalSubscription(self.clone(), Some(id))
534+
}
535+
440536
#[doc(alias = "g_dbus_connection_signal_subscribe")]
441537
#[allow(clippy::too_many_arguments)]
538+
#[deprecated(note = "Prefer subscribe_to_signal")]
442539
pub fn signal_subscribe<
443540
P: Fn(&DBusConnection, &str, &str, &str, &str, &glib::Variant) + 'static,
444541
>(
@@ -507,6 +604,7 @@ impl DBusConnection {
507604
}
508605

509606
#[doc(alias = "g_dbus_connection_signal_unsubscribe")]
607+
#[deprecated(note = "Prefer subscribe_to_signal")]
510608
pub fn signal_unsubscribe(&self, subscription_id: SignalSubscriptionId) {
511609
unsafe {
512610
ffi::g_dbus_connection_signal_unsubscribe(

gio/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ pub use self::dbus::*;
3333
mod dbus_connection;
3434
pub use self::dbus_connection::{
3535
ActionGroupExportId, FilterId, MenuModelExportId, RegistrationBuilder, RegistrationId,
36-
SignalSubscriptionId, WatcherId,
36+
SignalSubscription, SignalSubscriptionId, WatcherId, WeakSignalSubscription,
3737
};
3838
mod dbus_message;
3939
mod dbus_method_invocation;

0 commit comments

Comments
 (0)