1use serde::{Deserialize, Deserializer, Serialize, Serializer};
9#[cfg(unix)]
10use std::os::fd::OwnedFd;
11use std::{
12 any::{Any, TypeId},
13 collections::{HashMap, hash_map::Entry},
14 fmt, io,
15 marker::PhantomData,
16 sync::{
17 Arc, Mutex, Weak,
18 atomic::{AtomicU64, Ordering},
19 },
20};
21
22#[cfg(unix)]
23use crate::{handle::TakeHandle, transport::ReceivedHandles};
24use crate::{
25 handle::{ErasedHandle, PutHandle},
26 transport::EncodeHandles,
27};
28
29pub(crate) fn pack_wire(owner: u8, id: u64) -> u64 {
31 debug_assert!(id < (1 << 63), "opaque identifier is too large to pack");
32 (id << 1) | u64::from(owner & 1)
33}
34
35pub(crate) fn unpack_wire(packed: u64) -> (u8, u64) {
36 ((packed & 1) as u8, packed >> 1)
37}
38
39const WIRE_GIFT: u8 = 0;
41const WIRE_CITATION: u8 = 1;
43
44const GRANT_RELEASE_THRESHOLD: u32 = u32::MAX / 2;
48
49pub trait OpaqueResource: Send + Sync + 'static {
59 type Marker: 'static;
60}
61
62pub(crate) trait ReleaseSink: Send + Sync + 'static {
71 fn release(&self, id: u64, count: u32);
72}
73
74pub(crate) struct LocalRef {
82 id: u64,
83 serial: u64,
86 session: Weak<Session>,
87}
88
89pub(crate) struct RemoteRef {
96 id: u64,
97 serial: u64,
99 session: Weak<Session>,
100}
101
102pub(crate) enum Inner {
105 Local(Arc<LocalRef>),
106 Remote(Arc<RemoteRef>),
107}
108
109impl Clone for Inner {
110 fn clone(&self) -> Self {
111 match self {
113 Self::Local(local) => Self::Local(local.clone()),
114 Self::Remote(remote) => Self::Remote(remote.clone()),
115 }
116 }
117}
118
119impl Inner {
120 fn id(&self) -> u64 {
121 match self {
122 Self::Local(local) => local.id,
123 Self::Remote(remote) => remote.id,
124 }
125 }
126
127 fn owner(&self) -> u8 {
128 match self {
129 Self::Local(_) => WIRE_GIFT,
130 Self::Remote(_) => WIRE_CITATION,
131 }
132 }
133}
134
135impl Drop for LocalRef {
136 fn drop(&mut self) {
137 let Some(session) = self.session.upgrade() else {
140 return;
141 };
142 let mut tables = session.tables.lock().unwrap();
143 let Some(entry) = tables.local.get(&self.id) else {
144 return;
145 };
146 if !entry.points_at(self) {
150 return;
151 }
152 if entry.granted == 0 {
156 tables.local.remove(&self.id);
157 }
158 }
159}
160
161impl Drop for RemoteRef {
162 fn drop(&mut self) {
163 let Some(session) = self.session.upgrade() else {
164 return;
165 };
166 let granted = {
167 let mut tables = session.tables.lock().unwrap();
168 if !tables
173 .remote
174 .get(&self.id)
175 .is_some_and(|entry| entry.points_at(self))
176 {
177 return;
178 }
179 tables
180 .remote
181 .remove(&self.id)
182 .expect("just matched")
183 .granted
184 };
185 if granted > 0 {
186 session.sink.release(self.id, granted);
187 }
188 }
189}
190
191const CITE_OWNED: &str = "cannot cite a resource this endpoint owns; \
193 gift it again to name it to the peer";
194
195pub struct Gift<M> {
199 pub(crate) inner: Inner,
200 marker: PhantomData<fn() -> M>,
201}
202
203pub struct Cite<M> {
207 pub(crate) inner: Inner,
208 marker: PhantomData<fn() -> M>,
209}
210
211impl<M> Cite<M> {
212 pub(crate) fn new(inner: Inner) -> Self {
213 Self {
214 inner,
215 marker: PhantomData,
216 }
217 }
218}
219
220impl<M> Gift<M> {
221 pub(crate) fn new(inner: Inner) -> Self {
222 Self {
223 inner,
224 marker: PhantomData,
225 }
226 }
227
228 pub fn cite(&self) -> Cite<M> {
234 assert!(matches!(self.inner, Inner::Remote(_)), "{CITE_OWNED}");
235 Cite {
236 inner: self.inner.clone(),
237 marker: PhantomData,
238 }
239 }
240}
241
242macro_rules! opaque_handle {
245 ($name:ident) => {
246 impl<M> Clone for $name<M> {
247 fn clone(&self) -> Self {
248 Self {
249 inner: self.inner.clone(),
250 marker: PhantomData,
251 }
252 }
253 }
254
255 impl<M> PartialEq for $name<M> {
256 fn eq(&self, other: &Self) -> bool {
257 self.inner.owner() == other.inner.owner() && self.inner.id() == other.inner.id()
258 }
259 }
260
261 impl<M> Eq for $name<M> {}
262
263 impl<M> fmt::Debug for $name<M> {
264 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
265 f.debug_struct(stringify!($name))
266 .field("owner", &self.inner.owner())
267 .field("id", &self.inner.id())
268 .finish_non_exhaustive()
269 }
270 }
271 };
272}
273
274opaque_handle!(Gift);
275opaque_handle!(Cite);
276
277impl<M> Serialize for Gift<M> {
278 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
279 assert!(
280 matches!(self.inner, Inner::Local(_)),
281 "cannot gift a resource this endpoint does not own; \
282 use `Gift::cite` to name it back to its owner"
283 );
284 crate::serde::serialize_opaque(&self.inner, serializer)
285 }
286}
287
288impl<'de, M> Deserialize<'de> for Gift<M> {
289 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
290 crate::serde::deserialize_gift(deserializer).map(Gift::new)
293 }
294}
295
296impl<M> Serialize for Cite<M> {
297 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
298 assert!(matches!(self.inner, Inner::Remote(_)), "{CITE_OWNED}");
299 crate::serde::serialize_opaque(&self.inner, serializer)
300 }
301}
302
303impl<'de, M: 'static> Deserialize<'de> for Cite<M> {
304 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
305 crate::serde::deserialize_cite(deserializer, TypeId::of::<M>()).map(Cite::new)
308 }
309}
310
311pub struct OpaqueGuard<T>(Arc<T>);
316impl<T> std::ops::Deref for OpaqueGuard<T> {
317 type Target = T;
318 fn deref(&self) -> &T {
319 &self.0
320 }
321}
322
323#[derive(Clone, Copy, Debug, thiserror::Error)]
325#[error("invalid opaque object")]
326pub struct InvalidOpaque;
327
328struct LocalEntry {
329 ty: TypeId,
330 marker: TypeId,
333 resource: Option<Arc<dyn Any + Send + Sync>>,
338 granted: u32,
340 handle: Weak<LocalRef>,
341}
342
343impl LocalEntry {
344 fn points_at(&self, handle: &LocalRef) -> bool {
345 std::ptr::eq(self.handle.as_ptr(), handle as *const LocalRef)
346 }
347}
348
349struct RemoteEntry {
350 granted: u32,
354 handle: Weak<RemoteRef>,
355}
356
357impl RemoteEntry {
358 fn points_at(&self, handle: &RemoteRef) -> bool {
359 std::ptr::eq(self.handle.as_ptr(), handle as *const RemoteRef)
360 }
361}
362
363#[derive(Default)]
364struct Tables {
365 next: u64,
366 local: HashMap<u64, LocalEntry>,
367 remote: HashMap<u64, RemoteEntry>,
368}
369
370pub(crate) struct Session {
377 serial: u64,
382 tables: Mutex<Tables>,
383 markers: Mutex<HashMap<TypeId, (TypeId, &'static str)>>,
387 sink: Box<dyn ReleaseSink>,
388}
389
390impl Session {
391 pub(crate) fn new(sink: Box<dyn ReleaseSink>) -> Arc<Self> {
392 static NEXT_SERIAL: AtomicU64 = AtomicU64::new(0);
393 Arc::new(Self {
394 serial: NEXT_SERIAL.fetch_add(1, Ordering::Relaxed),
395 tables: Mutex::new(Tables::default()),
396 markers: Mutex::new(HashMap::new()),
397 sink,
398 })
399 }
400
401 fn check_serial(&self, serial: u64) {
408 assert_eq!(
409 serial, self.serial,
410 "opaque reference redeemed against a different session"
411 );
412 }
413
414 fn record_marker<T: OpaqueResource>(&self) {
422 let marker = TypeId::of::<T::Marker>();
423 let previous = {
424 let mut markers = self.markers.lock().unwrap();
425 match markers.entry(marker) {
426 Entry::Occupied(entry) if entry.get().0 != TypeId::of::<T>() => Some(entry.get().1),
427 Entry::Occupied(_) => None,
428 Entry::Vacant(entry) => {
429 entry.insert((TypeId::of::<T>(), std::any::type_name::<T>()));
430 None
431 }
432 }
433 };
434 if let Some(previous) = previous {
437 panic!(
438 "opaque marker `{}` is already registered for resource type `{}`; \
439 it cannot also name `{}`",
440 std::any::type_name::<T::Marker>(),
441 previous,
442 std::any::type_name::<T>(),
443 );
444 }
445 }
446
447 pub(crate) fn register<T: OpaqueResource>(self: &Arc<Self>, value: T) -> Gift<T::Marker> {
448 self.record_marker::<T>();
449 let mut tables = self.tables.lock().unwrap();
450 let id = tables.next;
451 tables.next = tables
452 .next
453 .checked_add(1)
454 .expect("opaque identifiers exhausted");
455 let handle = Arc::new(LocalRef {
456 id,
457 serial: self.serial,
458 session: Arc::downgrade(self),
459 });
460 tables.local.insert(
461 id,
462 LocalEntry {
463 ty: TypeId::of::<T>(),
464 marker: TypeId::of::<T::Marker>(),
465 resource: Some(Arc::new(value)),
466 granted: 0,
467 handle: Arc::downgrade(&handle),
468 },
469 );
470 Gift::new(Inner::Local(handle))
471 }
472
473 pub(crate) fn acquire<T: OpaqueResource>(
474 &self,
475 value: Cite<T::Marker>,
476 ) -> Result<OpaqueGuard<T>, InvalidOpaque> {
477 let Inner::Local(local) = &value.inner else {
482 return Err(InvalidOpaque);
483 };
484 self.check_serial(local.serial);
485 let tables = self.tables.lock().unwrap();
486 let entry = tables.local.get(&local.id).ok_or(InvalidOpaque)?;
490 if entry.ty != TypeId::of::<T>() {
494 return Err(InvalidOpaque);
495 }
496 let resource = entry.resource.as_ref().ok_or(InvalidOpaque)?;
499 Ok(OpaqueGuard(
500 resource
501 .clone()
502 .downcast::<T>()
503 .map_err(|_| InvalidOpaque)?,
504 ))
505 }
506
507 pub(crate) fn unregister<T: OpaqueResource>(
518 &self,
519 value: Cite<T::Marker>,
520 ) -> Result<Option<T>, InvalidOpaque> {
521 let Inner::Local(local) = &value.inner else {
523 return Err(InvalidOpaque);
524 };
525 self.check_serial(local.serial);
526 let mut tables = self.tables.lock().unwrap();
527 let entry = tables.local.get_mut(&local.id).ok_or(InvalidOpaque)?;
528 if entry.ty != TypeId::of::<T>() {
529 return Err(InvalidOpaque);
530 }
531 let resource = entry.resource.take().ok_or(InvalidOpaque)?;
532 let resource = resource.downcast::<T>().map_err(|_| InvalidOpaque)?;
533 Ok(Arc::try_unwrap(resource).ok())
534 }
535
536 pub(crate) fn try_unregister<T: OpaqueResource>(
552 &self,
553 value: Cite<T::Marker>,
554 ) -> Result<Option<T>, InvalidOpaque> {
555 let Inner::Local(local) = &value.inner else {
557 return Err(InvalidOpaque);
558 };
559 self.check_serial(local.serial);
560 let mut tables = self.tables.lock().unwrap();
561 let entry = tables.local.get_mut(&local.id).ok_or(InvalidOpaque)?;
562 if entry.ty != TypeId::of::<T>() {
563 return Err(InvalidOpaque);
564 }
565 let resource = entry.resource.take().ok_or(InvalidOpaque)?;
566 let resource = match resource.downcast::<T>() {
567 Ok(resource) => resource,
568 Err(resource) => {
569 entry.resource = Some(resource);
570 return Err(InvalidOpaque);
571 }
572 };
573 match Arc::try_unwrap(resource) {
574 Ok(value) => Ok(Some(value)),
575 Err(shared) => {
576 entry.resource = Some(shared);
577 Ok(None)
578 }
579 }
580 }
581
582 pub(crate) fn release(&self, id: u64, count: u32) {
585 let mut tables = self.tables.lock().unwrap();
586 let Some(entry) = tables.local.get_mut(&id) else {
587 return;
588 };
589 if entry.granted != u32::MAX {
593 entry.granted = entry.granted.saturating_sub(count);
594 }
595 if entry.granted == 0 && entry.handle.upgrade().is_none() {
596 tables.local.remove(&id);
597 }
598 }
599
600 fn gift(&self, handle: &Arc<LocalRef>) -> Escrowed {
603 let mut tables = self.tables.lock().unwrap();
604 if let Some(entry) = tables.local.get_mut(&handle.id) {
605 entry.granted = entry.granted.saturating_add(1);
606 }
607 Escrowed::Gift(handle.clone())
608 }
609
610 fn receive(self: &Arc<Self>, id: u64) -> Inner {
613 let (handle, release) = {
614 let mut tables = self.tables.lock().unwrap();
615 let session = Arc::downgrade(self);
616 let entry = tables.remote.entry(id).or_insert_with(|| RemoteEntry {
617 granted: 0,
618 handle: Weak::new(),
619 });
620 entry.granted = entry.granted.saturating_add(1);
621 let release = if entry.granted >= GRANT_RELEASE_THRESHOLD {
622 let release = entry.granted - 1;
623 entry.granted = 1;
624 Some(release)
625 } else {
626 None
627 };
628 let handle = if let Some(handle) = entry.handle.upgrade() {
633 handle
634 } else {
635 let handle = Arc::new(RemoteRef {
636 id,
637 serial: self.serial,
638 session,
639 });
640 entry.handle = Arc::downgrade(&handle);
641 handle
642 };
643 (handle, release)
644 };
645 if let Some(count) = release {
646 self.sink.release(id, count);
647 }
648 Inner::Remote(handle)
649 }
650
651 fn cite(self: &Arc<Self>, id: u64, marker: TypeId) -> Result<Inner, InvalidOpaque> {
670 let mut tables = self.tables.lock().unwrap();
671 let entry = tables.local.get_mut(&id).ok_or(InvalidOpaque)?;
672 if entry.marker != marker {
673 return Err(InvalidOpaque);
674 }
675 if let Some(handle) = entry.handle.upgrade() {
676 return Ok(Inner::Local(handle));
677 }
678 let handle = Arc::new(LocalRef {
681 id,
682 serial: self.serial,
683 session: Arc::downgrade(self),
684 });
685 entry.handle = Arc::downgrade(&handle);
686 Ok(Inner::Local(handle))
687 }
688
689 pub(crate) fn take_gift(self: &Arc<Self>, owner: u8, id: u64) -> Result<Inner, InvalidOpaque> {
698 if owner != WIRE_GIFT {
699 return Err(InvalidOpaque);
700 }
701 Ok(self.receive(id))
702 }
703
704 pub(crate) fn take_cite(
712 self: &Arc<Self>,
713 owner: u8,
714 id: u64,
715 marker: TypeId,
716 ) -> Result<Inner, InvalidOpaque> {
717 if owner != WIRE_CITATION {
718 return Err(InvalidOpaque);
719 }
720 self.cite(id, marker)
721 }
722}
723
724enum Escrowed {
726 Gift(Arc<LocalRef>),
729 Citation(#[allow(dead_code)] Arc<RemoteRef>),
736}
737
738#[derive(Default)]
743pub(crate) struct Ledger {
744 items: Vec<Escrowed>,
745}
746
747impl Ledger {
748 pub(crate) fn put(&mut self, value: &Inner, session: &Arc<Session>) -> (u8, u64) {
751 match value {
752 Inner::Local(local) => {
753 session.check_serial(local.serial);
757 self.items.push(session.gift(local));
758 (WIRE_GIFT, local.id)
759 }
760 Inner::Remote(remote) => {
761 session.check_serial(remote.serial);
762 self.items.push(Escrowed::Citation(remote.clone()));
763 (WIRE_CITATION, remote.id)
764 }
765 }
766 }
767
768 pub(crate) fn commit(self) {
772 }
774
775 pub(crate) fn rescind(self) {
783 for item in &self.items {
784 let Escrowed::Gift(handle) = item else {
785 continue;
786 };
787 let Some(session) = handle.session.upgrade() else {
788 continue;
789 };
790 let mut tables = session.tables.lock().unwrap();
791 if let Some(entry) = tables.local.get_mut(&handle.id)
792 && entry.granted != u32::MAX
793 {
794 entry.granted = entry.granted.saturating_sub(1);
795 }
796 }
797 }
798}
799
800pub(crate) struct SessionFrame<'a> {
804 pub(crate) inner: EncodeHandles,
805 pub(crate) session: &'a Arc<Session>,
806 pub(crate) ledger: &'a mut Ledger,
807}
808
809impl PutHandle for SessionFrame<'_> {
810 #[cfg(unix)]
811 fn put_handle(&mut self, handle: &dyn ErasedHandle) -> io::Result<u32> {
812 self.inner.put_handle(handle)
813 }
814
815 #[cfg(windows)]
816 fn put_handle(&mut self, handle: &dyn ErasedHandle) -> io::Result<usize> {
817 self.inner.put_handle(handle)
818 }
819
820 fn put_opaque(&mut self, opaque: &Inner) -> io::Result<(u8, u64)> {
821 Ok(self.ledger.put(opaque, self.session))
822 }
823}
824
825#[cfg(unix)]
827pub(crate) struct SessionHandles<'a> {
828 pub(crate) inner: ReceivedHandles,
829 pub(crate) session: &'a Arc<Session>,
830}
831
832#[cfg(unix)]
833impl TakeHandle for SessionHandles<'_> {
834 fn take_handle(&mut self, index: u32) -> io::Result<OwnedFd> {
835 self.inner.take_handle(index)
836 }
837
838 fn finish(&mut self) -> io::Result<()> {
839 self.inner.finish()
840 }
841 fn take_gift(&mut self, owner: u8, id: u64) -> io::Result<Inner> {
842 self.session
843 .take_gift(owner, id)
844 .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "invalid opaque reference"))
845 }
846
847 fn take_cite(&mut self, owner: u8, id: u64, marker: TypeId) -> io::Result<Inner> {
848 self.session
849 .take_cite(owner, id, marker)
850 .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "invalid opaque reference"))
851 }
852}
853
854#[cfg(test)]
855mod tests {
856 use super::*;
857 use std::sync::atomic::{AtomicBool, Ordering};
858
859 #[derive(Default)]
860 struct Recorder(Mutex<Vec<(u64, u32)>>);
861 impl ReleaseSink for Arc<Recorder> {
862 fn release(&self, id: u64, count: u32) {
863 self.0.lock().unwrap().push((id, count));
864 }
865 }
866
867 fn session() -> (Arc<Session>, Arc<Recorder>) {
869 let recorder = Arc::new(Recorder::default());
870 (Session::new(Box::new(recorder.clone())), recorder)
871 }
872
873 struct Marker;
874 struct OtherMarker;
875 struct DropMarker;
876 struct Value(u32);
877 struct OtherValue;
878 struct DropValue(Arc<AtomicBool>);
879 impl OpaqueResource for Value {
880 type Marker = Marker;
881 }
882 impl OpaqueResource for OtherValue {
883 type Marker = OtherMarker;
884 }
885 impl OpaqueResource for DropValue {
886 type Marker = DropMarker;
887 }
888 impl Drop for DropValue {
889 fn drop(&mut self) {
890 self.0.store(true, Ordering::Relaxed);
891 }
892 }
893
894 fn cited<M: 'static>(session: &Arc<Session>, gift: &Gift<M>) -> Cite<M> {
898 Cite::new(
899 session
900 .take_cite(WIRE_CITATION, gift.inner.id(), TypeId::of::<M>())
901 .unwrap(),
902 )
903 }
904
905 struct Impostor;
907 impl OpaqueResource for Impostor {
908 type Marker = Marker;
909 }
910
911 #[test]
912 #[should_panic(expected = "is already registered for resource type")]
913 fn two_resource_types_under_one_marker_panic_at_registration() {
914 let (session, _) = session();
915 let _opaque = session.register(Value(42));
916 let _conflict = session.register(Impostor);
917 }
918
919 #[test]
920 fn a_citation_naming_a_differently_typed_entry_is_rejected() {
921 let (session, _) = session();
922 let opaque = session.register(Value(42));
923 let id = opaque.inner.id();
924 assert!(
925 session
926 .take_cite(WIRE_CITATION, id, TypeId::of::<Marker>())
927 .is_ok()
928 );
929 assert!(
930 session
931 .take_cite(WIRE_CITATION, id, TypeId::of::<OtherMarker>())
932 .is_err()
933 );
934 }
935
936 #[test]
937 #[should_panic(expected = "different session")]
938 fn redeeming_an_opaque_against_another_session_panics() {
939 let (first, _) = session();
940 let (second, _) = session();
941 let opaque = first.register(Value(42));
942 let _ = second.acquire::<Value>(cited(&first, &opaque));
943 }
944
945 #[test]
946 #[should_panic(expected = "different session")]
947 fn serializing_an_opaque_into_another_session_panics() {
948 let (first, _) = session();
949 let (second, _) = session();
950 let opaque = first.register(Value(42));
951 Ledger::default().put(&opaque.inner, &second);
952 }
953
954 #[test]
955 fn guards_outlive_registration() {
956 let (session, _) = session();
957 let opaque = session.register(Value(42));
958 let guard = session.acquire::<Value>(cited(&session, &opaque)).unwrap();
959 assert!(
960 session
961 .unregister::<Value>(cited(&session, &opaque))
962 .unwrap()
963 .is_none()
964 );
965 assert_eq!(guard.0.0, 42);
966 assert!(session.acquire::<Value>(cited(&session, &opaque)).is_err());
967 }
968
969 #[test]
970 fn unregister_returns_exclusively_owned_value() {
971 let (session, _) = session();
972 let opaque = session.register(Value(42));
973 let value = session
974 .unregister::<Value>(cited(&session, &opaque))
975 .unwrap()
976 .unwrap();
977 assert_eq!(value.0, 42);
978 }
979
980 #[test]
981 fn try_unregister_restores_a_shared_value() {
982 let (session, _) = session();
983 let opaque = session.register(Value(42));
984 let guard = session.acquire::<Value>(cited(&session, &opaque)).unwrap();
985 assert!(
986 session
987 .try_unregister::<Value>(cited(&session, &opaque))
988 .unwrap()
989 .is_none()
990 );
991 drop(guard);
992 assert_eq!(
995 session
996 .try_unregister::<Value>(cited(&session, &opaque))
997 .unwrap()
998 .unwrap()
999 .0,
1000 42
1001 );
1002 }
1003
1004 #[test]
1005 fn try_unregister_returns_exclusively_owned_value() {
1006 let (session, _) = session();
1007 let opaque = session.register(Value(42));
1008 let value = session
1009 .try_unregister::<Value>(cited(&session, &opaque))
1010 .unwrap()
1011 .unwrap();
1012 assert_eq!(value.0, 42);
1013 assert!(session.acquire::<Value>(cited(&session, &opaque)).is_err());
1014 }
1015
1016 #[test]
1017 fn wrong_type_does_not_remove_value() {
1018 let (session, _) = session();
1019 let opaque = session.register(Value(42));
1020 let wrong = Cite::<OtherMarker>::new(opaque.inner.clone());
1021 assert!(session.unregister::<OtherValue>(wrong).is_err());
1022 assert_eq!(
1023 session
1024 .acquire::<Value>(cited(&session, &opaque))
1025 .unwrap()
1026 .0
1027 .0,
1028 42
1029 );
1030 }
1031
1032 #[test]
1033 fn dropping_session_drops_registered_values() {
1034 let dropped = Arc::new(AtomicBool::new(false));
1035 let (session, _) = session();
1036 let opaque = session.register(DropValue(dropped.clone()));
1037 drop(opaque);
1038 drop(session);
1039 assert!(dropped.load(Ordering::Relaxed));
1040 }
1041
1042 #[test]
1043 fn dropping_the_last_local_handle_retires_an_ungifted_entry() {
1044 let (session, _) = session();
1045 let opaque = session.register(Value(42));
1046 drop(opaque);
1047 assert!(session.tables.lock().unwrap().local.is_empty());
1048 }
1049
1050 #[test]
1051 fn a_gifted_entry_outlives_its_local_handle_until_released() {
1052 let (session, _) = session();
1053 let opaque = session.register(Value(42));
1054 let Inner::Local(handle) = &opaque.inner else {
1055 unreachable!()
1056 };
1057 let escrow = session.gift(handle);
1058 drop(escrow);
1059 drop(opaque);
1060 assert_eq!(session.tables.lock().unwrap().local.len(), 1);
1062 session.release(0, 1);
1063 assert!(session.tables.lock().unwrap().local.is_empty());
1064 }
1065
1066 #[test]
1067 fn cloning_an_opaque_does_not_grant_a_protocol_reference() {
1068 let (session, _) = session();
1069 let opaque = session.register(Value(42));
1070 let clones: Vec<_> = (0..8).map(|_| opaque.clone()).collect();
1071 assert_eq!(session.tables.lock().unwrap().local[&0].granted, 0);
1072 drop(clones);
1073 drop(opaque);
1074 assert!(session.tables.lock().unwrap().local.is_empty());
1075 }
1076
1077 #[test]
1078 fn rescinding_undoes_the_gift_increment() {
1079 let (session, _) = session();
1080 let opaque = session.register(Value(42));
1081 let mut ledger = Ledger::default();
1082 assert_eq!(ledger.put(&opaque.inner, &session), (WIRE_GIFT, 0));
1083 assert_eq!(session.tables.lock().unwrap().local[&0].granted, 1);
1084 ledger.rescind();
1085 assert_eq!(session.tables.lock().unwrap().local[&0].granted, 0);
1086 }
1087
1088 #[test]
1089 fn saturated_owner_grant_count_is_immortal() {
1090 let (session, _) = session();
1091 let opaque = session.register(Value(42));
1092 let Inner::Local(handle) = &opaque.inner else {
1093 unreachable!()
1094 };
1095 session
1096 .tables
1097 .lock()
1098 .unwrap()
1099 .local
1100 .get_mut(&0)
1101 .unwrap()
1102 .granted = u32::MAX - 1;
1103
1104 let escrow = session.gift(handle);
1105 assert_eq!(session.tables.lock().unwrap().local[&0].granted, u32::MAX);
1106 session.release(0, u32::MAX);
1107 assert_eq!(session.tables.lock().unwrap().local[&0].granted, u32::MAX);
1108
1109 let ledger = Ledger {
1110 items: vec![escrow],
1111 };
1112 ledger.rescind();
1113 assert_eq!(session.tables.lock().unwrap().local[&0].granted, u32::MAX);
1114 drop(opaque);
1115 assert!(session.tables.lock().unwrap().local.contains_key(&0));
1116 }
1117
1118 #[test]
1119 fn remote_grants_are_collapsed_at_the_high_threshold() {
1120 let (session, recorder) = session();
1121 let first: Gift<Marker> = Gift::new(session.take_gift(WIRE_GIFT, 7).unwrap());
1122 session
1123 .tables
1124 .lock()
1125 .unwrap()
1126 .remote
1127 .get_mut(&7)
1128 .unwrap()
1129 .granted = GRANT_RELEASE_THRESHOLD - 1;
1130
1131 let second: Gift<Marker> = Gift::new(session.take_gift(WIRE_GIFT, 7).unwrap());
1132 assert_eq!(first, second);
1133 assert_eq!(session.tables.lock().unwrap().remote[&7].granted, 1);
1134 assert_eq!(
1135 *recorder.0.lock().unwrap(),
1136 vec![(7, GRANT_RELEASE_THRESHOLD - 1)]
1137 );
1138
1139 drop(first);
1140 drop(second);
1141 assert_eq!(
1142 *recorder.0.lock().unwrap(),
1143 vec![(7, GRANT_RELEASE_THRESHOLD - 1), (7, 1)]
1144 );
1145 }
1146
1147 #[test]
1148 fn committing_leaves_the_gift_increment_in_place() {
1149 let (session, _) = session();
1150 let opaque = session.register(Value(42));
1151 let mut ledger = Ledger::default();
1152 ledger.put(&opaque.inner, &session);
1153 ledger.commit();
1154 assert_eq!(session.tables.lock().unwrap().local[&0].granted, 1);
1155 }
1156
1157 #[test]
1158 fn citing_an_opaque_has_no_protocol_effect() {
1159 let (session, recorder) = session();
1160 let opaque: Gift<Marker> = Gift::new(session.take_gift(WIRE_GIFT, 7).unwrap());
1161 let mut ledger = Ledger::default();
1162 assert_eq!(
1163 ledger.put(&opaque.cite().inner, &session),
1164 (WIRE_CITATION, 7)
1165 );
1166 ledger.commit();
1167 drop(opaque);
1169 assert_eq!(*recorder.0.lock().unwrap(), vec![(7, 1)]);
1170 }
1171
1172 #[test]
1173 fn repeated_gifts_of_one_id_accumulate_into_a_single_release() {
1174 let (session, recorder) = session();
1175 let first: Gift<Marker> = Gift::new(session.take_gift(WIRE_GIFT, 3).unwrap());
1176 let second: Gift<Marker> = Gift::new(session.take_gift(WIRE_GIFT, 3).unwrap());
1177 assert_eq!(first, second);
1178 drop(first);
1179 assert!(recorder.0.lock().unwrap().is_empty());
1180 drop(second);
1181 assert_eq!(*recorder.0.lock().unwrap(), vec![(3, 2)]);
1182 }
1183
1184 #[test]
1188 fn a_gift_in_a_citation_position_is_rejected() {
1189 let (session, _) = session();
1190 let opaque = session.register(Value(42));
1191 let id = opaque.inner.id();
1192 assert!(
1193 session
1194 .take_cite(WIRE_GIFT, id, TypeId::of::<Marker>())
1195 .is_err()
1196 );
1197 }
1198
1199 #[test]
1200 fn a_citation_in_a_gift_position_is_rejected() {
1201 let (session, _) = session();
1202 assert!(session.take_gift(WIRE_CITATION, 7).is_err());
1203 }
1204
1205 #[test]
1208 #[should_panic(expected = "cannot cite a resource this endpoint owns")]
1209 fn citing_a_resource_this_endpoint_owns_panics() {
1210 let (session, _) = session();
1211 let _ = session.register(Value(42)).cite();
1212 }
1213
1214 #[test]
1215 #[should_panic(expected = "cannot gift a resource this endpoint does not own")]
1216 fn gifting_a_resource_the_peer_owns_panics() {
1217 let (session, _) = session();
1218 let mirrored: Gift<Marker> = Gift::new(session.take_gift(WIRE_GIFT, 7).unwrap());
1219 let _ = postcard::to_allocvec(&mirrored);
1220 }
1221
1222 #[test]
1226 #[should_panic(expected = "cannot cite a resource this endpoint owns")]
1227 fn re_serializing_a_citation_that_came_home_panics() {
1228 let (session, _) = session();
1229 let opaque = session.register(Value(42));
1230 let _ = postcard::to_allocvec(&cited(&session, &opaque));
1231 }
1232
1233 #[test]
1234 fn a_citation_for_an_unknown_id_is_rejected() {
1235 let (session, _) = session();
1236 assert!(
1237 session
1238 .take_cite(WIRE_CITATION, 99, TypeId::of::<Marker>())
1239 .is_err()
1240 );
1241 }
1242
1243 #[test]
1247 fn a_citation_for_an_unregistered_but_still_granted_id_resolves() {
1248 let (session, _) = session();
1249 let opaque = session.register(Value(42));
1250 let id = opaque.inner.id();
1251 Ledger::default().put(&opaque.inner, &session);
1253 session
1254 .unregister::<Value>(cited(&session, &opaque))
1255 .unwrap();
1256 let cite = Cite::<Marker>::new(
1257 session
1258 .take_cite(WIRE_CITATION, id, TypeId::of::<Marker>())
1259 .unwrap(),
1260 );
1261 assert!(session.acquire::<Value>(cite).is_err());
1262 }
1263
1264 #[test]
1265 fn plain_postcard_use_panics() {
1266 let (session, _) = session();
1267 let opaque = session.register(Value(42));
1268 assert!(
1269 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
1270 postcard::to_allocvec(&opaque)
1271 }))
1272 .is_err()
1273 );
1274 assert!(std::panic::catch_unwind(|| postcard::from_bytes::<Gift<Marker>>(&[0])).is_err());
1275 }
1276
1277 #[test]
1278 fn wire_form_survives_packing_both_owners() {
1279 for owner in [WIRE_GIFT, WIRE_CITATION] {
1280 for id in [0, 1, 42, u32::MAX as u64, (1 << 62) - 1] {
1281 assert_eq!(unpack_wire(pack_wire(owner, id)), (owner, id));
1282 }
1283 }
1284 }
1285
1286 #[test]
1287 fn releasing_an_unknown_id_is_ignored() {
1288 let (session, _) = session();
1289 session.release(1234, 5);
1290 }
1291}