1- use rustc_target:: abi:: Size ;
2-
31use crate :: * ;
42
53// Locks are pointer-sized pieces of data, initialized to 0.
6- // We use them to count readers, with usize::MAX representing the write-locked state .
4+ // We use the first 4 bytes to store the RwLockId .
75
8- fn deref_lock < ' mir , ' tcx : ' mir > (
6+ fn srwlock_get_or_create_id < ' mir , ' tcx : ' mir > (
97 ecx : & mut MiriEvalContext < ' mir , ' tcx > ,
108 lock_op : OpTy < ' tcx , Tag > ,
11- ) -> InterpResult < ' tcx , MPlaceTy < ' tcx , Tag > > {
12- // `lock` is a pointer to `void*`; cast it to a pointer to `usize`.
13- let lock = ecx. deref_operand ( lock_op) ?;
14- let usize = ecx. machine . layouts . usize ;
15- assert_eq ! ( lock. layout. size, usize . size) ;
16- Ok ( lock. offset ( Size :: ZERO , MemPlaceMeta :: None , usize, ecx) ?)
9+ ) -> InterpResult < ' tcx , RwLockId > {
10+ let id = ecx. read_scalar_at_offset ( lock_op, 0 , ecx. machine . layouts . u32 ) ?. to_u32 ( ) ?;
11+ if id == 0 {
12+ // 0 is a default value and also not a valid rwlock id. Need to allocate
13+ // a new rwlock.
14+ let id = ecx. rwlock_create ( ) ;
15+ ecx. write_scalar_at_offset ( lock_op, 0 , id. to_u32_scalar ( ) , ecx. machine . layouts . u32 ) ?;
16+ Ok ( id)
17+ } else {
18+ Ok ( RwLockId :: from_u32 ( id) )
19+ }
1720}
1821
1922impl < ' mir , ' tcx > EvalContextExt < ' mir , ' tcx > for crate :: MiriEvalContext < ' mir , ' tcx > { }
@@ -24,17 +27,20 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
2427 lock_op : OpTy < ' tcx , Tag > ,
2528 ) -> InterpResult < ' tcx > {
2629 let this = self . eval_context_mut ( ) ;
27- assert_eq ! ( this. get_total_thread_count( ) , 1 , "concurrency on Windows is not supported" ) ;
28-
29- let lock = deref_lock ( this, lock_op) ?;
30- let lock_val = this. read_scalar ( lock. into ( ) ) ?. to_machine_usize ( this) ?;
31- if lock_val == 0 {
32- // Currently not locked. Lock it.
33- let new_val = Scalar :: from_machine_usize ( this. machine_usize_max ( ) , this) ;
34- this. write_scalar ( new_val, lock. into ( ) ) ?;
30+ let id = srwlock_get_or_create_id ( this, lock_op) ?;
31+ let active_thread = this. get_active_thread ( ) ;
32+
33+ if this. rwlock_is_locked ( id) {
34+ // Note: this will deadlock if the lock is already locked by this
35+ // thread in any way.
36+ //
37+ // FIXME: Detect and report the deadlock proactively. (We currently
38+ // report the deadlock only when no thread can continue execution,
39+ // but we could detect that this lock is already locked and report
40+ // an error.)
41+ this. rwlock_enqueue_and_block_writer ( id, active_thread) ;
3542 } else {
36- // Lock is already held. This is a deadlock.
37- throw_machine_stop ! ( TerminationInfo :: Deadlock ) ;
43+ this. rwlock_writer_lock ( id, active_thread) ;
3844 }
3945
4046 Ok ( ( ) )
@@ -46,18 +52,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
4652 lock_op : OpTy < ' tcx , Tag > ,
4753 ) -> InterpResult < ' tcx , u8 > {
4854 let this = self . eval_context_mut ( ) ;
49- assert_eq ! ( this. get_total_thread_count( ) , 1 , "concurrency on Windows is not supported" ) ;
50-
51- let lock = deref_lock ( this, lock_op) ?;
52- let lock_val = this. read_scalar ( lock. into ( ) ) ?. to_machine_usize ( this) ?;
53- if lock_val == 0 {
54- // Currently not locked. Lock it.
55- let new_val = this. machine_usize_max ( ) ;
56- this. write_scalar ( Scalar :: from_machine_usize ( new_val, this) , lock. into ( ) ) ?;
57- Ok ( 1 )
58- } else {
55+ let id = srwlock_get_or_create_id ( this, lock_op) ?;
56+ let active_thread = this. get_active_thread ( ) ;
57+
58+ if this. rwlock_is_locked ( id) {
5959 // Lock is already held.
6060 Ok ( 0 )
61+ } else {
62+ this. rwlock_writer_lock ( id, active_thread) ;
63+ Ok ( 1 )
6164 }
6265 }
6366
@@ -67,17 +70,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
6770 lock_op : OpTy < ' tcx , Tag > ,
6871 ) -> InterpResult < ' tcx > {
6972 let this = self . eval_context_mut ( ) ;
70- assert_eq ! ( this. get_total_thread_count( ) , 1 , "concurrency on Windows is not supported" ) ;
71-
72- let lock = deref_lock ( this, lock_op) ?;
73- let lock_val = this. read_scalar ( lock. into ( ) ) ?. to_machine_usize ( this) ?;
74- if lock_val == this. machine_usize_max ( ) {
75- // Currently locked. Unlock it.
76- let new_val = 0 ;
77- this. write_scalar ( Scalar :: from_machine_usize ( new_val, this) , lock. into ( ) ) ?;
78- } else {
79- // Lock is not locked.
80- throw_ub_format ! ( "calling ReleaseSRWLockExclusive on an SRWLock that is not exclusively locked" ) ;
73+ let id = srwlock_get_or_create_id ( this, lock_op) ?;
74+ let active_thread = this. get_active_thread ( ) ;
75+
76+ if !this. rwlock_writer_unlock ( id, active_thread) {
77+ // The docs do not say anything about this case, but it seems better to not allow it.
78+ throw_ub_format ! ( "calling ReleaseSRWLockExclusive on an SRWLock that is not exclusively locked by the current thread" ) ;
8179 }
8280
8381 Ok ( ( ) )
@@ -89,21 +87,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
8987 lock_op : OpTy < ' tcx , Tag > ,
9088 ) -> InterpResult < ' tcx > {
9189 let this = self . eval_context_mut ( ) ;
92- assert_eq ! ( this. get_total_thread_count( ) , 1 , "concurrency on Windows is not supported" ) ;
90+ let id = srwlock_get_or_create_id ( this, lock_op) ?;
91+ let active_thread = this. get_active_thread ( ) ;
9392
94- let lock = deref_lock ( this, lock_op) ?;
95- let lock_val = this. read_scalar ( lock. into ( ) ) ?. to_machine_usize ( this) ?;
96- if lock_val == this. machine_usize_max ( ) {
97- // Currently write locked. This is a deadlock.
98- throw_machine_stop ! ( TerminationInfo :: Deadlock ) ;
93+ if this. rwlock_is_write_locked ( id) {
94+ this. rwlock_enqueue_and_block_reader ( id, active_thread) ;
9995 } else {
100- // Bump up read counter (cannot overflow as we just checkd against usize::MAX);
101- let new_val = lock_val+1 ;
102- // Make sure this does not reach the "write locked" flag.
103- if new_val == this. machine_usize_max ( ) {
104- throw_unsup_format ! ( "SRWLock read-acquired too many times" ) ;
105- }
106- this. write_scalar ( Scalar :: from_machine_usize ( new_val, this) , lock. into ( ) ) ?;
96+ this. rwlock_reader_lock ( id, active_thread) ;
10797 }
10898
10999 Ok ( ( ) )
@@ -115,21 +105,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
115105 lock_op : OpTy < ' tcx , Tag > ,
116106 ) -> InterpResult < ' tcx , u8 > {
117107 let this = self . eval_context_mut ( ) ;
118- assert_eq ! ( this. get_total_thread_count( ) , 1 , "concurrency on Windows is not supported" ) ;
108+ let id = srwlock_get_or_create_id ( this, lock_op) ?;
109+ let active_thread = this. get_active_thread ( ) ;
119110
120- let lock = deref_lock ( this, lock_op) ?;
121- let lock_val = this. read_scalar ( lock. into ( ) ) ?. to_machine_usize ( this) ?;
122- if lock_val == this. machine_usize_max ( ) {
123- // Currently write locked.
111+ if this. rwlock_is_write_locked ( id) {
124112 Ok ( 0 )
125113 } else {
126- // Bump up read counter (cannot overflow as we just checkd against usize::MAX);
127- let new_val = lock_val+1 ;
128- // Make sure this does not reach the "write locked" flag.
129- if new_val == this. machine_usize_max ( ) {
130- throw_unsup_format ! ( "SRWLock read-acquired too many times" ) ;
131- }
132- this. write_scalar ( Scalar :: from_machine_usize ( new_val, this) , lock. into ( ) ) ?;
114+ this. rwlock_reader_lock ( id, active_thread) ;
133115 Ok ( 1 )
134116 }
135117 }
@@ -140,20 +122,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
140122 lock_op : OpTy < ' tcx , Tag > ,
141123 ) -> InterpResult < ' tcx > {
142124 let this = self . eval_context_mut ( ) ;
143- assert_eq ! ( this. get_total_thread_count( ) , 1 , "concurrency on Windows is not supported" ) ;
144-
145- let lock = deref_lock ( this, lock_op) ?;
146- let lock_val = this. read_scalar ( lock. into ( ) ) ?. to_machine_usize ( this) ?;
147- if lock_val == this. machine_usize_max ( ) {
148- // Currently write locked. This is a UB.
149- throw_ub_format ! ( "calling ReleaseSRWLockShared on write-locked SRWLock" ) ;
150- } else if lock_val == 0 {
151- // Currently not locked at all.
152- throw_ub_format ! ( "calling ReleaseSRWLockShared on unlocked SRWLock" ) ;
153- } else {
154- // Decrement read counter (cannot overflow as we just checkd against 0);
155- let new_val = lock_val-1 ;
156- this. write_scalar ( Scalar :: from_machine_usize ( new_val, this) , lock. into ( ) ) ?;
125+ let id = srwlock_get_or_create_id ( this, lock_op) ?;
126+ let active_thread = this. get_active_thread ( ) ;
127+
128+ if !this. rwlock_reader_unlock ( id, active_thread) {
129+ // The docs do not say anything about this case, but it seems better to not allow it.
130+ throw_ub_format ! ( "calling ReleaseSRWLockShared on an SRWLock that is not locked by the current thread" ) ;
157131 }
158132
159133 Ok ( ( ) )
0 commit comments