@@ -402,16 +402,16 @@ def mock_unregister(cb):
402402 self .assertEqual (len (registered_callbacks ), 1 )
403403
404404 def test_concurrent_purge_race_condition (self ):
405- # Concurrent threads attempting to check memebership and call purge for the same owner.
406- # Here we explicitly define a synchronized set to mimic the behavior of _live_owners.
407- # This set will block two threads on __contains__, allowing us to test the race condition.
405+ # Concurrent threads attempting to check membership and call purge for the same owner.
406+ # Here we explicitly define a synchronized dict to mimic the behavior of _live_owners.
407+ # This dict will block two threads on __contains__, allowing us to test the race condition.
408408 cache = subprocess_server ._SharedCache (lambda x : "obj" , lambda x : None )
409409 owner = cache .register ()
410410
411411 barrier = threading .Barrier (2 )
412412 exceptions = []
413413
414- class SynchronizedSet ( set ):
414+ class SynchronizedDict ( dict ):
415415 def __contains__ (self , item ):
416416 res = super ().__contains__ (item )
417417 try :
@@ -421,7 +421,7 @@ def __contains__(self, item):
421421 pass
422422 return res
423423
424- cache ._live_owners = SynchronizedSet (cache ._live_owners )
424+ cache ._live_owners = SynchronizedDict (cache ._live_owners )
425425
426426 def purge_worker ():
427427 try :
@@ -551,6 +551,53 @@ def __init__(self):
551551 # Clean up the other owner
552552 cache .purge (other_owner )
553553
554+ def test_non_context_owners_do_not_share_keys (self ):
555+ cache = subprocess_server ._SharedCache (self .with_prefix , lambda x : None )
556+ # owner1 is a non-context owner (e.g., prism)
557+ owner1 = cache .register (is_context = False )
558+ a = cache .get ('a' , owner = owner1 )
559+
560+ # owner2 is another non-context owner (e.g., short-lived expansion service)
561+ owner2 = cache .register (is_context = False )
562+ b = cache .get ('b' , owner = owner2 )
563+
564+ # Verify that owner1 does not own 'b'
565+ self .assertNotIn (owner1 , cache ._cache [('b' , )].owners )
566+
567+ # Verify that owner2 does not own 'a'
568+ self .assertNotIn (owner2 , cache ._cache [('a' , )].owners )
569+
570+ # Purging owner2 should immediately destroy/remove 'b'
571+ cache .purge (owner2 )
572+ self .assertNotIn (('b' , ), cache ._cache )
573+
574+ # 'a' is still alive because owner1 is still registered
575+ self .assertIn (('a' , ), cache ._cache )
576+
577+ # Purging owner1 should destroy/remove 'a'
578+ cache .purge (owner1 )
579+ self .assertNotIn (('a' , ), cache ._cache )
580+
581+ def test_context_owner_owns_all_keys (self ):
582+ cache = subprocess_server ._SharedCache (self .with_prefix , lambda x : None )
583+ # owner1 is a non-context owner (e.g., prism)
584+ owner1 = cache .register (is_context = False )
585+
586+ # owner2 is a context owner (e.g., cache_subprocesses)
587+ owner2 = cache .register (is_context = True )
588+
589+ # owner3 is another non-context owner (e.g., short-lived service)
590+ owner3 = cache .register (is_context = False )
591+
592+ # owner3 requests 'b'
593+ b = cache .get ('b' , owner = owner3 )
594+
595+ # owner2 (context) should own 'b'
596+ self .assertIn (owner2 , cache ._cache [('b' , )].owners )
597+
598+ # owner1 (non-context) should NOT own 'b'
599+ self .assertNotIn (owner1 , cache ._cache [('b' , )].owners )
600+
554601
555602if __name__ == '__main__' :
556603 unittest .main ()
0 commit comments