1
+ use std:: sync:: atomic:: { AtomicBool , Ordering } ;
1
2
use std:: time:: SystemTime ;
2
3
4
+ use rustc_target:: abi:: Size ;
5
+
3
6
use crate :: concurrency:: thread:: MachineCallback ;
4
7
use crate :: * ;
5
8
@@ -71,23 +74,54 @@ fn is_mutex_kind_normal<'mir, 'tcx: 'mir>(
71
74
// - id: u32
72
75
// - kind: i32
73
76
74
- #[ inline]
75
77
fn mutex_id_offset < ' mir , ' tcx : ' mir > ( ecx : & MiriInterpCx < ' mir , ' tcx > ) -> InterpResult < ' tcx , u64 > {
76
- // When adding a new OS, make sure its PTHREAD_MUTEX_INITIALIZER is 0 for this offset
77
- // (and the `mutex_kind_offset` below).
78
- Ok ( match & * ecx. tcx . sess . target . os {
78
+ let offset = match & * ecx. tcx . sess . target . os {
79
79
"linux" | "illumos" | "solaris" => 0 ,
80
80
// macOS stores a signature in the first bytes, so we have to move to offset 4.
81
81
"macos" => 4 ,
82
82
os => throw_unsup_format ! ( "`pthread_mutex` is not supported on {os}" ) ,
83
- } )
83
+ } ;
84
+
85
+ // Sanity-check this against PTHREAD_MUTEX_INITIALIZER (but only once):
86
+ // the id must start out as 0.
87
+ static SANITY : AtomicBool = AtomicBool :: new ( false ) ;
88
+ if !SANITY . swap ( true , Ordering :: Relaxed ) {
89
+ let static_initializer = ecx. eval_path ( & [ "libc" , "PTHREAD_MUTEX_INITIALIZER" ] ) ;
90
+ let id_field = static_initializer
91
+ . offset ( Size :: from_bytes ( offset) , ecx. machine . layouts . u32 , ecx)
92
+ . unwrap ( ) ;
93
+ let id = ecx. read_scalar ( & id_field) . unwrap ( ) . to_u32 ( ) . unwrap ( ) ;
94
+ assert_eq ! (
95
+ id, 0 ,
96
+ "PTHREAD_MUTEX_INITIALIZER is incompatible with our pthread_mutex layout: id is not 0"
97
+ ) ;
98
+ }
99
+
100
+ Ok ( offset)
84
101
}
85
102
86
- #[ inline]
87
103
fn mutex_kind_offset < ' mir , ' tcx : ' mir > ( ecx : & MiriInterpCx < ' mir , ' tcx > ) -> u64 {
88
104
// These offsets are picked for compatibility with Linux's static initializer
89
105
// macros, e.g. PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP.)
90
- if ecx. pointer_size ( ) . bytes ( ) == 8 { 16 } else { 12 }
106
+ let offset = if ecx. pointer_size ( ) . bytes ( ) == 8 { 16 } else { 12 } ;
107
+
108
+ // Sanity-check this against PTHREAD_MUTEX_INITIALIZER (but only once):
109
+ // the kind must start out as PTHREAD_MUTEX_DEFAULT.
110
+ static SANITY : AtomicBool = AtomicBool :: new ( false ) ;
111
+ if !SANITY . swap ( true , Ordering :: Relaxed ) {
112
+ let static_initializer = ecx. eval_path ( & [ "libc" , "PTHREAD_MUTEX_INITIALIZER" ] ) ;
113
+ let kind_field = static_initializer
114
+ . offset ( Size :: from_bytes ( mutex_kind_offset ( ecx) ) , ecx. machine . layouts . i32 , ecx)
115
+ . unwrap ( ) ;
116
+ let kind = ecx. read_scalar ( & kind_field) . unwrap ( ) . to_i32 ( ) . unwrap ( ) ;
117
+ assert_eq ! (
118
+ kind,
119
+ ecx. eval_libc_i32( "PTHREAD_MUTEX_DEFAULT" ) ,
120
+ "PTHREAD_MUTEX_INITIALIZER is incompatible with our pthread_mutex layout: kind is not PTHREAD_MUTEX_DEFAULT"
121
+ ) ;
122
+ }
123
+
124
+ offset
91
125
}
92
126
93
127
fn mutex_get_id < ' mir , ' tcx : ' mir > (
@@ -108,7 +142,7 @@ fn mutex_reset_id<'mir, 'tcx: 'mir>(
108
142
ecx. deref_pointer_and_write (
109
143
mutex_op,
110
144
mutex_id_offset ( ecx) ?,
111
- Scalar :: from_i32 ( 0 ) ,
145
+ Scalar :: from_u32 ( 0 ) ,
112
146
ecx. libc_ty_layout ( "pthread_mutex_t" ) ,
113
147
ecx. machine . layouts . u32 ,
114
148
)
@@ -145,15 +179,30 @@ fn mutex_set_kind<'mir, 'tcx: 'mir>(
145
179
// We ignore the platform layout and store our own fields:
146
180
// - id: u32
147
181
148
- #[ inline]
149
182
fn rwlock_id_offset < ' mir , ' tcx : ' mir > ( ecx : & MiriInterpCx < ' mir , ' tcx > ) -> InterpResult < ' tcx , u64 > {
150
- // When adding a new OS, make sure its PTHREAD_RWLOCK_INITIALIZER is 0 for this offset.
151
- Ok ( match & * ecx. tcx . sess . target . os {
183
+ let offset = match & * ecx. tcx . sess . target . os {
152
184
"linux" | "illumos" | "solaris" => 0 ,
153
185
// macOS stores a signature in the first bytes, so we have to move to offset 4.
154
186
"macos" => 4 ,
155
187
os => throw_unsup_format ! ( "`pthread_rwlock` is not supported on {os}" ) ,
156
- } )
188
+ } ;
189
+
190
+ // Sanity-check this against PTHREAD_RWLOCK_INITIALIZER (but only once):
191
+ // the id must start out as 0.
192
+ static SANITY : AtomicBool = AtomicBool :: new ( false ) ;
193
+ if !SANITY . swap ( true , Ordering :: Relaxed ) {
194
+ let static_initializer = ecx. eval_path ( & [ "libc" , "PTHREAD_RWLOCK_INITIALIZER" ] ) ;
195
+ let id_field = static_initializer
196
+ . offset ( Size :: from_bytes ( offset) , ecx. machine . layouts . u32 , ecx)
197
+ . unwrap ( ) ;
198
+ let id = ecx. read_scalar ( & id_field) . unwrap ( ) . to_u32 ( ) . unwrap ( ) ;
199
+ assert_eq ! (
200
+ id, 0 ,
201
+ "PTHREAD_RWLOCK_INITIALIZER is incompatible with our pthread_rwlock layout: id is not 0"
202
+ ) ;
203
+ }
204
+
205
+ Ok ( offset)
157
206
}
158
207
159
208
fn rwlock_get_id < ' mir , ' tcx : ' mir > (
@@ -214,21 +263,64 @@ fn condattr_set_clock_id<'mir, 'tcx: 'mir>(
214
263
// - id: u32
215
264
// - clock: i32
216
265
217
- #[ inline]
218
266
fn cond_id_offset < ' mir , ' tcx : ' mir > ( ecx : & MiriInterpCx < ' mir , ' tcx > ) -> InterpResult < ' tcx , u64 > {
219
- // When adding a new OS, make sure its PTHREAD_COND_INITIALIZER is 0 for this offset
220
- // (and the `COND_CLOCK_OFFSET` below is initialized to `CLOCK_REALTIME`).
221
- Ok ( match & * ecx. tcx . sess . target . os {
267
+ let offset = match & * ecx. tcx . sess . target . os {
222
268
"linux" | "illumos" | "solaris" => 0 ,
223
269
// macOS stores a signature in the first bytes, so we have to move to offset 4.
224
270
"macos" => 4 ,
225
271
os => throw_unsup_format ! ( "`pthread_cond` is not supported on {os}" ) ,
226
- } )
272
+ } ;
273
+
274
+ // Sanity-check this against PTHREAD_COND_INITIALIZER (but only once):
275
+ // the id must start out as 0.
276
+ static SANITY : AtomicBool = AtomicBool :: new ( false ) ;
277
+ if !SANITY . swap ( true , Ordering :: Relaxed ) {
278
+ let static_initializer = ecx. eval_path ( & [ "libc" , "PTHREAD_COND_INITIALIZER" ] ) ;
279
+ let id_field = static_initializer
280
+ . offset ( Size :: from_bytes ( offset) , ecx. machine . layouts . u32 , ecx)
281
+ . unwrap ( ) ;
282
+ let id = ecx. read_scalar ( & id_field) . unwrap ( ) . to_u32 ( ) . unwrap ( ) ;
283
+ assert_eq ! (
284
+ id, 0 ,
285
+ "PTHREAD_COND_INITIALIZER is incompatible with our pthread_cond layout: id is not 0"
286
+ ) ;
287
+ }
288
+
289
+ Ok ( offset)
290
+ }
291
+
292
+ /// Determines whether this clock represents the real-time clock, CLOCK_REALTIME.
293
+ fn is_cond_clock_realtime < ' mir , ' tcx : ' mir > ( ecx : & MiriInterpCx < ' mir , ' tcx > , clock_id : i32 ) -> bool {
294
+ // To ensure compatibility with PTHREAD_COND_INITIALIZER on all platforms,
295
+ // we can't just compare with CLOCK_REALTIME: on Solarish, PTHREAD_COND_INITIALIZER
296
+ // makes the clock 0 but CLOCK_REALTIME is 3.
297
+ // However, we need to always be able to distinguish this from CLOCK_MONOTONIC.
298
+ clock_id == ecx. eval_libc_i32 ( "CLOCK_REALTIME" )
299
+ || ( clock_id == 0 && clock_id != ecx. eval_libc_i32 ( "CLOCK_MONOTONIC" ) )
227
300
}
228
301
229
- // macOS doesn't have a clock attribute, but to keep the code uniform we store
230
- // a clock ID in the pthread_cond_t anyway. There's enough space.
231
- const COND_CLOCK_OFFSET : u64 = 8 ;
302
+ fn cond_clock_offset < ' mir , ' tcx : ' mir > ( ecx : & MiriInterpCx < ' mir , ' tcx > ) -> u64 {
303
+ // macOS doesn't have a clock attribute, but to keep the code uniform we store
304
+ // a clock ID in the pthread_cond_t anyway. There's enough space.
305
+ let offset = 8 ;
306
+
307
+ // Sanity-check this against PTHREAD_COND_INITIALIZER (but only once):
308
+ // the clock must start out as CLOCK_REALTIME.
309
+ static SANITY : AtomicBool = AtomicBool :: new ( false ) ;
310
+ if !SANITY . swap ( true , Ordering :: Relaxed ) {
311
+ let static_initializer = ecx. eval_path ( & [ "libc" , "PTHREAD_COND_INITIALIZER" ] ) ;
312
+ let id_field = static_initializer
313
+ . offset ( Size :: from_bytes ( offset) , ecx. machine . layouts . i32 , ecx)
314
+ . unwrap ( ) ;
315
+ let id = ecx. read_scalar ( & id_field) . unwrap ( ) . to_i32 ( ) . unwrap ( ) ;
316
+ assert ! (
317
+ is_cond_clock_realtime( ecx, id) ,
318
+ "PTHREAD_COND_INITIALIZER is incompatible with our pthread_cond layout: clock is not CLOCK_REALTIME"
319
+ ) ;
320
+ }
321
+
322
+ offset
323
+ }
232
324
233
325
fn cond_get_id < ' mir , ' tcx : ' mir > (
234
326
ecx : & mut MiriInterpCx < ' mir , ' tcx > ,
@@ -260,7 +352,7 @@ fn cond_get_clock_id<'mir, 'tcx: 'mir>(
260
352
) -> InterpResult < ' tcx , i32 > {
261
353
ecx. deref_pointer_and_read (
262
354
cond_op,
263
- COND_CLOCK_OFFSET ,
355
+ cond_clock_offset ( ecx ) ,
264
356
ecx. libc_ty_layout ( "pthread_cond_t" ) ,
265
357
ecx. machine . layouts . i32 ,
266
358
) ?
@@ -274,7 +366,7 @@ fn cond_set_clock_id<'mir, 'tcx: 'mir>(
274
366
) -> InterpResult < ' tcx , ( ) > {
275
367
ecx. deref_pointer_and_write (
276
368
cond_op,
277
- COND_CLOCK_OFFSET ,
369
+ cond_clock_offset ( ecx ) ,
278
370
Scalar :: from_i32 ( clock_id) ,
279
371
ecx. libc_ty_layout ( "pthread_cond_t" ) ,
280
372
ecx. machine . layouts . i32 ,
@@ -776,7 +868,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
776
868
let this = self . eval_context_mut ( ) ;
777
869
778
870
let attr = this. read_pointer ( attr_op) ?;
779
- // Default clock if att is null, and on macOS where there is no clock attribute.
871
+ // Default clock if `attr` is null, and on macOS where there is no clock attribute.
780
872
let clock_id = if this. ptr_is_null ( attr) ? || this. tcx . sess . target . os == "macos" {
781
873
this. eval_libc_i32 ( "CLOCK_REALTIME" )
782
874
} else {
@@ -858,7 +950,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
858
950
}
859
951
} ;
860
952
861
- let timeout_time = if clock_id == this . eval_libc_i32 ( "CLOCK_REALTIME" ) {
953
+ let timeout_time = if is_cond_clock_realtime ( this , clock_id ) {
862
954
this. check_no_isolation ( "`pthread_cond_timedwait` with `CLOCK_REALTIME`" ) ?;
863
955
CallbackTime :: RealTime ( SystemTime :: UNIX_EPOCH . checked_add ( duration) . unwrap ( ) )
864
956
} else if clock_id == this. eval_libc_i32 ( "CLOCK_MONOTONIC" ) {
0 commit comments