@@ -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