diff options
Diffstat (limited to 'src/js/timeout.nim')
-rw-r--r-- | src/js/timeout.nim | 104 |
1 files changed, 104 insertions, 0 deletions
diff --git a/src/js/timeout.nim b/src/js/timeout.nim new file mode 100644 index 00000000..0e31d8a3 --- /dev/null +++ b/src/js/timeout.nim @@ -0,0 +1,104 @@ +import selectors +import streams +import tables + +import js/javascript + +type TimeoutState*[T] = object + timeoutid: int32 + timeouts: Table[int32, tuple[handler: (proc()), fdi: int]] + intervals: Table[int32, tuple[handler: (proc()), fdi: int, tofree: JSValue]] + timeout_fdis: Table[int, int32] + interval_fdis: Table[int, int32] + selector: Selector[T] #TODO would be better with void... + jsctx: JSContext + err: Stream #TODO shouldn't be needed + evalJSFree: proc(src, file: string) #TODO ew + +func newTimeoutState*[T](selector: Selector[T], jsctx: JSContext, + err: Stream, evalJSFree: proc(src, file: string)): TimeoutState[T] = + return TimeoutState[T]( + selector: selector, + jsctx: jsctx, + err: err, + evalJSFree: evalJSFree + ) + +func empty*(state: TimeoutState): bool = + return state.timeouts.len == 0 and state.intervals.len == 0 + +#TODO varargs +proc setTimeout*[T: JSValue|string, S](state: var TimeoutState[S], handler: T, + timeout = 0i32): int32 = + let id = state.timeoutid + inc state.timeoutid + let fdi = state.selector.registerTimer(max(timeout, 1), true, default(S)) + state.timeout_fdis[fdi] = id + when T is string: + let evalJSFree = state.evalJSFree + state.timeouts[id] = ((proc() = + evalJSFree(handler, "setTimeout handler") + ), fdi) + else: + let fun = JS_DupValue(state.jsctx, handler) + let jsctx = state.jsctx + let err = state.err + state.timeouts[id] = ((proc() = + let ret = JS_Call(jsctx, fun, JS_UNDEFINED, 0, nil) + if JS_IsException(ret): + jsctx.writeException(err) + JS_FreeValue(jsctx, ret) + JS_FreeValue(jsctx, fun) + ), fdi) + return id + +proc clearTimeout*(state: var TimeoutState, id: int32) = + if id in state.timeouts: + let timeout = state.timeouts[id] + state.selector.unregister(timeout.fdi) + state.timeout_fdis.del(timeout.fdi) + state.timeouts.del(id) + +proc clearInterval*(state: var TimeoutState, id: int32) = + if id in state.intervals: + let interval = state.intervals[id] + state.selector.unregister(interval.fdi) + JS_FreeValue(state.jsctx, interval.tofree) + state.interval_fdis.del(interval.fdi) + state.intervals.del(id) + +#TODO varargs +proc setInterval*[T: JSValue|string, S](state: var TimeoutState[S], handler: T, + interval = 0i32): int32 = + let id = state.timeoutid + inc state.timeoutid + let fdi = state.selector.registerTimer(max(interval, 1), false, default(S)) + state.interval_fdis[fdi] = id + when T is string: + let evalJSFree = state.evalJSFree + state.intervals[id] = ((proc() = + evalJSFree(handler, "setInterval handler") + ), fdi, JS_NULL) + else: + let fun = JS_DupValue(state.jsctx, handler) + let jsctx = state.jsctx + let err = state.err + state.intervals[id] = ((proc() = + let ret = JS_Call(jsctx, handler, JS_UNDEFINED, 0, nil) + if JS_IsException(ret): + jsctx.writeException(err) + JS_FreeValue(jsctx, ret) + ), fdi, fun) + return id + +proc runTimeoutFd*(state: var TimeoutState, fd: int): bool = + if fd in state.interval_fdis: + state.intervals[state.interval_fdis[fd]].handler() + return true + elif fd in state.timeout_fdis: + let id = state.timeout_fdis[fd] + let timeout = state.timeouts[id] + timeout.handler() + state.clearTimeout(id) + return true + return false |