@@ -286,6 +286,22 @@ fn condattr_get_clock_id<'tcx>(
286
286
. to_i32 ( )
287
287
}
288
288
289
+ fn translate_clock_id < ' tcx > (
290
+ ecx : & MiriInterpCx < ' tcx > ,
291
+ raw_id : i32 ,
292
+ ) -> InterpResult < ' tcx , ClockType > {
293
+ // To ensure compatibility with PTHREAD_COND_INITIALIZER on all platforms,
294
+ // we can't just compare with CLOCK_REALTIME: on Solarish, PTHREAD_COND_INITIALIZER
295
+ // makes the clock 0 but CLOCK_REALTIME is 3.
296
+ Ok ( if raw_id == ecx. eval_libc_i32 ( "CLOCK_REALTIME" ) || raw_id == 0 {
297
+ ClockType :: Realtime
298
+ } else if raw_id == ecx. eval_libc_i32 ( "CLOCK_MONOTONIC" ) {
299
+ ClockType :: Monotonic
300
+ } else {
301
+ throw_unsup_format ! ( "unsupported type of clock id: {raw_id}" ) ;
302
+ } )
303
+ }
304
+
289
305
fn condattr_set_clock_id < ' tcx > (
290
306
ecx : & mut MiriInterpCx < ' tcx > ,
291
307
attr_ptr : & OpTy < ' tcx > ,
@@ -331,16 +347,6 @@ fn cond_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> {
331
347
Ok ( offset)
332
348
}
333
349
334
- /// Determines whether this clock represents the real-time clock, CLOCK_REALTIME.
335
- fn is_cond_clock_realtime < ' tcx > ( ecx : & MiriInterpCx < ' tcx > , clock_id : i32 ) -> bool {
336
- // To ensure compatibility with PTHREAD_COND_INITIALIZER on all platforms,
337
- // we can't just compare with CLOCK_REALTIME: on Solarish, PTHREAD_COND_INITIALIZER
338
- // makes the clock 0 but CLOCK_REALTIME is 3.
339
- // However, we need to always be able to distinguish this from CLOCK_MONOTONIC.
340
- clock_id == ecx. eval_libc_i32 ( "CLOCK_REALTIME" )
341
- || ( clock_id == 0 && clock_id != ecx. eval_libc_i32 ( "CLOCK_MONOTONIC" ) )
342
- }
343
-
344
350
fn cond_clock_offset < ' tcx > ( ecx : & MiriInterpCx < ' tcx > ) -> u64 {
345
351
// macOS doesn't have a clock attribute, but to keep the code uniform we store
346
352
// a clock ID in the pthread_cond_t anyway. There's enough space.
@@ -355,20 +361,30 @@ fn cond_clock_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> u64 {
355
361
. offset ( Size :: from_bytes ( offset) , ecx. machine . layouts . i32 , ecx)
356
362
. unwrap ( ) ;
357
363
let id = ecx. read_scalar ( & id_field) . unwrap ( ) . to_i32 ( ) . unwrap ( ) ;
364
+ let id = translate_clock_id ( ecx, id) . expect ( "static initializer should be valid" ) ;
358
365
assert ! (
359
- is_cond_clock_realtime ( ecx , id ) ,
366
+ matches! ( id , ClockType :: Realtime ) ,
360
367
"PTHREAD_COND_INITIALIZER is incompatible with our pthread_cond layout: clock is not CLOCK_REALTIME"
361
368
) ;
362
369
}
363
370
364
371
offset
365
372
}
366
373
374
+ #[ derive( Debug , Clone , Copy ) ]
375
+ enum ClockType {
376
+ Realtime ,
377
+ Monotonic ,
378
+ }
379
+
367
380
#[ derive( Debug ) ]
368
381
/// Additional data that we attach with each cond instance.
369
382
struct AdditionalCondData {
370
383
/// The address of the cond.
371
384
address : u64 ,
385
+
386
+ /// The clock type of the cond.
387
+ clock_type : ClockType ,
372
388
}
373
389
374
390
fn cond_get_id < ' tcx > (
@@ -377,8 +393,14 @@ fn cond_get_id<'tcx>(
377
393
) -> InterpResult < ' tcx , CondvarId > {
378
394
let cond = ecx. deref_pointer ( cond_ptr) ?;
379
395
let address = cond. ptr ( ) . addr ( ) . bytes ( ) ;
380
- let id = ecx. condvar_get_or_create_id ( & cond, cond_id_offset ( ecx) ?, |_| {
381
- Ok ( Some ( Box :: new ( AdditionalCondData { address } ) ) )
396
+ let id = ecx. condvar_get_or_create_id ( & cond, cond_id_offset ( ecx) ?, |ecx| {
397
+ let raw_id = if ecx. tcx . sess . target . os == "macos" {
398
+ ecx. eval_libc_i32 ( "CLOCK_REALTIME" )
399
+ } else {
400
+ cond_get_clock_id ( ecx, cond_ptr) ?
401
+ } ;
402
+ let clock_type = translate_clock_id ( ecx, raw_id) ?;
403
+ Ok ( Some ( Box :: new ( AdditionalCondData { address, clock_type } ) ) )
382
404
} ) ?;
383
405
384
406
// Check that the mutex has not been moved since last use.
@@ -405,20 +427,6 @@ fn cond_get_clock_id<'tcx>(
405
427
. to_i32 ( )
406
428
}
407
429
408
- fn cond_set_clock_id < ' tcx > (
409
- ecx : & mut MiriInterpCx < ' tcx > ,
410
- cond_ptr : & OpTy < ' tcx > ,
411
- clock_id : i32 ,
412
- ) -> InterpResult < ' tcx , ( ) > {
413
- ecx. deref_pointer_and_write (
414
- cond_ptr,
415
- cond_clock_offset ( ecx) ,
416
- Scalar :: from_i32 ( clock_id) ,
417
- ecx. libc_ty_layout ( "pthread_cond_t" ) ,
418
- ecx. machine . layouts . i32 ,
419
- )
420
- }
421
-
422
430
impl < ' tcx > EvalContextExt < ' tcx > for crate :: MiriInterpCx < ' tcx > { }
423
431
pub trait EvalContextExt < ' tcx > : crate :: MiriInterpCxExt < ' tcx > {
424
432
fn pthread_mutexattr_init ( & mut self , attr_op : & OpTy < ' tcx > ) -> InterpResult < ' tcx , ( ) > {
@@ -827,17 +835,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
827
835
} else {
828
836
condattr_get_clock_id ( this, attr_op) ?
829
837
} ;
838
+ let clock_type = translate_clock_id ( this, clock_id) ?;
830
839
831
840
let cond = this. deref_pointer ( cond_op) ?;
832
841
let address = cond. ptr ( ) . addr ( ) . bytes ( ) ;
833
842
this. condvar_create (
834
843
& cond,
835
844
cond_id_offset ( this) ?,
836
- Some ( Box :: new ( AdditionalCondData { address } ) ) ,
845
+ Some ( Box :: new ( AdditionalCondData { address, clock_type } ) ) ,
837
846
) ?;
838
847
839
- cond_set_clock_id ( this, cond_op, clock_id) ?;
840
-
841
848
Ok ( ( ) )
842
849
}
843
850
@@ -891,7 +898,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
891
898
let mutex_id = mutex_get_id ( this, mutex_op) ?;
892
899
893
900
// Extract the timeout.
894
- let clock_id = cond_get_clock_id ( this, cond_op) ?;
901
+ let clock_type = this
902
+ . condvar_get_data :: < AdditionalCondData > ( id)
903
+ . expect ( "additional data should always be present for pthreads" )
904
+ . clock_type ;
895
905
let duration = match this
896
906
. read_timespec ( & this. deref_pointer_as ( abstime_op, this. libc_ty_layout ( "timespec" ) ) ?) ?
897
907
{
@@ -902,13 +912,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
902
912
return Ok ( ( ) ) ;
903
913
}
904
914
} ;
905
- let timeout_clock = if is_cond_clock_realtime ( this , clock_id ) {
915
+ let timeout_clock = if matches ! ( clock_type , ClockType :: Realtime ) {
906
916
this. check_no_isolation ( "`pthread_cond_timedwait` with `CLOCK_REALTIME`" ) ?;
907
917
TimeoutClock :: RealTime
908
- } else if clock_id == this . eval_libc_i32 ( "CLOCK_MONOTONIC" ) {
918
+ } else if let ClockType :: Monotonic = clock_type {
909
919
TimeoutClock :: Monotonic
910
920
} else {
911
- throw_unsup_format ! ( "unsupported clock id: {}" , clock_id ) ;
921
+ throw_unsup_format ! ( "unsupported clock id: {clock_type:?}" ) ;
912
922
} ;
913
923
914
924
this. condvar_wait (
@@ -933,7 +943,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
933
943
934
944
// Destroying an uninit pthread_cond is UB, so check to make sure it's not uninit.
935
945
cond_get_id ( this, cond_op) ?;
936
- cond_get_clock_id ( this, cond_op) ?;
937
946
938
947
// This might lead to false positives, see comment in pthread_mutexattr_destroy
939
948
this. write_uninit ( & this. deref_pointer_as ( cond_op, this. libc_ty_layout ( "pthread_cond_t" ) ) ?) ?;
0 commit comments