aboutsummaryrefslogtreecommitdiff
path: root/v_windows/v/vlib/os/notify/backend_linux.c.v
blob: 1913913881a33a090ca7a9d4323a2b0763bf43ee (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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
module notify

import time
import os

#include <sys/epoll.h>

struct C.epoll_event {
	events u32
	data   C.epoll_data_t
}

[typedef]
union C.epoll_data_t {
	ptr voidptr
	fd  int
	u32 u32
	u64 u64
}

fn C.epoll_create1(int) int

fn C.epoll_ctl(int, int, int, &C.epoll_event) int

fn C.epoll_wait(int, &C.epoll_event, int, int) int

// EpollNotifier provides methods that implement FdNotifier using the
// epoll I/O event notification facility (linux only)
[noinit]
struct EpollNotifier {
	epoll_fd int
}

// EpollEvent describes an event that occurred for a file descriptor in
// the watch list
[noinit]
struct EpollEvent {
pub:
	fd   int
	kind FdEventType
}

// new creates a new EpollNotifier
// The FdNotifier interface is returned to allow OS specific
// implementations without exposing the concrete type
pub fn new() ?FdNotifier {
	fd := C.epoll_create1(0) // 0 indicates default behavior
	if fd == -1 {
		return error(os.posix_get_error_msg(C.errno))
	}
	// Needed to circumvent V limitations
	x := &EpollNotifier{
		epoll_fd: fd
	}
	return x
}

const (
	epoll_read         = u32(C.EPOLLIN)
	epoll_write        = u32(C.EPOLLOUT)
	epoll_peer_hangup  = u32(C.EPOLLRDHUP)
	epoll_exception    = u32(C.EPOLLPRI)
	epoll_error        = u32(C.EPOLLERR)
	epoll_hangup       = u32(C.EPOLLHUP)
	epoll_edge_trigger = u32(C.EPOLLET)
	epoll_one_shot     = u32(C.EPOLLONESHOT)
	epoll_wake_up      = u32(C.EPOLLWAKEUP)
	epoll_exclusive    = u32(C.EPOLLEXCLUSIVE)
)

// ctl is a helper method for add, modify, and remove
fn (mut en EpollNotifier) ctl(fd int, op int, mask u32) ? {
	event := C.epoll_event{
		events: mask
		data: C.epoll_data_t{
			fd: fd
		}
	}
	if C.epoll_ctl(en.epoll_fd, op, fd, &event) == -1 {
		return error(os.posix_get_error_msg(C.errno))
	}
}

// add adds a file descriptor to the watch list
fn (mut en EpollNotifier) add(fd int, events FdEventType, conf ...FdConfigFlags) ? {
	mask := flags_to_mask(events, ...conf)
	en.ctl(fd, C.EPOLL_CTL_ADD, mask) ?
}

// modify sets an existing entry in the watch list to the provided events and configuration
fn (mut en EpollNotifier) modify(fd int, events FdEventType, conf ...FdConfigFlags) ? {
	mask := flags_to_mask(events, ...conf)
	en.ctl(fd, C.EPOLL_CTL_MOD, mask) ?
}

// remove removes a file descriptor from the watch list
fn (mut en EpollNotifier) remove(fd int) ? {
	en.ctl(fd, C.EPOLL_CTL_DEL, 0) ?
}

// wait waits to be notified of events on the watch list,
// returns at most 512 events
fn (mut en EpollNotifier) wait(timeout time.Duration) []FdEvent {
	// arbitrary 512 limit; events will round robin on successive
	// waits if the number exceeds this
	// NOTE: we use a fixed size array here for stack allocation; this has
	//       the added bonus of making EpollNotifier thread safe
	events := [512]C.epoll_event{}
	// populate events with the new events
	to := timeout.sys_milliseconds()
	count := C.epoll_wait(en.epoll_fd, &events[0], events.len, to)

	if count > 0 {
		mut arr := []FdEvent{cap: count}
		for i := 0; i < count; i++ {
			fd := unsafe { events[i].data.fd }
			kind := event_mask_to_flag(events[i].events)
			if kind.is_empty() {
				// NOTE: tcc only reports the first event for some
				// reason, leaving subsequent structs in the array as 0
				// (or possibly garbage)
				panic('encountered an empty event kind; this is most likely due to using tcc')
			}
			arr << &EpollEvent{
				fd: fd
				kind: kind
			}
		}
		return arr
	}
	return []
}

// close closes the EpollNotifier,
// any successive calls to add, modify, remove, and wait should fail
fn (mut en EpollNotifier) close() ? {
	if C.close(en.epoll_fd) == -1 {
		return error(os.posix_get_error_msg(C.errno))
	}
}

// event_mask_to_flag is a helper function that converts a bitmask
// returned by epoll_wait to FdEventType
fn event_mask_to_flag(mask u32) FdEventType {
	mut flags := FdEventType{}

	if mask & notify.epoll_read != 0 {
		flags.set(.read)
	}
	if mask & notify.epoll_write != 0 {
		flags.set(.write)
	}
	if mask & notify.epoll_peer_hangup != 0 {
		flags.set(.peer_hangup)
	}
	if mask & notify.epoll_exception != 0 {
		flags.set(.exception)
	}
	if mask & notify.epoll_error != 0 {
		flags.set(.error)
	}
	if mask & notify.epoll_hangup != 0 {
		flags.set(.hangup)
	}

	return flags
}

// flags_to_mask is a helper function that converts FdEventType and
// FdConfigFlags to a bitmask used by the C functions
fn flags_to_mask(events FdEventType, confs ...FdConfigFlags) u32 {
	mut mask := u32(0)
	if events.has(.read) {
		mask |= notify.epoll_read
	}
	if events.has(.write) {
		mask |= notify.epoll_write
	}
	if events.has(.peer_hangup) {
		mask |= notify.epoll_peer_hangup
	}
	if events.has(.exception) {
		mask |= notify.epoll_exception
	}
	if events.has(.error) {
		mask |= notify.epoll_error
	}
	if events.has(.hangup) {
		mask |= notify.epoll_hangup
	}
	for conf in confs {
		if conf.has(.edge_trigger) {
			mask |= notify.epoll_edge_trigger
		}
		if conf.has(.one_shot) {
			mask |= notify.epoll_one_shot
		}
		if conf.has(.wake_up) {
			mask |= notify.epoll_wake_up
		}
		if conf.has(.exclusive) {
			mask |= notify.epoll_exclusive
		}
	}
	return mask
}