Skip to content

Commit 83c9737

Browse files
committed
[Rust] Implement support for persistent data notifications
Using `DataNotificationClosure::register_persisted` you can keep the notifications active until the associated `BinaryView` is destroyed.
1 parent d0c0dc1 commit 83c9737

File tree

1 file changed

+93
-15
lines changed

1 file changed

+93
-15
lines changed

rust/src/data_notification.rs

Lines changed: 93 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -83,16 +83,17 @@ macro_rules! trait_handler {
8383
) -> DataNotificationHandle<'a, H> {
8484
// SAFETY: this leak is undone on drop
8585
let leak_notify = Box::leak(Box::new(notify));
86-
let handle = BNBinaryDataNotification {
86+
let notifications = BNBinaryDataNotification {
8787
context: leak_notify as *mut _ as *mut c_void,
88+
unregistered: None,
8889
$($ffi_param_name: triggers.$fun_name.then_some($fun_name::<H>)),*,
8990
// TODO: Require all BNBinaryDataNotification's to be implemented?
9091
// Since core developers are not required to write Rust bindings (yet) we do not
91-
// force new binary data notifications callbacks to be written here.of core developers who do not wish to write
92+
// force new binary data notifications callbacks to be written here.
9293
..Default::default()
9394
};
9495
// Box it to prevent a copy being returned in `DataNotificationHandle`.
95-
let mut boxed_handle = Box::new(handle);
96+
let mut boxed_handle = Box::new(notifications);
9697
unsafe { BNRegisterDataNotification(view.handle, boxed_handle.as_mut()) };
9798
DataNotificationHandle {
9899
bv: view.to_owned(),
@@ -101,12 +102,38 @@ macro_rules! trait_handler {
101102
}
102103
}
103104

104-
/// Implement closures that will be called by on the event of data modification.
105+
fn register_persisted_data_notification<'a, H: CustomDataNotification + 'a>(
106+
view: &BinaryView,
107+
notify: H,
108+
triggers: DataNotificationTriggers,
109+
) -> PersistedDataNotificationHandle<'a, H> {
110+
// SAFETY: this leak is undone on drop
111+
let leak_notify = Box::leak(Box::new(notify));
112+
// NOTE: The notification structure here is passed as ptr but eventually gets copied, so
113+
// the lifetime of this object just needs to live to after `BNRegisterDataNotification`.
114+
let notifications = BNBinaryDataNotification {
115+
context: leak_notify as *mut _ as *mut c_void,
116+
unregistered: Some(cb_unregistered::<H>),
117+
$($ffi_param_name: triggers.$fun_name.then_some($fun_name::<H>)),*,
118+
// TODO: Require all BNBinaryDataNotification's to be implemented?
119+
// Since core developers are not required to write Rust bindings (yet) we do not
120+
// force new binary data notifications callbacks to be written here.
121+
..Default::default()
122+
};
123+
let mut boxed_handle = Box::new(notifications);
124+
unsafe { BNRegisterDataNotification(view.handle, boxed_handle.as_mut()) };
125+
PersistedDataNotificationHandle {
126+
handle: boxed_handle,
127+
_life: std::marker::PhantomData,
128+
}
129+
}
130+
131+
/// Implement closures that will be called in the event of data modification.
105132
///
106-
/// Once dropped the closures will stop being called.
133+
/// Once dropped, the closures will stop being called.
107134
///
108135
/// NOTE: Closures are not executed on the same thread as the event that occurred, you must not depend
109-
/// on any serial behavior
136+
/// on any serial behavior.
110137
///
111138
/// # Example
112139
///
@@ -148,6 +175,8 @@ macro_rules! trait_handler {
148175
)*
149176

150177
/// Register the closures to be notified up until the [`DataNotificationHandle`] is dropped.
178+
///
179+
/// If you need to have the closures notified indefinitely, use [`DataNotificationClosure::register_persisted`].
151180
pub fn register(
152181
self,
153182
view: &BinaryView,
@@ -160,6 +189,20 @@ macro_rules! trait_handler {
160189
)*
161190
register_data_notification(view, self, triggers)
162191
}
192+
193+
/// Register the closures to be notified up until the [`BinaryView`] is dropped.
194+
pub fn register_persisted(
195+
self,
196+
view: &BinaryView,
197+
) -> PersistedDataNotificationHandle<'a, Self> {
198+
let mut triggers = DataNotificationTriggers::default();
199+
$(
200+
if self.$fun_name.is_some() {
201+
triggers = triggers.$fun_name();
202+
}
203+
)*
204+
register_persisted_data_notification(view, self, triggers)
205+
}
163206
}
164207

165208
impl CustomDataNotification for DataNotificationClosure<'_> {
@@ -352,27 +395,27 @@ trait_handler! {
352395
var: *mut BNDataVariable: &DataVariable = &DataVariable::from_raw(&*var),
353396
),
354397
externalLibraryAdded => external_library_added(
355-
data: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(data),
398+
view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
356399
library: *mut BNExternalLibrary: &ExternalLibrary = &ExternalLibrary::from_raw(NonNull::new(library).unwrap()),
357400
),
358401
externalLibraryUpdated => external_library_updated(
359-
data: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(data),
402+
view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
360403
library: *mut BNExternalLibrary: &ExternalLibrary = &ExternalLibrary::from_raw(NonNull::new(library).unwrap()),
361404
),
362405
externalLibraryRemoved => external_library_removed(
363-
data: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(data),
406+
view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
364407
library: *mut BNExternalLibrary: &ExternalLibrary = &ExternalLibrary::from_raw(NonNull::new(library).unwrap()),
365408
),
366409
externalLocationAdded => external_location_added(
367-
data: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(data),
410+
view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
368411
location: *mut BNExternalLocation: &ExternalLocation = &ExternalLocation::from_raw(NonNull::new(location).unwrap()),
369412
),
370413
externalLocationUpdated => external_location_updated(
371-
data: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(data),
414+
view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
372415
location: *mut BNExternalLocation: &ExternalLocation = &ExternalLocation::from_raw(NonNull::new(location).unwrap()),
373416
),
374417
externalLocationRemoved => external_location_removed(
375-
data: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(data),
418+
view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
376419
location: *mut BNExternalLocation: &ExternalLocation = &ExternalLocation::from_raw(NonNull::new(location).unwrap()),
377420
),
378421
typeArchiveAttached => type_archive_attached(
@@ -406,8 +449,8 @@ trait_handler! {
406449
entry: *mut BNUndoEntry: &UndoEntry = &UndoEntry::from_raw(NonNull::new(entry).unwrap()),
407450
),
408451
rebased => rebased(
409-
oldview: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(oldview),
410-
newview: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(newview),
452+
old_view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(old_view),
453+
new_view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(new_view),
411454
),
412455
}
413456

@@ -430,7 +473,7 @@ impl<T: CustomDataNotification> DataNotificationHandle<'_, T> {
430473
}
431474

432475
pub fn unregister(self) -> T {
433-
// NOTE don't drop the ctxt, return it
476+
// NOTE: Don't drop the ctxt, return it
434477
let mut slf = core::mem::ManuallyDrop::new(self);
435478
unsafe { slf.unregister_bv() };
436479
unsafe { *slf.extract_context() }
@@ -444,3 +487,38 @@ impl<T: CustomDataNotification> Drop for DataNotificationHandle<'_, T> {
444487
let _ctxt = unsafe { self.extract_context() };
445488
}
446489
}
490+
491+
unsafe impl<T: CustomDataNotification> Send for DataNotificationHandle<'_, T> {}
492+
unsafe impl<T: CustomDataNotification> Sync for DataNotificationHandle<'_, T> {}
493+
494+
pub struct PersistedDataNotificationHandle<'a, T: CustomDataNotification>
495+
where
496+
T: 'a,
497+
{
498+
handle: Box<BNBinaryDataNotification>,
499+
_life: std::marker::PhantomData<&'a T>,
500+
}
501+
502+
impl<T: CustomDataNotification> PersistedDataNotificationHandle<'_, T> {
503+
unsafe fn unregister_bv(&mut self, view: &BinaryView) {
504+
BNUnregisterDataNotification(view.handle, self.handle.as_mut())
505+
}
506+
507+
unsafe fn extract_context(&mut self) -> Box<T> {
508+
Box::from_raw(self.handle.context as *mut T)
509+
}
510+
511+
pub fn unregister(self, view: &BinaryView) -> T {
512+
// NOTE: Don't drop the ctxt, return it
513+
let mut slf = core::mem::ManuallyDrop::new(self);
514+
unsafe { slf.unregister_bv(view) };
515+
unsafe { *slf.extract_context() }
516+
}
517+
}
518+
519+
unsafe impl<T: CustomDataNotification> Send for PersistedDataNotificationHandle<'_, T> {}
520+
unsafe impl<T: CustomDataNotification> Sync for PersistedDataNotificationHandle<'_, T> {}
521+
522+
unsafe extern "C" fn cb_unregistered<T: CustomDataNotification>(ctxt: *mut c_void) {
523+
let _ = Box::from_raw(ctxt as *mut T);
524+
}

0 commit comments

Comments
 (0)