diff options
author | bptato <nincsnevem662@gmail.com> | 2024-09-23 19:51:20 +0200 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2024-09-23 19:58:54 +0200 |
commit | fcd9aa9f9c604ed5d104343542962a26b2acda62 (patch) | |
tree | 7b0eea9b63bacc27cdc6471e2b2409b7b5d15c9e /src/js | |
parent | 8e8c7f0911f4a20446a83090d722fecaf203f6f3 (diff) | |
download | chawan-fcd9aa9f9c604ed5d104343542962a26b2acda62.tar.gz |
Replace std/selectors with poll
std/selectors uses OS-specific selector APIs, which sounds good in theory (faster than poll!), but sucks for portability in practice. Sure, you can fix portability bugs, but who knows how many there are on untested platforms... poll is standard, so if it works on one computer it should work on all other ones. (I hope.) As a bonus, I rewrote the timeout API for poll, which incidentally fixes setTimeout across forks. Also, SIGWINCH should now work on all platforms (as we self-pipe instead of signalfd/kqueue magic).
Diffstat (limited to 'src/js')
-rw-r--r-- | src/js/timeout.nim | 113 |
1 files changed, 73 insertions, 40 deletions
diff --git a/src/js/timeout.nim b/src/js/timeout.nim index 0213156a..72a68dbc 100644 --- a/src/js/timeout.nim +++ b/src/js/timeout.nim @@ -1,5 +1,5 @@ -import std/selectors -import std/tables +import std/algorithm +import std/times import io/dynstream import js/console @@ -15,59 +15,76 @@ type TimeoutEntry = ref object t: TimeoutType - fd: int + id: int32 val: JSValue args: seq[JSValue] + expires: int64 + timeout: int32 + + EvalJSFree* = proc(opaque: RootRef; src, file: string) {.nimcall.} TimeoutState* = ref object timeoutid: int32 - timeouts: Table[int32, TimeoutEntry] - timeoutFds: Table[int, int32] - selector: Selector[int] #TODO would be better with void... + timeouts: seq[TimeoutEntry] jsctx: JSContext err: DynStream #TODO shouldn't be needed - evalJSFree: proc(src, file: string) #TODO ew + evalJSFree: EvalJSFree + opaque: RootRef + sorted: bool -func newTimeoutState*(selector: Selector[int]; jsctx: JSContext; err: DynStream; - evalJSFree: proc(src, file: string)): TimeoutState = +func newTimeoutState*(jsctx: JSContext; err: DynStream; + evalJSFree: EvalJSFree; opaque: RootRef): TimeoutState = return TimeoutState( - selector: selector, jsctx: jsctx, err: err, - evalJSFree: evalJSFree + evalJSFree: evalJSFree, + opaque: opaque, + sorted: true ) func empty*(state: TimeoutState): bool = return state.timeouts.len == 0 +proc clearTimeout0(state: var TimeoutState; i: int) = + let entry = state.timeouts[i] + JS_FreeValue(state.jsctx, entry.val) + for arg in entry.args: + JS_FreeValue(state.jsctx, arg) + state.timeouts.del(i) + if state.timeouts.len != i: # only set if we del'd in the middle + state.sorted = false + proc clearTimeout*(state: var TimeoutState; id: int32) = - if id in state.timeouts: - let entry = state.timeouts[id] - state.selector.unregister(entry.fd) - JS_FreeValue(state.jsctx, entry.val) - for arg in entry.args: - JS_FreeValue(state.jsctx, arg) - state.timeoutFds.del(entry.fd) - state.timeouts.del(id) + var j = -1 + for i in 0 ..< state.timeouts.len: + if state.timeouts[i].id == id: + j = i + break + if j != -1: + state.clearTimeout0(j) + +proc getUnixMillis(): int64 = + let now = getTime() + return now.toUnix() * 1000 + now.nanosecond div 1_000_000 -#TODO varargs proc setTimeout*(state: var TimeoutState; t: TimeoutType; handler: JSValue; timeout: int32; args: openArray[JSValue]): int32 = let id = state.timeoutid inc state.timeoutid - let fd = state.selector.registerTimer(max(timeout, 1), t == ttTimeout, 0) - state.timeoutFds[fd] = id let entry = TimeoutEntry( t: t, - fd: fd, - val: JS_DupValue(state.jsctx, handler) + id: id, + val: JS_DupValue(state.jsctx, handler), + expires: getUnixMillis() + int64(timeout), + timeout: timeout ) for arg in args: entry.args.add(JS_DupValue(state.jsctx, arg)) - state.timeouts[id] = entry + state.timeouts.add(entry) + state.sorted = false return id -proc runEntry(state: var TimeoutState; entry: TimeoutEntry; name: string) = +proc runEntry(state: var TimeoutState; entry: TimeoutEntry) = if JS_IsFunction(state.jsctx, entry.val): let ret = JS_Call(state.jsctx, entry.val, JS_UNDEFINED, cint(entry.args.len), entry.args.toJSValueArray()) @@ -77,23 +94,39 @@ proc runEntry(state: var TimeoutState; entry: TimeoutEntry; name: string) = else: var s: string if state.jsctx.fromJS(entry.val, s).isSome: - state.evalJSFree(s, name) + state.evalJSFree(state.opaque, s, $entry.t) + +# for poll +proc sortAndGetTimeout*(state: var TimeoutState): cint = + if state.timeouts.len == 0: + return -1 + if not state.sorted: + state.timeouts.sort(proc(a, b: TimeoutEntry): int = + cmp(a.expires, b.expires), order = Descending) + state.sorted = true + let now = getUnixMillis() + return cint(max(state.timeouts[^1].expires - now, -1)) -proc runTimeoutFd*(state: var TimeoutState; fd: int): bool = - if fd notin state.timeoutFds: - return false - let id = state.timeoutFds[fd] - let entry = state.timeouts[id] - state.runEntry(entry, $entry.t) - if entry.t == ttTimeout: - state.clearTimeout(id) - return true +proc run*(state: var TimeoutState): bool = + let H = state.timeouts.high + let now = getUnixMillis() + var found = false + for i in countdown(H, 0): + if state.timeouts[i].expires > now: + break + let entry = state.timeouts[i] + state.runEntry(entry) + found = true + case entry.t + of ttTimeout: state.clearTimeout0(i) + of ttInterval: + entry.expires = now + entry.timeout + state.sorted = false + return found proc clearAll*(state: var TimeoutState) = - for entry in state.timeouts.values: - state.selector.unregister(entry.fd) + for entry in state.timeouts: JS_FreeValue(state.jsctx, entry.val) for arg in entry.args: JS_FreeValue(state.jsctx, arg) - state.timeouts.clear() - state.timeoutFds.clear() + state.timeouts.setLen(0) |