@@ -96,6 +96,12 @@ pub(crate) struct StoreAsyncGuardWrapper {
9696 pub ( crate ) guard : * mut LocalRwLockWriteGuard < Box < StoreInner > > ,
9797}
9898
99+ pub ( crate ) struct StorePtrPauseGuard {
100+ store_id : StoreId ,
101+ ptr : * mut StoreInner ,
102+ ref_count_decremented : bool ,
103+ }
104+
99105#[ cfg( feature = "experimental-async" ) ]
100106pub ( crate ) enum GetStoreAsyncGuardResult {
101107 Ok ( StoreAsyncGuardWrapper ) ,
@@ -170,13 +176,56 @@ impl StoreContext {
170176 pub ( crate ) unsafe fn ensure_installed ( store_ptr : * mut StoreInner ) -> StoreInstallGuard {
171177 let store_id = unsafe { store_ptr. as_ref ( ) . unwrap ( ) . objects . id ( ) } ;
172178 if Self :: is_active ( store_id) {
179+ let current_ptr = STORE_CONTEXT_STACK . with ( |cell| {
180+ let stack = cell. borrow ( ) ;
181+ unsafe { stack. last ( ) . unwrap ( ) . entry . get ( ) . as_ref ( ) . unwrap ( ) . as_ptr ( ) }
182+ } ) ;
183+ assert_eq ! ( store_ptr, current_ptr, "Store context pointer mismatch" ) ;
173184 StoreInstallGuard :: NotInstalled
174185 } else {
175186 Self :: install ( store_id, StoreContextEntry :: Sync ( store_ptr) ) ;
176187 StoreInstallGuard :: Installed ( store_id)
177188 }
178189 }
179190
191+ /// "Pause" one borrow of the store context.
192+ ///
193+ /// # Safety
194+ /// Code must ensure it does not use the StorePtrWrapper or
195+ /// StoreAsyncGuardWrapper that it owns, or any StoreRef/StoreMut
196+ /// derived from them, while the store context is paused.
197+ ///
198+ /// The safe, correct use-case for this method is to
199+ /// pause the store context while executing WASM code, which
200+ /// cannot use the store context directly. This allows an async
201+ /// context to uninstall the store context when suspending if it's
202+ /// called from a sync imported function. The imported function
203+ /// will have borrowed the store context in its trampoline, which
204+ /// will prevent the async context from uninstalling the store.
205+ /// However, since the imported function passes a mutable borrow
206+ /// of its store into `Function::call`, it will expect the store
207+ /// to change before the call returns.
208+ pub ( crate ) unsafe fn pause ( id : StoreId ) -> StorePtrPauseGuard {
209+ STORE_CONTEXT_STACK . with ( |cell| {
210+ let mut stack = cell. borrow_mut ( ) ;
211+ let top = stack
212+ . last_mut ( )
213+ . expect ( "No store context installed on this thread" ) ;
214+ assert_eq ! ( top. id, id, "Mismatched store context access" ) ;
215+ let ref_count_decremented = if top. borrow_count > 0 {
216+ top. borrow_count -= 1 ;
217+ true
218+ } else {
219+ false
220+ } ;
221+ StorePtrPauseGuard {
222+ store_id : id,
223+ ptr : unsafe { top. entry . get ( ) . as_ref ( ) . unwrap ( ) . as_ptr ( ) } ,
224+ ref_count_decremented,
225+ }
226+ } )
227+ }
228+
180229 /// Safety: This method lets you borrow multiple mutable references
181230 /// to the currently active store context. The caller must ensure that:
182231 /// * there is only one mutable reference alive, or
@@ -291,6 +340,9 @@ impl Clone for StorePtrWrapper {
291340
292341impl Drop for StorePtrWrapper {
293342 fn drop ( & mut self ) {
343+ if std:: thread:: panicking ( ) {
344+ return ;
345+ }
294346 let id = self . as_mut ( ) . objects_mut ( ) . id ( ) ;
295347 STORE_CONTEXT_STACK . with ( |cell| {
296348 let mut stack = cell. borrow_mut ( ) ;
@@ -306,6 +358,9 @@ impl Drop for StorePtrWrapper {
306358#[ cfg( feature = "experimental-async" ) ]
307359impl Drop for StoreAsyncGuardWrapper {
308360 fn drop ( & mut self ) {
361+ if std:: thread:: panicking ( ) {
362+ return ;
363+ }
309364 let id = unsafe { self . guard . as_ref ( ) . unwrap ( ) . objects . id ( ) } ;
310365 STORE_CONTEXT_STACK . with ( |cell| {
311366 let mut stack = cell. borrow_mut ( ) ;
@@ -323,12 +378,28 @@ impl Drop for StoreInstallGuard {
323378 if let Self :: Installed ( store_id) = self {
324379 STORE_CONTEXT_STACK . with ( |cell| {
325380 let mut stack = cell. borrow_mut ( ) ;
326- let top = stack. pop ( ) . expect ( "Store context stack underflow" ) ;
327- assert_eq ! ( top. id, * store_id, "Mismatched store context uninstall" ) ;
328- assert_eq ! (
329- top. borrow_count, 0 ,
330- "Cannot uninstall store context while it is still borrowed"
331- ) ;
381+ match ( stack. pop ( ) , std:: thread:: panicking ( ) ) {
382+ ( Some ( top) , false ) => {
383+ assert_eq ! ( top. id, * store_id, "Mismatched store context uninstall" ) ;
384+ assert_eq ! (
385+ top. borrow_count, 0 ,
386+ "Cannot uninstall store context while it is still borrowed"
387+ ) ;
388+ }
389+ ( Some ( top) , true ) => {
390+ // If we're panicking and there's a store ID mismatch, just
391+ // put the store back in the hope that its own install guard
392+ // take care of uninstalling it later.
393+ if top. id != * store_id {
394+ stack. push ( top) ;
395+ }
396+ }
397+ ( None , false ) => panic ! ( "Store context stack underflow" ) ,
398+ ( None , true ) => {
399+ // Nothing to do if we're panicking; panics can put the context
400+ // in an invalid state, and we don't to cause another panic here.
401+ }
402+ }
332403 } )
333404 }
334405 }
@@ -338,12 +409,51 @@ impl Drop for ForcedStoreInstallGuard {
338409 fn drop ( & mut self ) {
339410 STORE_CONTEXT_STACK . with ( |cell| {
340411 let mut stack = cell. borrow_mut ( ) ;
341- let top = stack. pop ( ) . expect ( "Store context stack underflow" ) ;
342- assert_eq ! ( top. id, self . store_id, "Mismatched store context uninstall" ) ;
412+ match ( stack. pop ( ) , std:: thread:: panicking ( ) ) {
413+ ( Some ( top) , false ) => {
414+ assert_eq ! ( top. id, self . store_id, "Mismatched store context uninstall" ) ;
415+ assert_eq ! (
416+ top. borrow_count, 0 ,
417+ "Cannot uninstall store context while it is still borrowed"
418+ ) ;
419+ }
420+ ( Some ( top) , true ) => {
421+ // If we're panicking and there's a store ID mismatch, just
422+ // put the store back in the hope that its own install guard
423+ // take care of uninstalling it later.
424+ if top. id != self . store_id {
425+ stack. push ( top) ;
426+ }
427+ }
428+ ( None , false ) => panic ! ( "Store context stack underflow" ) ,
429+ ( None , true ) => {
430+ // Nothing to do if we're panicking; panics can put the context
431+ // in an invalid state, and we don't to cause another panic here.
432+ }
433+ }
434+ } )
435+ }
436+ }
437+
438+ impl Drop for StorePtrPauseGuard {
439+ fn drop ( & mut self ) {
440+ if std:: thread:: panicking ( ) {
441+ return ;
442+ }
443+ STORE_CONTEXT_STACK . with ( |cell| {
444+ let mut stack = cell. borrow_mut ( ) ;
445+ let top = stack
446+ . last_mut ( )
447+ . expect ( "No store context installed on this thread" ) ;
448+ assert_eq ! ( top. id, self . store_id, "Mismatched store context access" ) ;
343449 assert_eq ! (
344- top. borrow_count, 0 ,
345- "Cannot uninstall store context while it is still borrowed"
450+ unsafe { top. entry. get( ) . as_ref( ) . unwrap( ) } . as_ptr( ) ,
451+ self . ptr,
452+ "Mismatched store context access"
346453 ) ;
454+ if self . ref_count_decremented {
455+ top. borrow_count += 1 ;
456+ }
347457 } )
348458 }
349459}
0 commit comments