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)
}
// 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]
fn select_with_retry(handle int, test Select, timeout time.Duration) ?bool {
mut retries := 10
for retries > 0 {
fn select_deadline(handle int, test Select, deadline time.Time) ?bool {
// if we have a 0 deadline here then the timeout that was passed was infinite...
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 {
if err.code() == 4 {
// signal! lets retry max 10 times
// 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
// Spurious wakeup from signal, keep waiting
continue
}
// we got other error
// NOT a spurious wakeup
return err
}
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
fn wait_for_common(handle int, deadline time.Time, timeout time.Duration, test Select) ? {
if deadline.unix == 0 {
// do not accept negative timeout
if timeout < 0 {
return err_timed_out
}
ready := select_with_retry(handle, test, timeout) ?
if ready {
return
}
return err_timed_out
// Convert timeouts to deadlines
real_deadline := if timeout == net.infinite_timeout {
time.unix(0)
} 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)
}
// 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) ?
ready := select_deadline(handle, test, real_deadline) ?
if ready {
return
}
return err_timed_out
}