From 50ab19d1365e8c67014eb21e895e668f51b04a93 Mon Sep 17 00:00:00 2001 From: Emily Hudson Date: Wed, 11 May 2022 16:03:48 -0500 Subject: [PATCH] net: select with deadlines (#14369) --- vlib/net/common.v | 61 ++++++++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 33 deletions(-) diff --git a/vlib/net/common.v b/vlib/net/common.v index 99dd1bcff9..1b571f8c59 100644 --- a/vlib/net/common.v +++ b/vlib/net/common.v @@ -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 }