1use crate::{
2 config::streaming::StreamingLogLevel,
3 logging::ServerLogger,
4 memory_management::{
5 ErrorGraph, FailureId, ManagedMemoryId, SharedMemoryBindings, release_or_leak,
6 },
7 server::{BufferBinding, ServerError},
8 stream::{FailureStore, Failures, StreamFactory, StreamMemory, StreamPool, base},
9};
10use core::any::Any;
11use cubecl_environment::collections::HashMap;
12use cubecl_environment::stream::StreamId;
13use std::{
14 boxed::Box,
15 format,
16 sync::{Arc, mpsc::SyncSender},
17 vec::Vec,
18};
19
20pub trait EventStreamBackend: 'static {
25 type Stream: core::fmt::Debug + StreamMemory;
28 type Event: Send + 'static;
30
31 fn create_stream(&self) -> Result<Self::Stream, ServerError>;
38 fn handle_cursor(stream: &Self::Stream, handle: &BufferBinding) -> u64;
40 fn flush(stream: &mut Self::Stream, failures: &mut ErrorGraph) -> Self::Event;
47 fn wait_event(stream: &mut Self::Stream, event: Self::Event) -> Result<(), ServerError>;
54 fn wait_event_sync(event: Self::Event) -> Result<(), ServerError>;
56}
57
58#[derive(Debug)]
63pub struct MultiStream<B: EventStreamBackend> {
64 streams: StreamPool<EventStreamBackendWrapper<B>>,
66 failures: Failures,
69 pub logger: Arc<ServerLogger>,
71 max_streams: usize,
72 gc: GcThread<B>,
73 shared_bindings_pool: Vec<(ManagedMemoryId, StreamId, u64)>,
74}
75
76pub struct StreamWrapper<B: EventStreamBackend> {
84 stream: B::Stream,
86 cursor: u64,
88 last_synced: HashMap<usize, u64>,
90}
91
92pub struct ResolvedStreams<'a, B: EventStreamBackend> {
94 pub cursor: u64,
98 streams: &'a mut StreamPool<EventStreamBackendWrapper<B>>,
99 failures: &'a mut ErrorGraph,
100 analysis: SharedBindingAnalysis,
101 gc: &'a GcThread<B>,
102 pub current: StreamId,
104}
105
106#[derive(Debug)]
107pub struct GcTask<B: EventStreamBackend> {
109 to_drop: Box<dyn Any + Send + 'static>,
110 event: B::Event,
112}
113
114impl<B: EventStreamBackend> GcTask<B> {
115 pub fn new<T: Send + 'static>(to_drop: T, event: B::Event) -> Self {
117 Self {
118 to_drop: Box::new(to_drop),
119 event,
120 }
121 }
122}
123
124#[derive(Debug)]
125pub struct EventStreamBackendWrapper<B: EventStreamBackend> {
128 backend: B,
129}
130
131impl<B: EventStreamBackend> StreamMemory for StreamWrapper<B> {
132 fn failure(&self, binding: &BufferBinding) -> Option<FailureId> {
133 self.stream.failure(binding)
134 }
135
136 fn taint(&mut self, binding: &BufferBinding, failure: FailureId, failures: &mut ErrorGraph) {
137 self.stream.taint(binding, failure, failures)
138 }
139
140 fn written(&mut self, binding: &BufferBinding, failures: &mut ErrorGraph) {
141 self.stream.written(binding, failures)
142 }
143}
144
145impl<B: EventStreamBackend> StreamFactory for EventStreamBackendWrapper<B> {
146 type Stream = StreamWrapper<B>;
147
148 fn create(&mut self) -> Result<Self::Stream, ServerError> {
149 Ok(StreamWrapper {
150 stream: self.backend.create_stream()?,
151 cursor: 0,
152 last_synced: Default::default(),
153 })
154 }
155}
156
157#[derive(Debug)]
158struct GcThread<B: EventStreamBackend> {
159 sender: SyncSender<GcTask<B>>,
160}
161
162impl<B: EventStreamBackend> GcThread<B> {
163 fn new() -> GcThread<B> {
164 let (sender, recv) = std::sync::mpsc::sync_channel::<GcTask<B>>(8);
165
166 cubecl_environment::thread::spawn(move || {
167 while let Ok(event) = recv.recv() {
168 match B::wait_event_sync(event.event) {
169 Ok(()) => core::mem::drop(event.to_drop),
170 Err(err) => release_or_leak(event.to_drop, &err),
171 }
172 }
173 });
174
175 GcThread { sender }
176 }
177 fn register(&self, task: GcTask<B>) {
178 self.sender.send(task).unwrap()
179 }
180}
181
182fn stream_index(stream_id: &StreamId, max_streams: usize) -> usize {
183 stream_id.value as usize % max_streams
184}
185
186impl<'a, B: EventStreamBackend> ResolvedStreams<'a, B> {
187 pub fn get(&mut self, stream_id: &StreamId) -> &mut B::Stream {
189 let stream = self.streams.get_mut(stream_id);
190 &mut stream.stream
191 }
192
193 pub fn stream_ids(&self) -> Vec<StreamId> {
196 self.streams.stream_ids().collect()
197 }
198
199 pub fn all(&mut self) -> impl Iterator<Item = &mut B::Stream> {
201 self.streams.streams_mut().map(|stream| &mut stream.stream)
202 }
203
204 pub fn current(&mut self) -> &mut B::Stream {
206 let stream = self.streams.get_mut(&self.current);
207 &mut stream.stream
208 }
209
210 pub fn current_and_failures(&mut self) -> (&mut B::Stream, &mut ErrorGraph) {
215 let stream = self.streams.get_mut(&self.current);
216 (&mut stream.stream, self.failures)
217 }
218
219 pub fn get_and_failures(&mut self, stream_id: &StreamId) -> (&mut B::Stream, &mut ErrorGraph) {
222 let stream = self.streams.get_mut(stream_id);
223 (&mut stream.stream, self.failures)
224 }
225
226 pub fn taint<'b>(
229 &mut self,
230 error: ServerError,
231 written: impl Iterator<Item = &'b BufferBinding>,
232 ) {
233 base::taint(self.streams, error, written, self.failures);
234 }
235
236 pub fn written<'b>(&mut self, written: impl Iterator<Item = &'b BufferBinding>) {
239 base::written(self.streams, written, self.failures);
240 }
241
242 pub fn gc(&mut self, gc: GcTask<B>) {
244 self.gc.sender.send(gc).unwrap();
245 }
246}
247
248impl<'a, B: EventStreamBackend> Drop for ResolvedStreams<'a, B> {
249 fn drop(&mut self) {
250 if self.analysis.pinned.is_empty() {
251 return;
252 }
253
254 let stream = self.streams.get_mut(&self.current);
255 let event_origin = B::flush(&mut stream.stream, self.failures);
256
257 let stream_gc = &mut unsafe { self.streams.get_special(0) }.stream;
258 let pinned = core::mem::take(&mut self.analysis.pinned);
259 if let Err(err) = B::wait_event(stream_gc, event_origin) {
262 release_or_leak(pinned, &err);
263 return;
264 }
265 let event = B::flush(stream_gc, self.failures);
266
267 self.gc.register(GcTask::new(pinned, event));
268 }
269}
270
271impl<B: EventStreamBackend> MultiStream<B> {
272 pub fn backend_mut(&mut self) -> &mut B {
276 &mut self.streams.factory_mut().backend
277 }
278
279 pub fn new(logger: Arc<ServerLogger>, backend: B, max_streams: u8) -> Self {
281 let wrapper = EventStreamBackendWrapper { backend };
282 Self {
283 streams: StreamPool::new(wrapper, max_streams, 1),
284 failures: Failures::new(logger.clone()),
285 logger,
286 max_streams: max_streams as usize,
287 gc: GcThread::new(),
288 shared_bindings_pool: Vec::new(),
289 }
290 }
291
292 pub fn stream_ids(&self) -> impl Iterator<Item = StreamId> + '_ {
294 self.streams.stream_ids()
295 }
296
297 pub fn gc(&mut self, gc: GcTask<B>) {
299 self.gc.sender.send(gc).unwrap();
300 }
301
302 pub fn try_stream_mut(&mut self, stream_id: &StreamId) -> Option<&mut B::Stream> {
306 self.streams
307 .try_get_mut(stream_id)
308 .map(|wrapper| &mut wrapper.stream)
309 }
310
311 pub fn resolve<'a>(
322 &mut self,
323 stream_id: StreamId,
324 handles: impl Iterator<Item = &'a BufferBinding>,
325 ) -> Result<ResolvedStreams<'_, B>, ServerError> {
326 self.streams.get_or_create(&stream_id)?;
327 let analysis = self.align_streams(stream_id, handles)?;
328 if !analysis.pinned.is_empty() {
329 unsafe { self.streams.get_or_create_special(0) }?;
333 }
334
335 let stream = self.streams.get_mut(&stream_id);
336 stream.cursor += 1;
337
338 Ok(ResolvedStreams {
339 cursor: stream.cursor,
340 streams: &mut self.streams,
341 failures: self.failures.graph_mut(),
342 current: stream_id,
343 analysis,
344 gc: &self.gc,
345 })
346 }
347
348 fn align_streams<'a>(
353 &mut self,
354 stream_id: StreamId,
355 handles: impl Iterator<Item = &'a BufferBinding>,
356 ) -> Result<SharedBindingAnalysis, ServerError> {
357 let analysis = self.update_shared_bindings(stream_id, handles)?;
358
359 self.apply_analysis(stream_id, analysis)
360 }
361
362 pub(crate) fn update_shared_bindings<'a>(
367 &mut self,
368 stream_id: StreamId,
369 handles: impl Iterator<Item = &'a BufferBinding>,
370 ) -> Result<SharedBindingAnalysis, ServerError> {
371 self.shared_bindings_pool.clear();
373
374 let mut analysis = SharedBindingAnalysis::default();
375
376 for handle in handles.filter(|handle| handle.stream != stream_id) {
378 let index = stream_index(&handle.stream, self.max_streams);
379 let stream = unsafe { self.streams.get_or_create_index(index) }?;
381 let cursor_handle = B::handle_cursor(&stream.stream, handle);
382
383 self.shared_bindings_pool.push((
384 handle.memory.descriptor().id,
385 handle.stream,
386 cursor_handle,
387 ));
388 analysis.pinned.push(handle.memory.clone());
393 }
394
395 let current = self.streams.get_mut(&stream_id);
396
397 for (handle_id, stream, cursor) in self.shared_bindings_pool.iter() {
398 let index = stream_index(stream, self.max_streams);
399
400 if let Some(last_synced) = current.last_synced.get(&index) {
401 if last_synced < cursor {
402 self.logger.log_streaming(
403 |level| matches!(level, StreamingLogLevel::Full),
404 || {
405 format!(
406 "Binding on {} is shared on {} since it's not sync {} < {}",
407 stream, stream_id, last_synced, cursor
408 )
409 },
410 );
411 analysis.shared(*handle_id, index);
412 }
413 } else {
414 self.logger.log_streaming(
415 |level| matches!(level, StreamingLogLevel::Full),
416 || {
417 format!(
418 "Binding on {} is shared on {} since it was never synced.",
419 stream, stream_id,
420 )
421 },
422 );
423 analysis.shared(*handle_id, index);
424 }
425 }
426
427 Ok(analysis)
428 }
429
430 pub(crate) fn apply_analysis(
431 &mut self,
432 stream_id: StreamId,
433 analysis: SharedBindingAnalysis,
434 ) -> Result<SharedBindingAnalysis, ServerError> {
435 if analysis.slices.is_empty() {
436 return Ok(analysis);
437 }
438
439 let mut events = Vec::with_capacity(analysis.slices.len());
440
441 unsafe {
442 for origin in analysis.slices.keys() {
443 let stream = self.streams.get_mut_index(*origin);
444 let event = B::flush(&mut stream.stream, self.failures.graph_mut());
445
446 events.push(((origin, stream.cursor), event));
447 }
448 }
449
450 let stream = self.streams.get_mut(&stream_id);
451
452 for ((stream_origin, cursor_origin), event) in events {
453 self.logger.log_streaming(
454 |level| !matches!(level, StreamingLogLevel::Disabled),
455 || format!("Waiting on {stream_origin} from {stream_id}",),
456 );
457
458 B::wait_event(&mut stream.stream, event)?;
459 stream.last_synced.insert(*stream_origin, cursor_origin);
461 }
462
463 Ok(analysis)
464 }
465}
466
467impl<B: EventStreamBackend> FailureStore for MultiStream<B> {
468 type Factory = EventStreamBackendWrapper<B>;
469
470 fn split(&mut self) -> (&mut StreamPool<Self::Factory>, &mut Failures) {
471 (&mut self.streams, &mut self.failures)
472 }
473
474 fn parts(&self) -> (&StreamPool<Self::Factory>, &Failures) {
475 (&self.streams, &self.failures)
476 }
477}
478
479impl<B: EventStreamBackend> core::fmt::Debug for StreamWrapper<B> {
480 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
481 f.debug_struct("StreamWrapper")
482 .field("stream", &self.stream)
483 .field("cursor", &self.cursor)
484 .field("last_synced", &self.last_synced)
485 .finish()
486 }
487}
488
489#[derive(Default, Debug)]
490pub(crate) struct SharedBindingAnalysis {
491 slices: HashMap<usize, Vec<ManagedMemoryId>>,
492 pinned: SharedMemoryBindings,
502}
503
504impl PartialEq for SharedBindingAnalysis {
507 fn eq(&self, other: &Self) -> bool {
508 self.slices == other.slices
509 }
510}
511
512impl Eq for SharedBindingAnalysis {}
513
514impl SharedBindingAnalysis {
515 fn shared(&mut self, id: ManagedMemoryId, index: usize) {
516 match self.slices.get_mut(&index) {
517 Some(bindings) => bindings.push(id),
518 None => {
519 self.slices.insert(index, alloc::vec![id]);
520 }
521 }
522 }
523}
524
525#[cfg(test)]
526mod tests {
527 use crate::server::Handle;
528 use core::sync::atomic::{AtomicBool, Ordering};
529
530 use super::*;
531
532 fn service() -> cubecl_common::device::ServiceId {
534 cubecl_common::device::ServiceId::of::<()>(cubecl_common::device::DeviceId::new(0, 0))
535 }
536
537 const MAX_STREAMS: u8 = 4;
538
539 #[test_log::test]
540 fn test_analysis_shared_bindings_1() {
541 let logger = Arc::new(ServerLogger::default());
542 let stream_1 = StreamId { value: 1 };
543 let stream_2 = StreamId { value: 2 };
544
545 let binding_1 = handle(stream_1);
546 let binding_2 = handle(stream_2);
547
548 let mut ms = MultiStream::new(logger, TestBackend, MAX_STREAMS);
549 ms.resolve(stream_1, [].into_iter()).unwrap();
550 ms.resolve(stream_2, [].into_iter()).unwrap();
551
552 let analysis = ms
553 .update_shared_bindings(stream_1, [&binding_1, &binding_2].into_iter())
554 .unwrap();
555
556 let mut expected = SharedBindingAnalysis::default();
557 expected.shared(
558 binding_2.memory.descriptor().id,
559 ms.streams.stream_index(&binding_2.stream),
560 );
561
562 assert_eq!(analysis, expected);
563 }
564
565 #[test_log::test]
566 fn test_analysis_shared_bindings_2() {
567 let logger = Arc::new(ServerLogger::default());
568 let stream_1 = StreamId { value: 1 };
569 let stream_2 = StreamId { value: 2 };
570
571 let binding_1 = handle(stream_1);
572 let binding_2 = handle(stream_2);
573 let binding_3 = handle(stream_1);
574
575 let mut ms = MultiStream::new(logger, TestBackend, 4);
576 ms.resolve(stream_1, [].into_iter()).unwrap();
577 ms.resolve(stream_2, [].into_iter()).unwrap();
578
579 let analysis = ms
580 .update_shared_bindings(stream_1, [&binding_1, &binding_2, &binding_3].into_iter())
581 .unwrap();
582
583 let mut expected = SharedBindingAnalysis::default();
584 expected.shared(
585 binding_2.memory.descriptor().id,
586 ms.streams.stream_index(&binding_2.stream),
587 );
588
589 assert_eq!(analysis, expected);
590 }
591
592 #[test_log::test]
593 fn test_analysis_no_shared() {
594 let logger = Arc::new(ServerLogger::default());
595 let stream_1 = StreamId { value: 1 };
596 let stream_2 = StreamId { value: 2 };
597
598 let binding_1 = handle(stream_1);
599 let binding_2 = handle(stream_1);
600 let binding_3 = handle(stream_1);
601
602 let mut ms = MultiStream::new(logger, TestBackend, MAX_STREAMS);
603 ms.resolve(stream_1, [].into_iter()).unwrap();
604 ms.resolve(stream_2, [].into_iter()).unwrap();
605
606 let analysis = ms
607 .update_shared_bindings(stream_1, [&binding_1, &binding_2, &binding_3].into_iter())
608 .unwrap();
609
610 let expected = SharedBindingAnalysis::default();
611
612 assert_eq!(analysis, expected);
613 }
614
615 #[test_log::test]
616 fn test_state() {
617 let logger = Arc::new(ServerLogger::default());
618 let stream_1 = StreamId { value: 1 };
619 let stream_2 = StreamId { value: 2 };
620
621 let binding_1 = handle(stream_1);
622 let binding_2 = handle(stream_2);
623 let binding_3 = handle(stream_1);
624
625 let mut ms = MultiStream::new(logger, TestBackend, MAX_STREAMS);
626 ms.resolve(stream_1, [].into_iter()).unwrap();
627 ms.resolve(stream_2, [].into_iter()).unwrap();
628
629 ms.resolve(stream_1, [&binding_1, &binding_2, &binding_3].into_iter())
630 .unwrap();
631
632 let stream1 = ms.streams.get_mut(&stream_1);
633 let index_2 = stream_index(&stream_2, MAX_STREAMS as usize);
634 assert_eq!(stream1.last_synced.get(&index_2), Some(&1));
635 assert_eq!(stream1.cursor, 2);
636
637 let stream2 = ms.streams.get_mut(&stream_2);
638 assert!(stream2.last_synced.is_empty());
639 assert_eq!(stream2.cursor, 1);
640 }
641
642 #[test_log::test]
643 fn test_cross_stream_binding_pinned_until_gc_event() {
644 let logger = Arc::new(ServerLogger::default());
645 let stream_1 = StreamId { value: 1 };
646 let stream_2 = StreamId { value: 2 };
647
648 let gate = Arc::new(AtomicBool::new(false));
649 let mut ms = MultiStream::new(logger, GatedBackend { gate: gate.clone() }, MAX_STREAMS);
650 ms.resolve(stream_1, [].into_iter()).unwrap();
651 ms.resolve(stream_2, [].into_iter()).unwrap();
652
653 let handle = Handle::new(service(), stream_1, 10);
654 let observer = handle.memory.clone();
655 let binding = handle.binding();
656
657 drop(ms.resolve(stream_2, [&binding].into_iter()).unwrap());
658 drop(binding);
659
660 assert!(
664 !observer.is_free(),
665 "cross-stream binding must stay pinned while the consumer event is pending"
666 );
667
668 gate.store(true, Ordering::Release);
669 wait_until_free(&observer);
670 }
671
672 #[test_log::test]
673 fn test_already_synced_cross_stream_binding_still_pinned() {
674 let logger = Arc::new(ServerLogger::default());
675 let stream_1 = StreamId { value: 1 };
676 let stream_2 = StreamId { value: 2 };
677
678 let gate = Arc::new(AtomicBool::new(true));
679 let mut ms = MultiStream::new(logger, GatedBackend { gate: gate.clone() }, MAX_STREAMS);
680 ms.resolve(stream_1, [].into_iter()).unwrap();
681 ms.resolve(stream_2, [].into_iter()).unwrap();
682
683 let handle_1 = Handle::new(service(), stream_1, 10);
685 let binding_1 = handle_1.binding();
686 drop(ms.resolve(stream_2, [&binding_1].into_iter()).unwrap());
687 drop(binding_1);
688
689 gate.store(false, Ordering::Release);
691
692 let handle_2 = Handle::new(service(), stream_1, 10);
693 let observer = handle_2.memory.clone();
694 let binding_2 = handle_2.binding();
695
696 let resolved = ms.resolve(stream_2, [&binding_2].into_iter()).unwrap();
701 assert!(resolved.analysis.slices.is_empty());
702 drop(resolved);
703 drop(binding_2);
704
705 assert!(
706 !observer.is_free(),
707 "already-synced cross-stream binding must still be pinned"
708 );
709
710 gate.store(true, Ordering::Release);
711 wait_until_free(&observer);
712 }
713
714 fn wait_until_free(observer: &crate::memory_management::ManagedMemoryHandle) {
715 let start = std::time::Instant::now();
716 while !observer.is_free() {
717 assert!(
718 start.elapsed() < std::time::Duration::from_secs(10),
719 "pinned binding was never released"
720 );
721 std::thread::yield_now();
722 }
723 }
724
725 fn handle(stream: StreamId) -> BufferBinding {
726 Handle::new(service(), stream, 10).binding()
727 }
728
729 struct TestBackend;
730
731 #[derive(Debug, Default)]
732 struct TestStream;
733
734 #[derive(Debug)]
735 struct TestEvent {}
736
737 struct GatedBackend {
740 gate: Arc<AtomicBool>,
741 }
742
743 #[derive(Debug, Default)]
744 struct GatedStream {
745 gate: Arc<AtomicBool>,
746 }
747
748 #[derive(Debug)]
749 struct GatedEvent {
750 gate: Arc<AtomicBool>,
751 }
752
753 impl StreamMemory for GatedStream {
755 fn failure(&self, _binding: &BufferBinding) -> Option<FailureId> {
756 None
757 }
758
759 fn taint(
760 &mut self,
761 _binding: &BufferBinding,
762 _failure: FailureId,
763 _failures: &mut ErrorGraph,
764 ) {
765 }
766
767 fn written(&mut self, _binding: &BufferBinding, _failures: &mut ErrorGraph) {}
768 }
769
770 impl StreamMemory for TestStream {
771 fn failure(&self, _binding: &BufferBinding) -> Option<FailureId> {
772 None
773 }
774
775 fn taint(
776 &mut self,
777 _binding: &BufferBinding,
778 _failure: FailureId,
779 _failures: &mut ErrorGraph,
780 ) {
781 }
782
783 fn written(&mut self, _binding: &BufferBinding, _failures: &mut ErrorGraph) {}
784 }
785
786 impl EventStreamBackend for GatedBackend {
787 type Stream = GatedStream;
788 type Event = GatedEvent;
789
790 fn create_stream(&self) -> Result<Self::Stream, ServerError> {
791 Ok(GatedStream {
792 gate: self.gate.clone(),
793 })
794 }
795
796 fn flush(stream: &mut Self::Stream, _failures: &mut ErrorGraph) -> Self::Event {
797 GatedEvent {
798 gate: stream.gate.clone(),
799 }
800 }
801
802 fn wait_event(_stream: &mut Self::Stream, _event: Self::Event) -> Result<(), ServerError> {
803 Ok(())
804 }
805
806 fn wait_event_sync(event: Self::Event) -> Result<(), ServerError> {
807 while !event.gate.load(Ordering::Acquire) {
808 std::thread::yield_now();
809 }
810 Ok(())
811 }
812
813 fn handle_cursor(_stream: &Self::Stream, _handle: &BufferBinding) -> u64 {
814 0
815 }
816 }
817
818 impl EventStreamBackend for TestBackend {
819 type Stream = TestStream;
820 type Event = TestEvent;
821
822 fn create_stream(&self) -> Result<Self::Stream, ServerError> {
823 Ok(TestStream)
824 }
825
826 fn flush(_stream: &mut Self::Stream, _failures: &mut ErrorGraph) -> Self::Event {
827 TestEvent {}
828 }
829
830 fn wait_event(_stream: &mut Self::Stream, _event: Self::Event) -> Result<(), ServerError> {
831 Ok(())
832 }
833
834 fn wait_event_sync(_event: Self::Event) -> Result<(), ServerError> {
835 Ok(())
836 }
837
838 fn handle_cursor(_stream: &Self::Stream, _handle: &BufferBinding) -> u64 {
839 0
840 }
841 }
842
843 #[test]
844 fn memory_is_released_only_when_the_device_runs_nothing_anymore() {
845 let memory = Arc::new(());
846
847 let other = ServerError::Generic {
848 reason: "the wait failed".into(),
849 backtrace: Default::default(),
850 };
851 release_or_leak(memory.clone(), &other);
852 assert_eq!(Arc::strong_count(&memory), 2, "leaked, not released");
853
854 let poisoned: ServerError = crate::driver::DevicePoison::new("status 700").into();
855 release_or_leak(memory.clone(), &poisoned);
856 assert_eq!(Arc::strong_count(&memory), 2, "released, not leaked");
857 }
858}