Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Setting timeout with callback on start the asynchttpserver #3202

Open
tulayang opened this issue Aug 11, 2015 · 1 comment
Open

Setting timeout with callback on start the asynchttpserver #3202

tulayang opened this issue Aug 11, 2015 · 1 comment
Labels
Async Everything related to Nim's async Feature Standard Library

Comments

@tulayang
Copy link
Contributor

I have an Idea that could setting timeout like this:

import asyncdispatch, asynchttpserver

proc server() =
    var server = newAsyncHttpServer()

    proc timeoutCb(fd: AsyncFD) = echo "Timeout fd ", repr fd
    setTimeout(1000, timeoutCb) 

    proc cb(req: Request) {.async.} = echo "Request"   
    asyncCheck server.serve(Port(5555), cb)

    runForever()

Call the proc timeoutCb and close the socket when using asyncdispatch.recv or asyncdispatch.recvInto to read data for more than 1000 milliseconds.

An object FdTimerTable used to register a AsyncFD that may be timed out. Register AsyncFD and milliseconds timeout in the proc asyncdispatch.recv or asyncdispatch.recvInto.

When the proc poll executed, call proc processReadTimers(p: PDispatcher) to iterative the ordered FdTimerTable by timeout, close the socket and call timeoutCb if the setting timeout < current time.

FdTimer = ref object
    fd: AsyncFD   
    timeout: float  # milliseconds timeout

FdTimerTable = ref object
    fds: TableRef[AsyncFD, DoublyLinkedNode[FdTimer]]
    timers: DoublyLinkedList[FdTimer]                

proc newFdTimerTable(): FdTimerTable  = 
    new(result)
    result.fds = newTable[AsyncFD, DoublyLinkedNode[FdTimer]]()
    result.timers = initDoublyLinkedList[FdTimer]()

proc hasKey(tb: FdTimerTable, fd: AsyncFD): bool =
    result = tb.fds.hasKey(fd)

proc add(tb: FdTimerTable, fd: AsyncFD, timeout: float) =
    var timer = new(FdTimer)
    timer.fd = fd
    timer.timeout = timeout
    var node = newDoublyLinkedNode(timer) 
    tb.fds[fd] = node
    tb.timers.append(node)

proc del(tb: FdTimerTable, fd: AsyncFD) =
    var node = tb.fds[fd]
    tb.fds.del(fd)
    tb.timers.remove(node)

iterator items(tb: FdTimerTable) {.inline.} =
    for timer in tb.timers:
        yield timer 

# Modify PDispatcher
# `readTimers` and `readTimeout` to record `AsyncFD` may be timeout,  
PDispatcher* = ref object of PDispatcherBase
    selector: Selector
    readTimers: FdTimerTable
    readTimeout: int
    timeoutCb: proc(fd: AsyncFD) {.closure, gcsafe.}

proc newDispatcher*(): PDispatcher =
    new result
    result.selector = newSelector()
    result.timers = @[]
    result.readTimers = newFdTimerTable()


# Setting timeout before start server
proc setTimeout*(timeout: int, cb = proc(fd: AsyncFD) = discard) =
    let p = getGlobalDispatcher()
    p.readTimeout = timeout
    p.timeoutCb = cb 

# Record the `AsyncFD` that is expected to read
proc addTimeout(fd: AsyncFD) =
    let p = getGlobalDispatcher()
    if p.readTimeout > 0 and not p.readTimers.hasKey(fd):
        p.readTimers.add(fd, epochTime()   p.readTimeout / 1000)

# Delete the `AsyncFD` if successfully read in the expected time.
proc delTimeout(fd: AsyncFD) =
    let p = getGlobalDispatcher()
    if p.readTimers.hasKey(fd):
        p.readTimers.del(fd)

# Iterative the ordered table to check for the timeout `AsyncFD`. 
proc processReadTimers(p: PDispatcher) = 
    let now = epochTime()
    for timer in p.readTimers.items():
        if now < timer.timeout:
            break
        if now >= timer.timeout:
            echo "Timeout ", repr timer.fd
            timer.fd.closeSocket()
            p.readTimers.del(timer.fd)
            if not isNil(p.timeoutCb):
                p.timeoutCb(timer.fd)


proc poll*(timeout = 500) =
    ...
    processReadTimers(p)

proc recv*(socket: AsyncFD, size: int,
                 flags = {SocketFlag.SafeDisconn}): Future[string] =
    ...
    proc cb(sock: AsyncFD): bool =
        delTimeout(sock)
        ...
    addTimeout(socket)
    ...

proc recvInto*(socket: AsyncFD, buf: cstring, size: int,
                       flags = {SocketFlag.SafeDisconn}): Future[int] =
    ...
    proc cb(sock: AsyncFD): bool =
        delTimeout(sock)
        ...
    addTimeout(socket)
    ...

Example: close the client and call timeoutCb after 1s

import asyncdispatch, asyncnet, asynchttpserver, os, net, threadpool

proc server() =
    var server = newAsyncHttpServer()

    proc timeoutCb(fd: AsyncFD) = echo "Timeout fd ", repr fd
    setTimeout(1000, timeoutCb) 

    proc cb(req: Request) {.async.} = echo "Request"   
    asyncCheck server.serve(Port(5555), cb)

    runForever()

proc request() =
    var client = newSocket()
    client.connect("127.0.0.1", Port(5555))
    client.send("GET /path HTTP/1.1\r\Lname:value")
    #sleep(3 * 1000)
    client.send("GET /path HTTP/1.1\r\Lname:value")

proc main() =
    parallel:
        spawn server()
        for i in 0..1:
            spawn request()
main()
@tulayang tulayang changed the title Setting timeout on the start asynchttpserver with callback Setting timeout on start the asynchttpserver with callback Aug 11, 2015
@tulayang tulayang changed the title Setting timeout on start the asynchttpserver with callback Setting timeout with callback on start the asynchttpserver Aug 11, 2015
@dom96 dom96 added the Async Everything related to Nim's async label Apr 4, 2016
@stale
Copy link

stale bot commented Aug 4, 2020

This issue has been automatically marked as stale because it has not had recent activity. If you think it is still a valid issue, write a comment below; otherwise it will be closed. Thank you for your contributions.

@stale stale bot added the stale Staled PR/issues; remove the label after fixing them label Aug 4, 2020
@ringabout ringabout added Feature and removed stale Staled PR/issues; remove the label after fixing them labels Mar 10, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Async Everything related to Nim's async Feature Standard Library
Projects
None yet
Development

No branches or pull requests

3 participants