@@ -58,19 +58,20 @@ pub struct MemoryMerkleSubTree {
5858 pub height : usize ,
5959 pub path_len : usize ,
6060 layout : MemoryMerkleSubTreeLayout ,
61- /// initial_data_ptr is a raw device pointer to the initial-memory buffer (`d_data`) captured
62- /// in [`Self::build_async`], stored as a `usize` so the borrow is erased .
61+ /// Shared handle to the initial-memory buffer (`d_data`) captured in [`Self::build_async`].
62+ /// `None` for empty/dummy subtrees that have no backing buffer .
6363 ///
64- /// SAFETY / lifetime: under the `OmitBottomLevels` layout the omitted bottom levels are never
65- /// materialized into `buf`; they are recomputed on demand from this buffer during
64+ /// Holding an `Arc` makes the subtree a co-owner of the buffer, so the host cannot free it
65+ /// while the subtree is alive. Under the `OmitBottomLevels` layout the omitted bottom levels
66+ /// are never materialized into `buf`; they are recomputed on demand from this buffer during
6667 /// [`MemoryMerkleTree::update_with_touched_blocks`] (see `recompute_omitted_node` in
67- /// `merkle_tree.cu`). The pointed-to buffer must therefore stay alive and unmodified from the
68- /// `build_async` call all the way until that update completes — not merely until the build
69- /// kernel runs. The compiler cannot enforce this through the erased pointer, so the caller is
70- /// responsible: in production the owning `DeviceBuffer`s are held in
71- /// `MemoryInventoryGPU::initial_memory` and only dropped after `update_with_touched_blocks` is
72- /// done .
73- initial_data_ptr : usize ,
68+ /// `merkle_tree.cu`), so the buffer must stay alive until that update completes.
69+ ///
70+ /// NOTE: this only fixes host-side ownership. The buffer is also consumed by GPU kernels
71+ /// enqueued on the stream, so it must additionally outlive those kernels — that ordering is
72+ /// guaranteed by the `stream.synchronize()` in [`MemoryMerkleTree::drop_subtrees`], not by the
73+ /// `Arc`. Drop the subtrees (which releases these handles) only after that sync .
74+ initial_data : Option < Arc < DeviceBuffer < u8 > > > ,
7475}
7576
7677impl MemoryMerkleSubTree {
@@ -139,7 +140,7 @@ impl MemoryMerkleSubTree {
139140 buf,
140141 path_len,
141142 layout,
142- initial_data_ptr : 0 ,
143+ initial_data : None ,
143144 }
144145 }
145146
@@ -150,7 +151,7 @@ impl MemoryMerkleSubTree {
150151 buf : DeviceBuffer :: new ( ) ,
151152 path_len : 0 ,
152153 layout : MemoryMerkleSubTreeLayout :: Full ,
153- initial_data_ptr : 0 ,
154+ initial_data : None ,
154155 }
155156 }
156157
@@ -171,15 +172,15 @@ impl MemoryMerkleSubTree {
171172 /// Here `addr_space_idx` is the address space _shifted_ by ADDR_SPACE_OFFSET = 1
172173 pub fn build_async (
173174 & mut self ,
174- d_data : & DeviceBuffer < u8 > ,
175+ d_data : Arc < DeviceBuffer < u8 > > ,
175176 addr_space_idx : usize ,
176177 zero_hash : & DeviceBuffer < H > ,
177178 device_ctx : & GpuDeviceCtx ,
178179 ) {
179180 let event = CudaEvent :: new ( ) . unwrap ( ) ;
180- // Capture the raw device pointer; `d_data` must outlive `update_with_touched_blocks`,
181- // which re-reads it under the `OmitBottomLevels` layout (see the `initial_data_ptr ` field).
182- self . initial_data_ptr = d_data. as_ptr ( ) as usize ;
181+ // Co-own the buffer; it must outlive `update_with_touched_blocks`, which re-reads it under
182+ // the `OmitBottomLevels` layout (see the `initial_data ` field).
183+ self . initial_data = Some ( d_data. clone ( ) ) ;
183184 if self . buf . is_empty ( ) {
184185 self . buf = DeviceBuffer :: with_capacity_on ( 1 , device_ctx) ;
185186 unsafe {
@@ -195,7 +196,7 @@ impl MemoryMerkleSubTree {
195196 } else {
196197 unsafe {
197198 build_merkle_subtree (
198- d_data,
199+ & d_data,
199200 1 << self . stored_heap_height ( ) ,
200201 & self . buf ,
201202 self . path_len ,
@@ -340,8 +341,8 @@ impl MemoryMerkleTree {
340341 /// `OmitBottomLevels` layout, `d_data` is also re-read during
341342 /// [`Self::update_with_touched_blocks`] to recompute the omitted bottom levels, so it must
342343 /// remain valid until that update completes — not just until the build kernel runs. See
343- /// [`MemoryMerkleSubTree`]'s `initial_data_ptr ` field for details.
344- pub fn build_async ( & mut self , d_data : & DeviceBuffer < u8 > , addr_space : usize ) {
344+ /// [`MemoryMerkleSubTree`]'s `initial_data ` field for details.
345+ pub fn build_async ( & mut self , d_data : Arc < DeviceBuffer < u8 > > , addr_space : usize ) {
345346 if addr_space < ADDR_SPACE_OFFSET as usize {
346347 return ;
347348 }
@@ -421,7 +422,7 @@ impl MemoryMerkleTree {
421422 let initial_data_ptrs = self
422423 . subtrees
423424 . iter ( )
424- . map ( |s| s. initial_data_ptr )
425+ . map ( |s| s. initial_data . as_ref ( ) . map_or ( 0 , |b| b . as_ptr ( ) as usize ) )
425426 . collect :: < Vec < _ > > ( ) ;
426427 let subtrees_pointers = self
427428 . subtrees
@@ -649,15 +650,15 @@ mod tests {
649650 . iter ( )
650651 . map ( |mem| {
651652 let mem_slice = mem. as_slice ( ) ;
652- if !mem_slice. is_empty ( ) {
653+ Arc :: new ( if !mem_slice. is_empty ( ) {
653654 mem_slice. to_device_on ( & gpu_merkle_tree. device_ctx ) . unwrap ( )
654655 } else {
655656 DeviceBuffer :: new ( )
656- }
657+ } )
657658 } )
658659 . collect :: < Vec < _ > > ( ) ;
659660 for ( i, mem_slice) in mem_slices. iter ( ) . enumerate ( ) {
660- gpu_merkle_tree. build_async ( mem_slice, i) ;
661+ gpu_merkle_tree. build_async ( mem_slice. clone ( ) , i) ;
661662 }
662663 assert_eq ! (
663664 gpu_merkle_tree. subtrees[ RV64_REGISTER_AS as usize - 1 ] . layout,
@@ -862,15 +863,15 @@ mod tests {
862863 . iter ( )
863864 . map ( |mem| {
864865 let mem_slice = mem. as_slice ( ) ;
865- if !mem_slice. is_empty ( ) {
866+ Arc :: new ( if !mem_slice. is_empty ( ) {
866867 mem_slice. to_device_on ( & gpu_merkle_tree. device_ctx ) . unwrap ( )
867868 } else {
868869 DeviceBuffer :: new ( )
869- }
870+ } )
870871 } )
871872 . collect :: < Vec < _ > > ( ) ;
872873 for ( i, mem_slice) in mem_slices. iter ( ) . enumerate ( ) {
873- gpu_merkle_tree. build_async ( mem_slice, i) ;
874+ gpu_merkle_tree. build_async ( mem_slice. clone ( ) , i) ;
874875 }
875876 gpu_merkle_tree. finalize ( ) ;
876877
0 commit comments