11//! Linux `eventfd` implementation. 
2- //! Currently just a stub. 
32use  std:: io; 
3+ use  std:: io:: { Error ,  ErrorKind } ; 
44
55use  rustc_target:: abi:: Endian ; 
66
77use  crate :: shims:: unix:: * ; 
8- use  crate :: * ; 
8+ use  crate :: { concurrency :: VClock ,   * } ; 
99
1010use  self :: shims:: unix:: fd:: FileDescriptor ; 
1111
12+ /// Minimum size of u8 array to hold u64 value. 
13+ const  U64_MIN_ARRAY_SIZE :  usize  = 8 ; 
14+ 
15+ /// Maximum value that the eventfd counter can hold. 
16+ const  MAX_COUNTER :  u64  = u64:: MAX  - 1 ; 
17+ 
1218/// A kind of file descriptor created by `eventfd`. 
1319/// The `Event` type isn't currently written to by `eventfd`. 
1420/// The interface is meant to keep track of objects associated 
@@ -20,7 +26,9 @@ use self::shims::unix::fd::FileDescriptor;
2026struct  Event  { 
2127    /// The object contains an unsigned 64-bit integer (uint64_t) counter that is maintained by the 
2228/// kernel. This counter is initialized with the value specified in the argument initval. 
23- val :  u64 , 
29+ counter :  u64 , 
30+     is_nonblock :  bool , 
31+     clock :  VClock , 
2432} 
2533
2634impl  FileDescription  for  Event  { 
@@ -35,6 +43,38 @@ impl FileDescription for Event {
3543        Ok ( Ok ( ( ) ) ) 
3644    } 
3745
46+     /// Read the counter in the buffer and return the counter if succeeded. 
47+ fn  read < ' tcx > ( 
48+         & mut  self , 
49+         _communicate_allowed :  bool , 
50+         bytes :  & mut  [ u8 ] , 
51+         ecx :  & mut  MiriInterpCx < ' tcx > , 
52+     )  -> InterpResult < ' tcx ,  io:: Result < usize > >  { 
53+         // Check the size of slice, and return error only if the size of the slice < 8. 
54+         let  Some ( bytes)  = bytes. first_chunk_mut :: < U64_MIN_ARRAY_SIZE > ( )  else  { 
55+             return  Ok ( Err ( Error :: from ( ErrorKind :: InvalidInput ) ) ) ; 
56+         } ; 
57+         // Block when counter == 0. 
58+         if  self . counter  == 0  { 
59+             if  self . is_nonblock  { 
60+                 return  Ok ( Err ( Error :: from ( ErrorKind :: WouldBlock ) ) ) ; 
61+             }  else  { 
62+                 //FIXME: blocking is not supported 
63+                 throw_unsup_format ! ( "eventfd: blocking is unsupported" ) ; 
64+             } 
65+         }  else  { 
66+             // Prevent false alarm in data race detection when doing synchronisation via eventfd. 
67+             ecx. acquire_clock ( & self . clock ) ; 
68+             // Return the counter in the host endianness using the buffer provided by caller. 
69+             * bytes = match  ecx. tcx . sess . target . endian  { 
70+                 Endian :: Little  => self . counter . to_le_bytes ( ) , 
71+                 Endian :: Big  => self . counter . to_be_bytes ( ) , 
72+             } ; 
73+             self . counter  = 0 ; 
74+             return  Ok ( Ok ( U64_MIN_ARRAY_SIZE ) ) ; 
75+         } 
76+     } 
77+ 
3878    /// A write call adds the 8-byte integer value supplied in 
3979/// its buffer (in native endianness) to the counter.  The maximum value that may be 
4080/// stored in the counter is the largest unsigned 64-bit value 
@@ -53,16 +93,37 @@ impl FileDescription for Event {
5393        bytes :  & [ u8 ] , 
5494        ecx :  & mut  MiriInterpCx < ' tcx > , 
5595    )  -> InterpResult < ' tcx ,  io:: Result < usize > >  { 
56-         let  bytes:  [ u8 ;  8 ]  = bytes. try_into ( ) . unwrap ( ) ;  // FIXME fail gracefully when this has the wrong size 
57-         // Convert from target endianness to host endianness. 
96+         // Check the size of slice, and return error only if the size of the slice < 8. 
97+         let  Some ( bytes)  = bytes. first_chunk :: < U64_MIN_ARRAY_SIZE > ( )  else  { 
98+             return  Ok ( Err ( Error :: from ( ErrorKind :: InvalidInput ) ) ) ; 
99+         } ; 
100+         // Convert from bytes to int according to host endianness. 
58101        let  num = match  ecx. tcx . sess . target . endian  { 
59-             Endian :: Little  => u64:: from_le_bytes ( bytes) , 
60-             Endian :: Big  => u64:: from_be_bytes ( bytes) , 
102+             Endian :: Little  => u64:: from_le_bytes ( * bytes) , 
103+             Endian :: Big  => u64:: from_be_bytes ( * bytes) , 
104+         } ; 
105+         // u64::MAX as input is invalid because the maximum value of counter is u64::MAX - 1. 
106+         if  num == u64:: MAX  { 
107+             return  Ok ( Err ( Error :: from ( ErrorKind :: InvalidInput ) ) ) ; 
108+         } 
109+         // If the addition does not let the counter to exceed the maximum value, update the counter. 
110+         // Else, block. 
111+         match  self . counter . checked_add ( num)  { 
112+             Some ( new_count @ 0 ..=MAX_COUNTER )  => { 
113+                 // Prevent false alarm in data race detection when doing synchronisation via eventfd. 
114+                 self . clock . join ( & ecx. release_clock ( ) . unwrap ( ) ) ; 
115+                 self . counter  = new_count; 
116+             } 
117+             None  | Some ( u64:: MAX )  => { 
118+                 if  self . is_nonblock  { 
119+                     return  Ok ( Err ( Error :: from ( ErrorKind :: WouldBlock ) ) ) ; 
120+                 }  else  { 
121+                     //FIXME: blocking is not supported 
122+                     throw_unsup_format ! ( "eventfd: blocking is unsupported" ) ; 
123+                 } 
124+             } 
61125        } ; 
62-         // FIXME handle blocking when addition results in exceeding the max u64 value 
63-         // or fail with EAGAIN if the file descriptor is nonblocking. 
64-         self . val  = self . val . checked_add ( num) . unwrap ( ) ; 
65-         Ok ( Ok ( 8 ) ) 
126+         Ok ( Ok ( U64_MIN_ARRAY_SIZE ) ) 
66127    } 
67128} 
68129
@@ -87,27 +148,41 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
87148fn  eventfd ( & mut  self ,  val :  & OpTy < ' tcx > ,  flags :  & OpTy < ' tcx > )  -> InterpResult < ' tcx ,  Scalar >  { 
88149        let  this = self . eval_context_mut ( ) ; 
89150
151+         // eventfd is Linux specific. 
152+         this. assert_target_os ( "linux" ,  "eventfd" ) ; 
153+ 
90154        let  val = this. read_scalar ( val) ?. to_u32 ( ) ?; 
91-         let  flags = this. read_scalar ( flags) ?. to_i32 ( ) ?; 
155+         let  mut   flags = this. read_scalar ( flags) ?. to_i32 ( ) ?; 
92156
93157        let  efd_cloexec = this. eval_libc_i32 ( "EFD_CLOEXEC" ) ; 
94158        let  efd_nonblock = this. eval_libc_i32 ( "EFD_NONBLOCK" ) ; 
95159        let  efd_semaphore = this. eval_libc_i32 ( "EFD_SEMAPHORE" ) ; 
96160
97-         if  flags &  ( efd_cloexec | efd_nonblock |  efd_semaphore)  != flags  { 
98-             throw_unsup_format ! ( "eventfd: flag {flags:#x}  is unsupported" ) ; 
161+         if  flags &  efd_semaphore == efd_semaphore  { 
162+             throw_unsup_format ! ( "eventfd: EFD_SEMAPHORE  is unsupported" ) ; 
99163        } 
164+ 
165+         let  mut  is_nonblock = false ; 
166+         // Unload the flag that we support. 
167+         // After unloading, flags != 0 means other flags are used. 
100168        if  flags &  efd_cloexec == efd_cloexec { 
101-             // cloexec does nothing as we don't support `exec` 
169+             flags &= !efd_cloexec ; 
102170        } 
103171        if  flags &  efd_nonblock == efd_nonblock { 
104-             // FIXME remember the nonblock flag 
172+             flags &= !efd_nonblock; 
173+             is_nonblock = true ; 
105174        } 
106-         if  flags &  efd_semaphore == efd_semaphore { 
107-             throw_unsup_format ! ( "eventfd: EFD_SEMAPHORE is unsupported" ) ; 
175+         if  flags != 0  { 
176+             let  einval = this. eval_libc ( "EINVAL" ) ; 
177+             this. set_last_error ( einval) ?; 
178+             return  Ok ( Scalar :: from_i32 ( -1 ) ) ; 
108179        } 
109180
110-         let  fd = this. machine . fds . insert_fd ( FileDescriptor :: new ( Event  {  val :  val. into ( )  } ) ) ; 
181+         let  fd = this. machine . fds . insert_fd ( FileDescriptor :: new ( Event  { 
182+             counter :  val. into ( ) , 
183+             is_nonblock, 
184+             clock :  VClock :: default ( ) , 
185+         } ) ) ; 
111186        Ok ( Scalar :: from_i32 ( fd) ) 
112187    } 
113188} 
0 commit comments