v/vlib/net/common.v

128 lines
3.4 KiB
V

module net
import time
// no_deadline should be given to functions when no deadline is wanted (i.e. all functions
// return instantly)
const no_deadline = time.Time{
unix: 0
}
// no_timeout should be given to functions when no timeout is wanted (i.e. all functions
// return instantly)
pub const no_timeout = time.Duration(0)
// infinite_timeout should be given to functions when an infinite_timeout is wanted (i.e. functions
// only ever return with data)
pub const infinite_timeout = time.Duration(-1)
// Shutdown shutsdown a socket and closes it
fn shutdown(handle int) ? {
$if windows {
C.shutdown(handle, C.SD_BOTH)
socket_error(C.closesocket(handle)) ?
} $else {
C.shutdown(handle, C.SHUT_RDWR)
socket_error(C.close(handle)) ?
}
}
// Select waits for an io operation (specified by parameter `test`) to be available
fn @select(handle int, test Select, timeout time.Duration) ?bool {
set := C.fd_set{}
C.FD_ZERO(&set)
C.FD_SET(handle, &set)
seconds := timeout.milliseconds() / 1000
microseconds := time.Duration(timeout - (seconds * time.second)).microseconds()
mut tt := C.timeval{
tv_sec: u64(seconds)
tv_usec: u64(microseconds)
}
mut timeval_timeout := &tt
// infinite timeout is signaled by passing null as the timeout to
// select
if timeout == net.infinite_timeout {
timeval_timeout = &C.timeval(0)
}
match test {
.read {
socket_error(C.@select(handle + 1, &set, C.NULL, C.NULL, timeval_timeout)) ?
}
.write {
socket_error(C.@select(handle + 1, C.NULL, &set, C.NULL, timeval_timeout)) ?
}
.except {
socket_error(C.@select(handle + 1, C.NULL, C.NULL, &set, timeval_timeout)) ?
}
}
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 := 3
for retries > 0 {
ready := @select(handle, test, timeout) or {
if err.code == 4 {
// signal! lets retry max 3 times
retries -= 1
continue
}
// we got other error
return err
}
return ready
}
return error('failed to @select more that three times due to interrupted system call')
}
// 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 {
// only accept infinite_timeout as a valid
// negative timeout - it is handled in @select however
if timeout < 0 && timeout != net.infinite_timeout {
return err_timed_out
}
ready := select_with_retry(handle, test, timeout) ?
if ready {
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 {
return
}
return err_timed_out
}
// wait_for_write waits for a write io operation to be available
fn wait_for_write(handle int, deadline time.Time, timeout time.Duration) ? {
return wait_for_common(handle, deadline, timeout, .write)
}
// wait_for_read waits for a read io operation to be available
fn wait_for_read(handle int, deadline time.Time, timeout time.Duration) ? {
return wait_for_common(handle, deadline, timeout, .read)
}