1use std::collections::BTreeMap;
2use std::rc::Rc;
3use std::time::Duration;
4
5use rustc_target::spec::Os;
6
7use crate::shims::files::FdNum;
8use crate::*;
9
10#[derive(Debug)]
13struct PollInterest<'tcx> {
14 revents_place: MPlaceTy<'tcx>,
16}
17
18impl VisitProvenance for PollInterest<'_> {
19 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
20 self.revents_place.visit_provenance(visit);
21 }
22}
23
24impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
25pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
26 fn poll(
27 &mut self,
28 fds: &OpTy<'tcx>,
29 nfds: &OpTy<'tcx>,
30 timeout: &OpTy<'tcx>,
31 dest: &MPlaceTy<'tcx>,
32 ) -> InterpResult<'tcx> {
33 let this = self.eval_context_mut();
34
35 let nfds_layout = this.libc_ty_layout("nfds_t");
36 let nfds: u64 = this.read_scalar(nfds)?.to_int(nfds_layout.size)?.try_into().unwrap();
37 let timeout = this.read_scalar(timeout)?.to_i32()?;
38 let fds_arr_layout = this.libc_array_ty_layout("pollfd", nfds);
39 let fds_arr_mplace = this.deref_pointer_as(fds, fds_arr_layout)?;
40 let mut fds_arr_iter = this.project_array_fields(&fds_arr_mplace)?;
41
42 let mut interests = BTreeMap::<FdNum, PollInterest<'tcx>>::new();
44 let mut invalid_interests = 0u32;
47
48 let watcher = Rc::new(ReadinessWatcher::default());
49
50 while let Some((_idx, pollfd)) = fds_arr_iter.next(this)? {
53 let fd_field = this.project_field_named(&pollfd, "fd")?;
54 let fd_num = this.read_scalar(&fd_field)?.to_i32()?;
55 let events_field = this.project_field_named(&pollfd, "events")?;
56 let events = this.read_scalar(&events_field)?.to_u16()?;
57 let revents_field = this.project_field_named(&pollfd, "revents")?;
58
59 let relevant_events = this.poll_bitflag_to_readiness(events)?;
60
61 let revents = if this.machine.fds.get(fd_num).is_some() {
62 let interest = PollInterest { revents_place: revents_field.clone() };
64 if interests.try_insert(fd_num, interest).is_err() {
65 throw_unsup_format!(
66 "poll: providing multiple interests for the same file descriptor is unsupported"
67 )
68 }
69 watcher
70 .add_interest(
71 fd_num,
72 relevant_events,
73 false,
74 u64::try_from(fd_num).unwrap(),
75 this,
76 )?
77 .unwrap();
79
80 0
83 } else if fd_num.is_negative() {
84 0
87 } else {
88 invalid_interests = invalid_interests.strict_add(1);
94
95 this.eval_libc_u16("POLLNVAL")
96 };
97
98 this.write_scalar(Scalar::from_u16(revents), &revents_field)?;
99 }
100
101 if timeout == 0 || invalid_interests > 0 || watcher.ready_count() > 0 {
102 let count = this.write_ready_events(watcher, interests)?;
106 let total = count.strict_add(invalid_interests);
108
109 return this.write_scalar(Scalar::from_u32(total), dest);
110 }
111
112 watcher.add_blocked_thread(this.machine.threads.active_thread());
115
116 let deadline = if timeout.is_positive() {
117 let timeout_duration = Duration::from_millis(u64::try_from(timeout).unwrap());
118 Some(this.machine.monotonic_clock.now().add_lossy(timeout_duration).into())
119 } else {
120 None
122 };
123
124 let dest = dest.clone();
125 this.block_thread(
126 BlockReason::Readiness,
127 deadline,
128 callback!(
129 @capture<'tcx> {
130 watcher: Rc<ReadinessWatcher>,
131 interests: BTreeMap<FdNum, PollInterest<'tcx>>,
132 dest: MPlaceTy<'tcx>,
133 } |this, reason: UnblockKind| {
134 if let UnblockKind::TimedOut = reason {
135 return this.write_scalar(Scalar::from_u32(0), &dest);
136 }
137
138 let count = this.write_ready_events(watcher, interests)?;
139 this.write_scalar(Scalar::from_u32(count), &dest)
140 }
141 ),
142 );
143
144 interp_ok(())
145 }
146}
147
148impl<'tcx> EvalContextPrivExt<'tcx> for crate::MiriInterpCx<'tcx> {}
149trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
150 fn write_ready_events(
153 &mut self,
154 watcher: Rc<ReadinessWatcher>,
155 interests: BTreeMap<FdNum, PollInterest<'tcx>>,
156 ) -> InterpResult<'tcx, u32> {
157 let this = self.eval_context_mut();
158
159 let mut fulfilled_interests = 0u32;
161
162 for ready in watcher.get_ready_interests(watcher.ready_count(), this)? {
165 let fd_num = FdNum::try_from(ready.data).expect("Data is always a file descriptor");
166 let interest = interests.get(&fd_num).expect("Interest should exist");
167
168 fulfilled_interests = fulfilled_interests.strict_add(1);
169
170 let poll_events = this.readiness_to_poll_bitflag(ready.active());
171 this.write_scalar(Scalar::from_u16(poll_events), &interest.revents_place)?;
172 }
173
174 interp_ok(fulfilled_interests)
175 }
176
177 fn readiness_to_poll_bitflag(&self, readiness: &Readiness) -> u16 {
180 let this = self.eval_context_ref();
181
182 let pollin = this.eval_libc_u16("POLLIN");
183 let pollout = this.eval_libc_u16("POLLOUT");
184 let pollhup = this.eval_libc_u16("POLLHUP");
185 let pollerr = this.eval_libc_u16("POLLERR");
186
187 let mut bitflag = 0;
188 if readiness.readable {
189 bitflag |= pollin;
190 }
191 if readiness.writable {
192 bitflag |= pollout;
193 }
194 if readiness.write_closed {
195 bitflag |= pollhup;
196 }
197 if readiness.error {
198 bitflag |= pollerr;
199 }
200
201 if matches!(this.tcx.sess.target.os, Os::Linux | Os::Android | Os::FreeBsd | Os::Illumos) {
202 let pollrdhup = this.eval_libc_u16("POLLRDHUP");
204 if readiness.read_closed {
205 bitflag |= pollrdhup;
206 }
207 }
208
209 bitflag
210 }
211
212 fn poll_bitflag_to_readiness(&self, mut bitflag: u16) -> InterpResult<'tcx, Readiness> {
217 let this = self.eval_context_ref();
218
219 let pollin = this.eval_libc_u16("POLLIN");
220 let pollout = this.eval_libc_u16("POLLOUT");
221 let pollhup = this.eval_libc_u16("POLLHUP");
222 let pollerr = this.eval_libc_u16("POLLERR");
223 let pollnval = this.eval_libc_u16("POLLNVAL");
224
225 let mut readiness = Readiness { write_closed: true, error: true, ..Readiness::EMPTY };
227
228 if bitflag & pollin == pollin {
229 readiness.readable = true;
230 bitflag &= !pollin;
231 }
232 if bitflag & pollout == pollout {
233 readiness.writable = true;
234 bitflag &= !pollout;
235 }
236 if bitflag & pollhup == pollhup {
237 bitflag &= !pollhup;
238 }
239 if bitflag & pollerr == pollerr {
240 bitflag &= !pollerr;
241 }
242 if bitflag & pollnval == pollnval {
243 bitflag &= !pollnval;
245 }
246
247 if matches!(this.tcx.sess.target.os, Os::Linux | Os::Android | Os::FreeBsd | Os::Illumos) {
248 let pollrdhup = this.eval_libc_u16("POLLRDHUP");
250 if bitflag & pollrdhup == pollrdhup {
251 readiness.read_closed = true;
252 bitflag &= !pollrdhup;
253 }
254 }
255
256 if bitflag != 0 {
257 throw_unsup_format!(
258 "poll: poll event {bitflag:#x} is unsupported. Only POLLIN, \
259 POLLOUT, POLLERR, POLLHUP, POLLNVAL and POLLRDHUP are supported."
260 );
261 }
262
263 interp_ok(readiness)
264 }
265}