@@ -29,19 +29,14 @@ pub enum AccessKind {
29
29
/// Information about a lock that is currently held.
30
30
#[ derive( Clone , Debug ) ]
31
31
struct LockInfo {
32
- suspended : Vec < SuspendedWriteLock > ,
32
+ /// Stores for which lifetimes (of the original write lock) we got
33
+ /// which suspensions.
34
+ suspended : HashMap < DynamicLifetime , Vec < CodeExtent > > ,
35
+ /// The current state of the lock that's actually effective.
33
36
active : Lock ,
34
37
}
35
38
36
- #[ derive( Clone , Debug ) ]
37
- struct SuspendedWriteLock {
38
- /// Original lifetime of the lock that is now suspended
39
- lft : DynamicLifetime ,
40
- /// Regions that all have to end to reenable this suspension
41
- suspensions : Vec < CodeExtent > ,
42
- }
43
-
44
- #[ derive( Clone , Debug ) ]
39
+ #[ derive( Clone , Debug , PartialEq ) ]
45
40
pub enum Lock {
46
41
NoLock ,
47
42
WriteLock ( DynamicLifetime ) ,
@@ -57,7 +52,7 @@ impl Default for LockInfo {
57
52
58
53
impl LockInfo {
59
54
fn new ( lock : Lock ) -> LockInfo {
60
- LockInfo { suspended : Vec :: new ( ) , active : lock }
55
+ LockInfo { suspended : HashMap :: new ( ) , active : lock }
61
56
}
62
57
63
58
fn access_permitted ( & self , frame : Option < usize > , access : AccessKind ) -> bool {
@@ -513,17 +508,17 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
513
508
}
514
509
515
510
/// Release or suspend a write lock of the given lifetime prematurely.
516
- /// When releasing, if there is no write lock or someone else's write lock, that's an error.
511
+ /// When releasing, if there is a read lock or someone else's write lock, that's an error.
512
+ /// We *do* accept relasing a NoLock, as this can happen when a local is first acquired and later force_allocate'd.
517
513
/// When suspending, the same cases are fine; we just register an additional suspension.
518
- pub ( crate ) fn release_write_lock ( & mut self , ptr : MemoryPointer , len : u64 ,
514
+ pub ( crate ) fn suspend_write_lock ( & mut self , ptr : MemoryPointer , len : u64 ,
519
515
lock_region : Option < CodeExtent > , suspend : Option < CodeExtent > ) -> EvalResult < ' tcx > {
520
516
assert ! ( len > 0 ) ;
521
517
let cur_frame = self . cur_frame ;
522
518
let lock_lft = DynamicLifetime { frame : cur_frame, region : lock_region } ;
523
519
let alloc = self . get_mut_unchecked ( ptr. alloc_id ) ?;
524
520
525
521
' locks: for lock in alloc. locks . iter_mut ( ptr. offset , len) {
526
- trace ! ( "Releasing {:?}" , lock) ;
527
522
let is_our_lock = match lock. active {
528
523
WriteLock ( lft) => {
529
524
lft == lock_lft
@@ -533,30 +528,25 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
533
528
}
534
529
} ;
535
530
if is_our_lock {
531
+ trace ! ( "Releasing {:?} at {:?}" , lock. active, lock_lft) ;
536
532
// Disable the lock
537
533
lock. active = NoLock ;
534
+ } else {
535
+ trace ! ( "Not touching {:?} at {:?} as its not our lock" , lock. active, lock_lft) ;
538
536
}
539
537
match suspend {
540
538
Some ( suspend_region) => {
541
- if is_our_lock {
542
- // We just released this lock, so add a new suspension
543
- lock. suspended . push ( SuspendedWriteLock { lft : lock_lft, suspensions : vec ! [ suspend_region] } ) ;
544
- } else {
545
- // Find our lock in the suspended ones
546
- for suspended_lock in lock. suspended . iter_mut ( ) . rev ( ) {
547
- if suspended_lock. lft == lock_lft {
548
- // Found it!
549
- suspended_lock. suspensions . push ( suspend_region) ;
550
- continue ' locks;
551
- }
552
- }
553
- // We did not find it. Someone else had the lock and we have not suspended it, that's just wrong.
554
- return err ! ( InvalidMemoryLockRelease { ptr, len, frame: cur_frame, lock: lock. active. clone( ) } ) ;
555
- }
539
+ trace ! ( "Adding suspension to {:?} at {:?}" , lock. active, lock_lft) ;
540
+ // We just released this lock, so add a new suspension.
541
+ // FIXME: Really, if there ever already is a suspension when is_our_lock, or if there is no suspension when !is_our_lock, something is amiss.
542
+ // But this model is not good enough yet to prevent that.
543
+ lock. suspended . entry ( lock_lft)
544
+ . or_insert_with ( || Vec :: new ( ) )
545
+ . push ( suspend_region) ;
556
546
}
557
547
None => {
558
- // If we do not suspend, make sure we actually released something
559
- if !is_our_lock {
548
+ // Make sure we did not try to release someone else's lock.
549
+ if !is_our_lock && lock . active != NoLock {
560
550
return err ! ( InvalidMemoryLockRelease { ptr, len, frame: cur_frame, lock: lock. active. clone( ) } ) ;
561
551
}
562
552
}
@@ -577,34 +567,33 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
577
567
let alloc = self . get_mut_unchecked ( ptr. alloc_id ) ?;
578
568
579
569
for lock in alloc. locks . iter_mut ( ptr. offset , len) {
580
- // If we have a suspension here, it will be the topmost one
581
- let ( got_the_lock, pop_suspension) = match lock. suspended . last_mut ( ) {
582
- None => ( true , false ) ,
583
- Some ( suspended_lock) => {
584
- if suspended_lock. lft == lock_lft {
585
- // That's us! Remove suspension (it should be in there). The same suspension can
586
- // occur multiple times (when there are multiple shared borrows of this that have the same
587
- // lifetime); only remove one of them.
588
- let idx = match suspended_lock. suspensions . iter ( ) . enumerate ( ) . find ( |& ( _, re) | re == & suspended_region) {
589
- None => // TODO: Can the user trigger this?
590
- bug ! ( "We have this lock suspended, but not for the given region." ) ,
591
- Some ( ( idx, _) ) => idx
592
- } ;
593
- suspended_lock. suspensions . remove ( idx) ;
594
- let got_lock = suspended_lock. suspensions . is_empty ( ) ;
595
- ( got_lock, got_lock)
596
- } else {
597
- // Someone else's suspension up top, we should be able to grab the lock
598
- ( true , false )
570
+ // Check if we have a suspension here
571
+ let ( got_the_lock, remove_suspension) = match lock. suspended . get_mut ( & lock_lft) {
572
+ None => {
573
+ trace ! ( "No suspension around, we can just acquire" ) ;
574
+ ( true , false )
575
+ }
576
+ Some ( suspensions) => {
577
+ trace ! ( "Found suspension of {:?}, removing it" , lock_lft) ;
578
+ // That's us! Remove suspension (it should be in there). The same suspension can
579
+ // occur multiple times (when there are multiple shared borrows of this that have the same
580
+ // lifetime); only remove one of them.
581
+ let idx = match suspensions. iter ( ) . enumerate ( ) . find ( |& ( _, re) | re == & suspended_region) {
582
+ None => // TODO: Can the user trigger this?
583
+ bug ! ( "We have this lock suspended, but not for the given region." ) ,
584
+ Some ( ( idx, _) ) => idx
585
+ } ;
586
+ suspensions. remove ( idx) ;
587
+ let got_lock = suspensions. is_empty ( ) ;
588
+ if got_lock {
589
+ trace ! ( "All suspensions are gone, we can have the lock again" ) ;
599
590
}
591
+ ( got_lock, got_lock)
600
592
}
601
593
} ;
602
- if pop_suspension { // with NLL; we could do that up in the match above...
603
- lock. suspended . pop ( ) ;
604
- } else {
605
- // Sanity check: Our lock should not be in the suspension list
606
- let found = lock. suspended . iter ( ) . find ( |suspended_lock| suspended_lock. lft == lock_lft) ;
607
- assert ! ( found. is_none( ) ) ;
594
+ if remove_suspension { // with NLL, we could do that up in the match above...
595
+ assert ! ( got_the_lock) ;
596
+ lock. suspended . remove ( & lock_lft) ;
608
597
}
609
598
if got_the_lock {
610
599
match lock. active {
@@ -653,7 +642,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
653
642
lock. active = NoLock ;
654
643
}
655
644
// Also clean up suspended write locks
656
- lock. suspended . retain ( |suspended_lock | !has_ended ( & suspended_lock . lft ) ) ;
645
+ lock. suspended . retain ( |lft , _suspensions | !has_ended ( lft) ) ;
657
646
}
658
647
// Clean up the map
659
648
alloc. locks . retain ( |lock| {
0 commit comments