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(this.machine.readiness_interests.new_watcher());
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.clone(), interests)?;
106 let total = count.strict_add(invalid_interests);
108
109 let inner = Rc::into_inner(watcher).unwrap();
113 inner.destroy(this);
114
115 return this.write_scalar(Scalar::from_u32(total), dest);
116 }
117
118 watcher.add_blocked_thread(this.machine.threads.active_thread());
121
122 let deadline = if timeout.is_positive() {
123 let timeout_duration = Duration::from_millis(u64::try_from(timeout).unwrap());
124 Some(this.machine.monotonic_clock.now().add_lossy(timeout_duration).into())
125 } else {
126 None
128 };
129
130 let dest = dest.clone();
131 this.block_thread(
132 BlockReason::Readiness,
133 deadline,
134 callback!(
135 @capture<'tcx> {
136 watcher: Rc<ReadinessWatcher>,
137 interests: BTreeMap<FdNum, PollInterest<'tcx>>,
138 dest: MPlaceTy<'tcx>,
139 } |this, reason: UnblockKind| {
140 if let UnblockKind::TimedOut = reason {
141 return this.write_null(&dest);
143 }
144
145 let count = this.write_ready_events(watcher, interests)?;
146 this.write_scalar(Scalar::from_u32(count), &dest)
147 }
148 ),
149 );
150
151 interp_ok(())
152 }
153}
154
155impl<'tcx> EvalContextPrivExt<'tcx> for crate::MiriInterpCx<'tcx> {}
156trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
157 fn write_ready_events(
160 &mut self,
161 watcher: Rc<ReadinessWatcher>,
162 interests: BTreeMap<FdNum, PollInterest<'tcx>>,
163 ) -> InterpResult<'tcx, u32> {
164 let this = self.eval_context_mut();
165
166 let mut fulfilled_interests = 0u32;
168
169 for ready in watcher.get_ready_interests(watcher.ready_count(), this)? {
172 let fd_num = FdNum::try_from(ready.data).expect("Data is always a file descriptor");
173 let interest = interests.get(&fd_num).expect("Interest should exist");
174
175 fulfilled_interests = fulfilled_interests.strict_add(1);
176
177 let poll_events = this.readiness_to_poll_bitflag(ready.active());
178 this.write_scalar(Scalar::from_u16(poll_events), &interest.revents_place)?;
179 }
180
181 interp_ok(fulfilled_interests)
186 }
187
188 fn readiness_to_poll_bitflag(&self, readiness: &Readiness) -> u16 {
191 let this = self.eval_context_ref();
192
193 let pollin = this.eval_libc_u16("POLLIN");
194 let pollout = this.eval_libc_u16("POLLOUT");
195 let pollhup = this.eval_libc_u16("POLLHUP");
196 let pollerr = this.eval_libc_u16("POLLERR");
197
198 let mut bitflag = 0;
199 if readiness.readable {
200 bitflag |= pollin;
201 }
202 if readiness.writable {
203 bitflag |= pollout;
204 }
205 if readiness.write_closed {
206 bitflag |= pollhup;
207 }
208 if readiness.error {
209 bitflag |= pollerr;
210 }
211
212 if matches!(this.tcx.sess.target.os, Os::Linux | Os::Android | Os::FreeBsd | Os::Illumos) {
213 let pollrdhup = this.eval_libc_u16("POLLRDHUP");
215 if readiness.read_closed {
216 bitflag |= pollrdhup;
217 }
218 }
219
220 bitflag
221 }
222
223 fn poll_bitflag_to_readiness(&self, mut bitflag: u16) -> InterpResult<'tcx, Readiness> {
228 let this = self.eval_context_ref();
229
230 let pollin = this.eval_libc_u16("POLLIN");
231 let pollout = this.eval_libc_u16("POLLOUT");
232 let pollhup = this.eval_libc_u16("POLLHUP");
233 let pollerr = this.eval_libc_u16("POLLERR");
234 let pollnval = this.eval_libc_u16("POLLNVAL");
235
236 let mut readiness = Readiness { write_closed: true, error: true, ..Readiness::EMPTY };
238
239 if bitflag & pollin == pollin {
240 readiness.readable = true;
241 bitflag &= !pollin;
242 }
243 if bitflag & pollout == pollout {
244 readiness.writable = true;
245 bitflag &= !pollout;
246 }
247 if bitflag & pollhup == pollhup {
248 bitflag &= !pollhup;
249 }
250 if bitflag & pollerr == pollerr {
251 bitflag &= !pollerr;
252 }
253 if bitflag & pollnval == pollnval {
254 bitflag &= !pollnval;
256 }
257
258 if matches!(this.tcx.sess.target.os, Os::Linux | Os::Android | Os::FreeBsd | Os::Illumos) {
259 let pollrdhup = this.eval_libc_u16("POLLRDHUP");
261 if bitflag & pollrdhup == pollrdhup {
262 readiness.read_closed = true;
263 bitflag &= !pollrdhup;
264 }
265 }
266
267 if bitflag != 0 {
268 throw_unsup_format!(
269 "poll: poll event {bitflag:#x} is unsupported. Only POLLIN, \
270 POLLOUT, POLLERR, POLLHUP, POLLNVAL and POLLRDHUP are supported."
271 );
272 }
273
274 interp_ok(readiness)
275 }
276}