Skip to content

Commit a5e5c9e

Browse files
qianxichen233lind
andauthored
Added test and comments for poll_syscall (#296)
* added tests and comment for poll_syscall * minor adjustment * resolved comments * resolved some comments * resolved comments --------- Co-authored-by: lind <lind@nyu.edu>
1 parent 11bc43e commit a5e5c9e

File tree

2 files changed

+609
-134
lines changed

2 files changed

+609
-134
lines changed

src/safeposix/syscalls/net_calls.rs

Lines changed: 116 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3215,12 +3215,77 @@ impl Cage {
32153215
return 0;
32163216
}
32173217

3218+
/// ## ------------------POLL SYSCALL------------------
3219+
/// ### Description
3220+
/// poll_syscall performs a similar task to select_syscall: it waits for
3221+
/// one of a set of file descriptors to become ready to perform I/O.
3222+
3223+
/// ### Function Arguments
3224+
/// The `poll_syscall()` receives two arguments:
3225+
/// * `fds` - The set of file descriptors to be monitored is specified in
3226+
/// the fds argument, which is an array of PollStruct structures
3227+
/// containing three fields: fd, events and revents. events and revents
3228+
/// are requested events and returned events, respectively. The field fd
3229+
/// contains a file descriptor for an open file. If this field is
3230+
/// negative, then the corresponding events field is ignored and the
3231+
/// revents field returns zero. The field events is an input parameter, a
3232+
/// bit mask specifying the events the application is interested in for
3233+
/// the file descriptor fd. The bits returned in revents can include any
3234+
/// of those specified in events, or POLLNVAL. The bits that may be
3235+
/// set/returned in events and revents are: 1. POLLIN: There is data to
3236+
/// read. 2. POLLPRI: There is some exceptional condition on the file
3237+
/// descriptor, currently not supported 3. POLLOUT: Writing is now
3238+
/// possible, though a write larger than the available space in a socket
3239+
/// or pipe will still block
3240+
/// 4. POLLNVAL: Invalid request: fd not open (only returned in revents;
3241+
/// ignored in events).
3242+
/// * `timeout` - The timeout argument is a RustDuration structure that
3243+
/// specifies the interval that poll() should block waiting for a file
3244+
/// descriptor to become ready. The call will block until either: 1. a
3245+
/// file descriptor becomes ready; 2. the call is interrupted by a signal
3246+
/// handler; 3. the timeout expires.
3247+
3248+
/// ### Returns
3249+
/// On success, poll_syscall returns a nonnegative value which is the
3250+
/// number of elements in the pollfds whose revents fields have been
3251+
/// set to a nonzero value (indicating an event or an error). A
3252+
/// return value of zero indicates that the system call timed out
3253+
/// before any file descriptors became ready.
3254+
///
3255+
/// ### Errors
3256+
/// * EINTR - A signal was caught.
3257+
/// * EINVAL - fd exceeds the FD_SET_MAX_FD.
3258+
///
3259+
/// ### Panics
3260+
/// No panic is expected from this syscall
32183261
pub fn poll_syscall(
32193262
&self,
32203263
fds: &mut [PollStruct],
32213264
timeout: Option<interface::RustDuration>,
32223265
) -> i32 {
3223-
//timeout is supposed to be in milliseconds
3266+
// timeout is supposed to be in milliseconds
3267+
3268+
// current implementation of poll_syscall is based on select_syscall
3269+
// which gives several issues:
3270+
// 1. according to standards, select_syscall should only support file descriptor
3271+
// that is smaller than 1024, while poll_syscall should not have such
3272+
// limitation but our implementation of poll_syscall is actually calling
3273+
// select_syscall directly which would mean poll_syscall would also have the
3274+
// 1024 maximum size limitation However, rustposix itself only support file
3275+
// descriptor that is smaller than 1024 which solves this issue automatically
3276+
// in an interesting way
3277+
// 2. current implementation of poll_syscall is very inefficient, that it passes
3278+
// each of the file descriptor into select_syscall one by one. A better
3279+
// solution might be transforming pollstruct into fdsets and pass into
3280+
// select_syscall once (TODO). A even more efficienct way would be completely
3281+
// rewriting poll_syscall so it does not depend on select_syscall anymore.
3282+
// This is also how Linux does for poll_syscall since Linux claims that poll
3283+
// have a better performance than select.
3284+
// 3. several revent value such as POLLERR (which should be set when pipe is
3285+
// broken), or POLLHUP (when peer closed its channel) are not possible to
3286+
// monitor. Since select_syscall does not have these features, so our
3287+
// poll_syscall, which derived from select_syscall, would subsequently not be
3288+
// able to support these features.
32243289

32253290
let mut return_code: i32 = 0;
32263291
let start_time = interface::starttimer();
@@ -3230,34 +3295,52 @@ impl Cage {
32303295
None => interface::RustDuration::MAX,
32313296
};
32323297

3298+
// according to standard, we should clear all revents
3299+
for structpoll in &mut *fds {
3300+
structpoll.revents = 0;
3301+
}
3302+
3303+
// we loop until either timeout
3304+
// or any of the file descriptor is ready
32333305
loop {
3306+
// iterate through each file descriptor
32343307
for structpoll in &mut *fds {
3308+
// get the file descriptor
32353309
let fd = structpoll.fd;
3310+
3311+
// according to standard, we should ignore all file descriptor
3312+
// that is smaller than 0
3313+
if fd < 0 {
3314+
continue;
3315+
}
3316+
3317+
// get the associated events to monitor
32363318
let events = structpoll.events;
32373319

32383320
// init FdSet structures
32393321
let reads = &mut interface::FdSet::new();
32403322
let writes = &mut interface::FdSet::new();
32413323
let errors = &mut interface::FdSet::new();
32423324

3243-
//read
3325+
// POLLIN for readable fd
32443326
if events & POLLIN > 0 {
32453327
reads.set(fd)
32463328
}
3247-
//write
3329+
// POLLOUT for writable fd
32483330
if events & POLLOUT > 0 {
32493331
writes.set(fd)
32503332
}
3251-
//err
3252-
if events & POLLERR > 0 {
3333+
// POLLPRI for except fd
3334+
if events & POLLPRI > 0 {
32533335
errors.set(fd)
32543336
}
32553337

3338+
// this mask is used for storing final revent result
32563339
let mut mask: i16 = 0;
32573340

3258-
//0 essentially sets the timeout to the max value allowed (which is almost
3259-
// always more than enough time) NOTE that the nfds argument is
3260-
// highest fd + 1
3341+
// here we just call select_syscall with timeout of zero,
3342+
// which essentially just check each fd set once then return
3343+
// NOTE that the nfds argument is highest fd + 1
32613344
let selectret = Self::select_syscall(
32623345
&self,
32633346
fd + 1,
@@ -3266,23 +3349,44 @@ impl Cage {
32663349
Some(errors),
32673350
Some(interface::RustDuration::ZERO),
32683351
);
3352+
// if there is any file descriptor ready
32693353
if selectret > 0 {
3270-
mask += if !reads.is_empty() { POLLIN } else { 0 };
3271-
mask += if !writes.is_empty() { POLLOUT } else { 0 };
3272-
mask += if !errors.is_empty() { POLLERR } else { 0 };
3354+
// is the file descriptor ready to read?
3355+
mask |= if !reads.is_empty() { POLLIN } else { 0 };
3356+
// is the file descriptor ready to write?
3357+
mask |= if !writes.is_empty() { POLLOUT } else { 0 };
3358+
// is there any exception conditions on the file descriptor?
3359+
mask |= if !errors.is_empty() { POLLPRI } else { 0 };
3360+
// this file descriptor is ready for something,
3361+
// increment the return value
32733362
return_code += 1;
32743363
} else if selectret < 0 {
3275-
return selectret;
3364+
// if there is any error, first check if the error
3365+
// is EBADF, which refers to invalid file descriptor error
3366+
// in this case, we should set POLLNVAL to revent
3367+
if selectret == -(Errno::EBADF as i32) {
3368+
mask |= POLLNVAL;
3369+
// according to standard, return value is the number of fds
3370+
// with non-zero revent, which may indicate an error as well
3371+
return_code += 1;
3372+
} else {
3373+
return selectret;
3374+
}
32763375
}
3376+
// set the revents
32773377
structpoll.revents = mask;
32783378
}
32793379

3380+
// we break if there is any file descriptor ready
3381+
// or timeout is reached
32803382
if return_code != 0 || interface::readtimer(start_time) > end_time {
32813383
break;
32823384
} else {
3385+
// otherwise, check for signal and loop again
32833386
if interface::sigcheck() {
32843387
return syscall_error(Errno::EINTR, "poll", "interrupted function call");
32853388
}
3389+
// We yield to let other threads continue if we've found no ready descriptors
32863390
interface::lind_yield();
32873391
}
32883392
}

0 commit comments

Comments
 (0)