net: select with deadlines (#14369)

Emily Hudson 2022-05-11 16:03:48 -05:00 committed by Jef Roosens
parent d2eef45612
commit 50ab19d136
Signed by: Jef Roosens
GPG Key ID: B75D4F293C7052DB
1 changed files with 28 additions and 33 deletions

View File

@ -65,56 +65,51 @@ fn @select(handle int, test Select, timeout time.Duration) ?bool {
return C.FD_ISSET(handle, &set) return C.FD_ISSET(handle, &set)
} }
// select_with_retry will retry the select if select is failing
// due to interrupted system call. This can happen on signals
// for example the GC Boehm uses signals internally on garbage
// collection
[inline] [inline]
fn select_with_retry(handle int, test Select, timeout time.Duration) ?bool { fn select_deadline(handle int, test Select, deadline time.Time) ?bool {
mut retries := 10 // if we have a 0 deadline here then the timeout that was passed was infinite...
for retries > 0 { infinite := deadline.unix_time() == 0
for infinite || time.now() <= deadline {
timeout := if infinite { net.infinite_timeout } else { deadline - time.now() }
ready := @select(handle, test, timeout) or { ready := @select(handle, test, timeout) or {
if err.code() == 4 { if err.code() == 4 {
// signal! lets retry max 10 times // Spurious wakeup from signal, keep waiting
// suspend thread with sleep to let the gc get
// cycles in the case the Bohem gc is interupting
time.sleep(1 * time.millisecond)
retries -= 1
continue continue
} }
// we got other error
// NOT a spurious wakeup
return err return err
} }
return ready return ready
} }
return error('failed to @select more that three times due to interrupted system call')
// Deadline elapsed
return false
} }
// wait_for_common wraps the common wait code // wait_for_common wraps the common wait code
fn wait_for_common(handle int, deadline time.Time, timeout time.Duration, test Select) ? { fn wait_for_common(handle int, deadline time.Time, timeout time.Duration, test Select) ? {
if deadline.unix == 0 { // Convert timeouts to deadlines
// do not accept negative timeout real_deadline := if timeout == net.infinite_timeout {
if timeout < 0 { time.unix(0)
return err_timed_out } else if timeout == 0 {
// No timeout set, so assume deadline
deadline
} else if timeout < 0 {
// TODO(emily): Do something nicer here :)
panic('invalid negative timeout')
} else {
// timeout
time.now().add(timeout)
} }
ready := select_with_retry(handle, test, timeout) ?
if ready { ready := select_deadline(handle, test, real_deadline) ?
return
}
return err_timed_out
}
// Convert the deadline into a timeout
// and use that
d_timeout := deadline.unix - time.now().unix
if d_timeout < 0 {
// deadline is in the past so this has already
// timed out
return err_timed_out
}
ready := select_with_retry(handle, test, timeout) ?
if ready { if ready {
return return
} }
return err_timed_out return err_timed_out
} }