aboutsummaryrefslogtreecommitdiff
path: root/core/sys/posix/sys_select.odin
blob: 117dee6250a367a75b80b6a87232474a7fe6acfb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix

import "base:intrinsics"

import "core:c"

when ODIN_OS == .Darwin {
	foreign import lib "system:System"
} else {
	foreign import lib "system:c"
}

// sys/select.h - select types

foreign lib {
	/*
	Examines the file descriptor sets to see whether some of their descriptors are ready for writing,
	or have an exceptional condition pending, respectively.

	Returns: -1 (setting errno) on failure, total amount of bits set in the bit masks otherwise

	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pselect.html ]]
	*/
	@(link_name=LPSELECT)
	pselect :: proc(
		nfds:     c.int,
		readfds:  ^fd_set,
		writefds: ^fd_set,
		errorfds: ^fd_set,
		timeout:  ^timespec,
		sigmask:  ^sigset_t,
	) -> c.int ---

	/*
	Equivalent to pselect() except a more specific timeout resolution (nanoseconds), 
	does not have a signal mask, and may modify the timeout.

	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pselect.html ]]
	*/
	@(link_name=LSELECT)
	select :: proc(
		nfds:     c.int,
		readfds:  ^fd_set,
		writefds: ^fd_set,
		errorfds: ^fd_set,
		timeout:  ^timeval,
	) -> c.int ---
}

when ODIN_OS == .NetBSD {
	LPSELECT :: "__pselect50"
	LSELECT  :: "__select50"
} else {
	LPSELECT :: "pselect"
	LSELECT  :: "select"
}

when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux || ODIN_OS == .Haiku {

	suseconds_t :: distinct (c.int32_t when ODIN_OS == .Darwin || ODIN_OS == .NetBSD || ODIN_OS == .Haiku else c.long)

	timeval :: struct {
		tv_sec:  time_t,      /* [PSX] seconds */
		tv_usec: suseconds_t, /* [PSX] microseconds */
	}

	// Maximum number of file descriptors in the fd_set structure.
	FD_SETSIZE :: #config(POSIX_FD_SETSIZE, 256 when ODIN_OS == .NetBSD else 1024)

	@(private)
	__NFDBITS :: size_of(c.int32_t) * 8

	// NOTE: this seems correct for FreeBSD but they do use a set backed by the long type themselves (thus the align change).
	@(private)
	ALIGN ::  align_of(c.long) when ODIN_OS == .FreeBSD || ODIN_OS == .Linux else align_of(c.int32_t)

	when ODIN_OS == .Haiku {
		fd_set :: struct #align(ALIGN) {
			fds_bits: [(FD_SETSIZE + (__NFDBITS - 1)) / __NFDBITS]c.int32_t,
		}
	} else {
		fd_set :: struct #align(ALIGN) {
			fds_bits: [(FD_SETSIZE / __NFDBITS) when (FD_SETSIZE % __NFDBITS) == 0 else (FD_SETSIZE / __NFDBITS) + 1]c.int32_t,
		}
	}

	@(private)
	__check_fd_set :: #force_inline proc "contextless" (_a: FD, _b: rawptr) -> bool {
		if _a < 0 {
			set_errno(.EINVAL)
		}

		if _a >= FD_SETSIZE {
			set_errno(.EINVAL)
		}

		return true
	}

	FD_CLR :: #force_inline proc "contextless" (_fd: FD, _p: ^fd_set) {
		if __check_fd_set(_fd, _p) {
			_p.fds_bits[cast(c.ulong)_fd / __NFDBITS] &= ~cast(c.int32_t)((cast(c.ulong)1) << (cast(c.ulong)_fd % __NFDBITS))
		}
	}

	FD_ISSET :: #force_inline proc "contextless" (_fd: FD, _p: ^fd_set) -> bool {
		if __check_fd_set(_fd, _p) {
			return bool(_p.fds_bits[cast(c.ulong)_fd / __NFDBITS] & cast(c.int32_t)((cast(c.ulong)1) << (cast(c.ulong)_fd % __NFDBITS)))
		}

		return false
	}

	FD_SET :: #force_inline proc "contextless" (_fd: FD, _p: ^fd_set) {
		if __check_fd_set(_fd, _p) {
			_p.fds_bits[cast(c.ulong)_fd / __NFDBITS] |= cast(c.int32_t)((cast(c.ulong)1) << (cast(c.ulong)_fd % __NFDBITS))
		}
	}

	FD_ZERO :: #force_inline proc "contextless" (_p: ^fd_set) {
		intrinsics.mem_zero(_p, size_of(fd_set))
	}

}