Skip to content

Instantly share code, notes, and snippets.

@dom96
Created August 10, 2013 11:02
Show Gist options
  • Save dom96/0c73a80b0dba2453956a to your computer and use it in GitHub Desktop.
Save dom96/0c73a80b0dba2453956a to your computer and use it in GitHub Desktop.
type
PacceptAsyncArgObject = ref object of TObject
dummy1: PAsyncSocket
type
PacceptAsyncRetObject* = ref object of TObject
resultVal*: PAsyncSocket
proc acceptAsync*(socket: PAsyncSocket): PPromise
iterator acceptAsync*(arg: PObject): PPromise {.closure.} =
let passedInParams = PacceptAsyncArgObject(arg)
let socket = passedInParams.dummy1
var readPromise = PPromise(special: true, socket: socket, kind: reqRead,
finished: false)
yield readPromise
var client: PAsyncSocket
new(client)
accept(socket, client)
var retObj = PacceptAsyncRetObject(resultVal: client)
yield PPromise(special: false, finished: true, value: retObj)
proc acceptAsync*(socket: PAsyncSocket): PPromise =
var params: PacceptAsyncArgObject
new(params)
params.dummy1 = socket
result = PPromise(special: false, iter: acceptAsync, arg: params,
finished: false)
proc acceptAsync*(socket: PAsyncSocket): PAsyncSocket {.async.} =
## Accepts a client connecting to a server socket asynchronously.
## Returns that client.
var readPromise = PPromise(special: true, socket: socket,
kind: reqRead, finished: false)
await readPromise
var client: PAsyncSocket; new(client)
accept(socket, client)
return client
@zah
Copy link

zah commented Aug 10, 2013

You may have the wrong idea that all of the "async" procs should be written as iterators. There is a basic API written as regular procs serving as a toolbox for writing .async. procs. Here is how async accept should look in pseudo-code:

proc acceptAsync(socket: PAsyncSocket): PPromise[TConnection] =
    result = newPromise
    socket.setAcceptHandler do (connection: TConnection):
       result.resolve(connection)
    socket.listen()

@zah
Copy link

zah commented Aug 10, 2013

Pseudo-code for type erasure for promises. I took some liberties with the type of "onCompleted":

type 
  PromiseBase = object
     onCompleted: seq[proc()] # should be a event/signal really

  Promise[T] = object of PromiseBase
     value: T

proc resolve[T](p: var Promise[T], value: T) =
   p.value = T
   p.onCompleted()

template wrapYield(iter, promise) =
   promise.onCompleted do:
     if not finished(iter): discard iter()
   yield promise

@zah
Copy link

zah commented Aug 10, 2013

Hmm, macro expansion should do something like this:

proc myRead(sock: PAsyncSocket): string {.async.} =
  var dataPromise = readAsync(sock)
  let data = await dataPromise
  return data
iterator myReadIter(s: Socket, caller_result: Promise[string]): PromiseBase =
  var promise: Promise[string] = s.asyncRead()
  wrapYield (this_iterator, promise)
  var data = promise.value
  caller_result.resolve(data)

proc myRead(s: Socket): Promise[string] =
  result = new(Promise[string])
  discard myReadIter(s, result)

@zah
Copy link

zah commented Feb 9, 2014

One little fix. The code above is assuming that closure iterators are defined in a different style.
With the current compiler, it should really be like this:

proc myRead(s: Socket): Promise[string] =
  result = new(Promise[string])
  var caller_result = result

  var myReadIter = iterator(): PRomiseBase =
    var promise: Promise[string] = s.asyncRead()
    wrapYield (myReadIter, promise)
    var data = promise.value
    caller_result.resolve(data) # pay attention here!
                                # any returns in the original proc are translated
                                # to caller_result.resolve

  discard myReadIter()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment