2020-08-21 00:01:37 +03:00
|
|
|
module net
|
|
|
|
|
|
|
|
import time
|
|
|
|
|
2021-06-13 23:53:38 +03:00
|
|
|
// 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)
|
2021-06-22 12:17:44 +03:00
|
|
|
pub const infinite_timeout = time.infinite
|
2021-06-13 23:53:38 +03:00
|
|
|
|
2023-01-25 13:32:05 +03:00
|
|
|
// ShutdownDirection is used by `net.shutdown`, for specifying the direction for which the
|
|
|
|
// communication will be cut.
|
|
|
|
pub enum ShutdownDirection {
|
|
|
|
read
|
|
|
|
write
|
|
|
|
read_and_write
|
|
|
|
}
|
|
|
|
|
|
|
|
[params]
|
|
|
|
pub struct ShutdownConfig {
|
|
|
|
how ShutdownDirection = .read_and_write
|
|
|
|
}
|
|
|
|
|
|
|
|
// shutdown shutsdown a socket, given its file descriptor `handle`.
|
|
|
|
// By default it shuts it down in both directions, both for reading
|
|
|
|
// and for writing. You can change that using `net.shutdown(handle, how: .read)`
|
|
|
|
// or `net.shutdown(handle, how: .write)`
|
|
|
|
pub fn shutdown(handle int, config ShutdownConfig) int {
|
|
|
|
$if windows {
|
|
|
|
return C.shutdown(handle, int(config.how))
|
|
|
|
} $else {
|
|
|
|
return C.shutdown(handle, int(config.how))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// close a socket, given its file descriptor `handle`.
|
|
|
|
pub fn close(handle int) ! {
|
2020-08-21 00:01:37 +03:00
|
|
|
$if windows {
|
2022-10-16 09:28:57 +03:00
|
|
|
socket_error(C.closesocket(handle))!
|
2020-08-21 00:01:37 +03:00
|
|
|
} $else {
|
2022-10-16 09:28:57 +03:00
|
|
|
socket_error(C.close(handle))!
|
2020-08-21 00:01:37 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Select waits for an io operation (specified by parameter `test`) to be available
|
2022-10-16 09:28:57 +03:00
|
|
|
fn @select(handle int, test Select, timeout time.Duration) !bool {
|
2020-08-21 00:01:37 +03:00
|
|
|
set := C.fd_set{}
|
|
|
|
|
|
|
|
C.FD_ZERO(&set)
|
|
|
|
C.FD_SET(handle, &set)
|
|
|
|
|
2021-06-22 12:17:44 +03:00
|
|
|
seconds := timeout / time.second
|
2021-06-13 23:53:38 +03:00
|
|
|
microseconds := time.Duration(timeout - (seconds * time.second)).microseconds()
|
2020-08-21 00:01:37 +03:00
|
|
|
|
|
|
|
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
|
2021-02-11 19:51:12 +03:00
|
|
|
if timeout == net.infinite_timeout {
|
2020-08-21 00:01:37 +03:00
|
|
|
timeval_timeout = &C.timeval(0)
|
|
|
|
}
|
|
|
|
|
|
|
|
match test {
|
|
|
|
.read {
|
2022-10-16 09:28:57 +03:00
|
|
|
socket_error(C.@select(handle + 1, &set, C.NULL, C.NULL, timeval_timeout))!
|
2020-08-21 00:01:37 +03:00
|
|
|
}
|
|
|
|
.write {
|
2022-10-16 09:28:57 +03:00
|
|
|
socket_error(C.@select(handle + 1, C.NULL, &set, C.NULL, timeval_timeout))!
|
2020-08-21 00:01:37 +03:00
|
|
|
}
|
|
|
|
.except {
|
2022-10-16 09:28:57 +03:00
|
|
|
socket_error(C.@select(handle + 1, C.NULL, C.NULL, &set, timeval_timeout))!
|
2020-08-21 00:01:37 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return C.FD_ISSET(handle, &set)
|
|
|
|
}
|
|
|
|
|
2021-06-16 04:07:24 +03:00
|
|
|
[inline]
|
2022-10-16 09:28:57 +03:00
|
|
|
fn select_deadline(handle int, test Select, deadline time.Time) !bool {
|
2022-05-12 00:03:48 +03:00
|
|
|
// 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() }
|
2021-06-16 04:07:24 +03:00
|
|
|
ready := @select(handle, test, timeout) or {
|
2022-04-12 13:38:40 +03:00
|
|
|
if err.code() == 4 {
|
2022-05-12 00:03:48 +03:00
|
|
|
// Spurious wakeup from signal, keep waiting
|
2021-06-16 04:07:24 +03:00
|
|
|
continue
|
|
|
|
}
|
2022-05-12 00:03:48 +03:00
|
|
|
|
|
|
|
// NOT a spurious wakeup
|
2021-06-16 04:07:24 +03:00
|
|
|
return err
|
|
|
|
}
|
2022-05-12 00:03:48 +03:00
|
|
|
|
2021-06-16 04:07:24 +03:00
|
|
|
return ready
|
|
|
|
}
|
2022-05-12 00:03:48 +03:00
|
|
|
|
|
|
|
// Deadline elapsed
|
|
|
|
return false
|
2021-06-16 04:07:24 +03:00
|
|
|
}
|
|
|
|
|
2020-08-21 00:01:37 +03:00
|
|
|
// wait_for_common wraps the common wait code
|
2022-10-16 09:28:57 +03:00
|
|
|
fn wait_for_common(handle int, deadline time.Time, timeout time.Duration, test Select) ! {
|
2022-05-12 00:03:48 +03:00
|
|
|
// 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)
|
2020-08-21 00:01:37 +03:00
|
|
|
}
|
2022-05-12 00:03:48 +03:00
|
|
|
|
2022-10-16 09:28:57 +03:00
|
|
|
ready := select_deadline(handle, test, real_deadline)!
|
2022-05-12 00:03:48 +03:00
|
|
|
|
2020-08-21 00:01:37 +03:00
|
|
|
if ready {
|
2021-06-13 23:53:38 +03:00
|
|
|
return
|
2020-08-21 00:01:37 +03:00
|
|
|
}
|
2022-05-12 00:03:48 +03:00
|
|
|
|
2020-08-21 00:01:37 +03:00
|
|
|
return err_timed_out
|
|
|
|
}
|
|
|
|
|
|
|
|
// wait_for_write waits for a write io operation to be available
|
2022-10-16 09:28:57 +03:00
|
|
|
fn wait_for_write(handle int, deadline time.Time, timeout time.Duration) ! {
|
2020-08-21 00:01:37 +03:00
|
|
|
return wait_for_common(handle, deadline, timeout, .write)
|
|
|
|
}
|
|
|
|
|
|
|
|
// wait_for_read waits for a read io operation to be available
|
2022-10-16 09:28:57 +03:00
|
|
|
fn wait_for_read(handle int, deadline time.Time, timeout time.Duration) ! {
|
2020-08-21 00:01:37 +03:00
|
|
|
return wait_for_common(handle, deadline, timeout, .read)
|
|
|
|
}
|