Skip to content

Commit

Permalink
picoev: enable running veb services on Termux
Browse files Browse the repository at this point in the history
  • Loading branch information
spytheman committed Jan 24, 2025
1 parent 772d210 commit a44ff78
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 2 deletions.
143 changes: 143 additions & 0 deletions vlib/picoev/loop_termux.c.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
module picoev

#include <sys/epoll.h>

$if !musl ? {
#include <sys/cdefs.h> // needed for cross compiling to linux
}

fn C.epoll_create(__flags int) int
fn C.epoll_wait(__epfd int, __events &C.epoll_event, __maxevents int, __timeout int) int
fn C.epoll_ctl(__epfd int, __op int, __fd int, __event &C.epoll_event) int

@[typedef]
union C.epoll_data {
mut:
ptr voidptr
fd int
u32 u32
u64 u64
}

@[packed]
pub struct C.epoll_event {
events u32
data C.epoll_data
}

@[heap]
pub struct EpollLoop {
mut:
id int
epoll_fd int
events [1024]C.epoll_event
now i64
}

type LoopType = EpollLoop

// create_epoll_loop creates a new epoll instance for and returns an
// `EpollLoop` struct with `id`
pub fn create_epoll_loop(id int) !&EpollLoop {
mut loop := &EpollLoop{
id: id
}

loop.epoll_fd = C.epoll_create(max_fds)
if loop.epoll_fd == -1 {
return error('could not create epoll loop!')
}

return loop
}

@[direct_array_access]
fn (mut pv Picoev) update_events(fd int, events int) int {
// check if fd is in range
assert fd < max_fds

mut target := pv.file_descriptors[fd]
mut ev := C.epoll_event{}

// fd belongs to loop
if events & picoev_del != target.events && target.loop_id != pv.loop.id {
return -1
}

if events & picoev_readwrite == target.events {
return 0
}

// vfmt off
ev.events = u32(
(if events & picoev_read != 0 { C.EPOLLIN } else { 0 })
|
(if events & picoev_write != 0 { C.EPOLLOUT } else { 0 })
)
// vfmt on
ev.data.fd = fd

if events & picoev_del != 0 {
// nothing to do
} else if events & picoev_readwrite == 0 {
// delete the file if it exists
epoll_ret := C.epoll_ctl(pv.loop.epoll_fd, C.EPOLL_CTL_DEL, fd, &ev)

// check error
assert epoll_ret == 0
} else {
// change settings to 0
mut epoll_ret := C.epoll_ctl(pv.loop.epoll_fd, C.EPOLL_CTL_MOD, fd, &ev)
if epoll_ret != 0 {
// if the file is not present we want to add it
assert C.errno == C.ENOENT
epoll_ret = C.epoll_ctl(pv.loop.epoll_fd, C.EPOLL_CTL_ADD, fd, &ev)

// check error
assert epoll_ret == 0
}
}

// convert to u32?
target.events = u32(events)
return 0
}

@[direct_array_access]
fn (mut pv Picoev) poll_once(max_wait_in_sec int) int {
nevents := C.epoll_wait(pv.loop.epoll_fd, &pv.loop.events[0], max_fds, max_wait_in_sec * 1000)

if nevents == -1 {
// timeout has occurred
return -1
}

for i := 0; i < nevents; i++ {
mut event := pv.loop.events[i]
target := unsafe { pv.file_descriptors[event.data.fd] }
unsafe {
assert event.data.fd < max_fds
}
if pv.loop.id == target.loop_id && target.events & picoev_readwrite != 0 {
mut read_events := 0
if event.events & u32(C.EPOLLIN) != 0 {
read_events |= picoev_read
}
if event.events & u32(C.EPOLLOUT) != 0 {
read_events |= picoev_write
}

if read_events != 0 {
// do callback!
unsafe { target.cb(event.data.fd, read_events, &pv) }
}
} else {
// defer epoll delete
event.events = 0
unsafe {
C.epoll_ctl(pv.loop.epoll_fd, C.EPOLL_CTL_DEL, event.data.fd, &event)
}
}
}
return 0
}
2 changes: 1 addition & 1 deletion vlib/picoev/picoev.v
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ pub fn new(config Config) !&Picoev {
// epoll on linux
// kqueue on macos and bsd
// select on windows and others
$if linux {
$if linux || termux {
pv.loop = create_epoll_loop(0) or { panic(err) }
} $else $if freebsd || macos {
pv.loop = create_kqueue_loop(0) or { panic(err) }
Expand Down
2 changes: 1 addition & 1 deletion vlib/picoev/socket_util.c.v
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ fn listen(config Config) !int {
// can be accepted
net.socket_error(C.setsockopt(fd, C.IPPROTO_IPV6, C.IPV6_V6ONLY, &flag_zero, sizeof(int)))!
}
$if linux {
$if linux || termux {
// epoll socket options
net.socket_error(C.setsockopt(fd, C.SOL_SOCKET, C.SO_REUSEPORT, &flag, sizeof(int)))!
net.socket_error(C.setsockopt(fd, C.IPPROTO_TCP, C.TCP_QUICKACK, &flag, sizeof(int)))!
Expand Down

0 comments on commit a44ff78

Please sign in to comment.