I just messed up the example here. I tried a lot of permutations over the past 
few hours, including some where the `spawn` call was abstracted in a `runIn` 
proc, and more.

The vast majority of the time I was testing with `command` being `{.nimcall.}` 
before I experimented with "command" being a generic and forcing people to 
inherit from `Actor` (which it turns out was very meh for different reasons an 
why I didn't follow that approach further).

If you want a full blown view of what I'm working on (It's not like I've 
written that much code yet). The actual meat is at the bottom of the code, 
that's a functional example, 2 examples that don't work and one that works if I 
keep `Actor` as value type as in this slice of code.
    
    
    # typeTable.nim - Base-type for storing pointers of various different types 
in a table, some form of "controlled" type erasure in a sense
    import std/[tables, hashes]
    
    type TypeId = distinct pointer
    proc `==`*(x, y: TypeId): bool {.borrow.}
    proc hash*(x: TypeId): Hash {.borrow.}
    proc getTypeId[T](typ: typedesc[T]): TypeId =
      var info {.global.}: int
      return TypeId(info.addr)
    
    type TypeTable* = distinct Table[TypeId, pointer]
    proc hasKey*[T](table: TypeTable, key: typedesc[T]): bool =
      let innerKey = key.getTypeId()
      return Table[TypeId, pointer](table).hasKey(innerKey)
    
    proc `[]`*[T](table: TypeTable; typ: typedesc[T]): T =
      let key = typ.getTypeId()
      let valuePtr = Table[TypeId, pointer](table)[key]
      return cast[T](valuePtr)
    
    proc `[]=`*[T: ptr](table: var TypeTable, key: typedesc[T], value: T) =
      let key = T.getTypeId()
      let valuePtr: pointer = value
      Table[TypeId, pointer](table)[key] = valuePtr
    
    # mailbox.nim - abstracts over Channels to make them easily storable and 
retrievable from typeTable
    import threading/channels
    import std/[options, isolation]
    
    type Mailbox*[T] = ptr Chan[T]
    
    proc newMailbox*[T](capacity: int): Mailbox[T] =
      let mailbox = createShared(Chan[T])
      mailbox[] = newChan[T](capacity)
      return mailbox
    
    proc destroyMailbox*[T](mailbox: Mailbox[T]) =
      freeShared(mailbox)
    
    proc send*[T](mailbox: Mailbox[T], value: T) =
      mailbox[].send(
        unsafeIsolate(deepCopy(value))
      )
    
    proc trySend*[T](mailbox: Mailbox[T], value: T): bool =
      return mailbox[].trySend(
        unsafeIsolate(deepCopy(value))
      )
    
    proc recv*[T](mailbox: Mailbox[T]): T = mailbox[].recv()
    
    proc tryRecv*[T](mailbox: Mailbox[T]): Option[T] =
      var msg: T
      let hasMsg = mailbox[].tryRecv(msg)
      if hasMsg:
        return some(msg)
      else:
        return none(T)
    
    proc peek*[T](mailbox: Mailbox[T]): int = mailbox[].peek()
    
    # mailboxTable.nim - abstracts over typeTable yet again to be more 
specifically about Mailboxes.
    type MailboxTable* = distinct TypeTable
    
    proc hasMailbox*[T](table: MailboxTable, typ: typedesc[T]): bool =
      return TypeTable(table).hasKey(Mailbox[T])
    
    proc `[]`*[T](table: MailboxTable, typ: typedesc[T]): Mailbox[T] =
      return TypeTable(table)[Mailbox[T]]
    
    proc `[]=`*[T](table: var MailboxTable, typ: typedesc[T], mailbox: 
Mailbox[T]) =
      TypeTable(table)[Mailbox[T]] = mailbox
    
    # actor.nim
    import std/options
    
    type Actor* = object
      targetMailboxes*: MailboxTable
      sourceMailboxes*: MailboxTable
      command*: proc(sources, targets: MailboxTable) {.nimcall.}
    
    proc newActor*(command: proc(sources: MailboxTable, targets: MailboxTable) 
{.nimcall.}): Actor =
      return Actor(command: command)
    
    proc hasTarget*[T](actor: Actor, targetTyp: typedesc[T]): bool =
      return actor.targetMailboxes.hasMailbox(T)
    
    proc addTarget*[T](actor: var Actor, mailbox: Mailbox[T]) =
      actor.targetMailboxes[T] = mailbox
    
    proc getTarget*[T](actor: Actor, typ: typedesc[T]): Option[Mailbox[T]] =
      if actor.hasTarget(T):
        let value = actor.targetMailboxes[T]
        return some(value)
      else:
        return none(Mailbox[T])
    
    proc hasSource[T](actor: Actor, sourceTyp: typedesc[T]): bool =
      return actor.sourceMailboxes.hasMailbox(T)
    
    proc addSource*[T](actor: var Actor, mailbox: Mailbox[T]) =
      actor.sourceMailboxes[T] = mailbox
    
    proc getSource*[T](actor: Actor, typ: typedesc[T]): Option[Mailbox[T]] =
      if actor.hasSource(T):
        let value = actor.sourceMailboxes[T]
        return some(value)
      else:
        return none(Mailbox[T])
    
    
    
    
    
    ## EXAMPLE ##
    import std/os
    import taskpools
    
    let threads = 4
    var tp = Taskpool.new(num_threads = threads)
    
    type A = ref object
      name: string
    
    proc run(sources: MailboxTable, targets: MailboxTable) {.raises:[], 
nimcall, gcsafe.} =
        sleep(1000)
        try:
          echo sources[A].recv().repr
        except KeyError:
          echo "Busted Keyerror"
    
    var act = newActor(run)
    
    
    let source = newMailbox[A](1)
    act.addSource(source)
    let msg = A(name: "test")
    
    
    # Example 1 - Works, but not the syntax I want
    tp.spawn run(act.sourceMailboxes, act.targetMailboxes)
    source.send(msg)
    
    # Example 2 - Does not work because spawn macro ("Invalid node kind 
nnkDotExpr for macros.`$`")
    # 2.1
    # proc runActor(actor: Actor, pool: Taskpool) =
    #   pool.spawn act.command(act.sourceMailboxes, act.targetMailboxes)
    # runActor(act, tp)
    # source.send(msg)
    
    # 2.2
    # proc runActor(actor: Actor, pool: Taskpool) =
    #   let runner = actor.command
    #   pool.spawn runner(act.sourceMailboxes, act.targetMailboxes)
    # runActor(act, tp)
    # source.send(msg)
    
    # Example 3 - Works 50% - Does not work with Actor being ref type, does 
work with Actor being value type
    # proc run(actor: Actor) {.gcsafe, raises: [].} =
    #   try:
    #     echo "Running"
    #     {.gcsafe.}: actor.command(actor.sourceMailboxes, 
actor.targetMailboxes)
    #   except:
    #     echo "Error"
    # tp.spawn act.run()
    # source.send(msg)
    
    
    # Cleanup
    tp.syncAll()
    tp.shutDown()
    
    
    Run

Reply via email to