1
- use rustc_target:: abi:: Size ;
2
-
3
1
use crate :: * ;
4
2
5
3
// 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 .
7
5
8
- fn deref_lock < ' mir , ' tcx : ' mir > (
6
+ fn srwlock_get_or_create_id < ' mir , ' tcx : ' mir > (
9
7
ecx : & mut MiriEvalContext < ' mir , ' tcx > ,
10
8
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
+ }
17
20
}
18
21
19
22
impl < ' mir , ' tcx > EvalContextExt < ' mir , ' tcx > for crate :: MiriEvalContext < ' mir , ' tcx > { }
@@ -24,17 +27,20 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
24
27
lock_op : OpTy < ' tcx , Tag > ,
25
28
) -> InterpResult < ' tcx > {
26
29
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) ;
35
42
} else {
36
- // Lock is already held. This is a deadlock.
37
- throw_machine_stop ! ( TerminationInfo :: Deadlock ) ;
43
+ this. rwlock_writer_lock ( id, active_thread) ;
38
44
}
39
45
40
46
Ok ( ( ) )
@@ -46,18 +52,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
46
52
lock_op : OpTy < ' tcx , Tag > ,
47
53
) -> InterpResult < ' tcx , u8 > {
48
54
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) {
59
59
// Lock is already held.
60
60
Ok ( 0 )
61
+ } else {
62
+ this. rwlock_writer_lock ( id, active_thread) ;
63
+ Ok ( 1 )
61
64
}
62
65
}
63
66
@@ -67,17 +70,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
67
70
lock_op : OpTy < ' tcx , Tag > ,
68
71
) -> InterpResult < ' tcx > {
69
72
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" ) ;
81
79
}
82
80
83
81
Ok ( ( ) )
@@ -89,21 +87,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
89
87
lock_op : OpTy < ' tcx , Tag > ,
90
88
) -> InterpResult < ' tcx > {
91
89
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 ( ) ;
93
92
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) ;
99
95
} 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) ;
107
97
}
108
98
109
99
Ok ( ( ) )
@@ -115,21 +105,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
115
105
lock_op : OpTy < ' tcx , Tag > ,
116
106
) -> InterpResult < ' tcx , u8 > {
117
107
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 ( ) ;
119
110
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) {
124
112
Ok ( 0 )
125
113
} 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) ;
133
115
Ok ( 1 )
134
116
}
135
117
}
@@ -140,20 +122,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
140
122
lock_op : OpTy < ' tcx , Tag > ,
141
123
) -> InterpResult < ' tcx > {
142
124
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" ) ;
157
131
}
158
132
159
133
Ok ( ( ) )
0 commit comments