about summary refs log tree commit diff stats
path: root/src/js
diff options
context:
space:
mode:
Diffstat (limited to 'src/js')
-rw-r--r--src/js/timeout.nim113
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)