src/threadButler/codegen

Source   Edit  

Defines all code for code generation in threadbutler. All names of generated types are inferred from the name they are being registered with. All names of fields and enum kinds are inferred based on the data registered.

Note: Only the macros are for general users. The procs are only for writing new integrations.

Procs

proc asSignalVar(name: ThreadName): NimNode {....raises: [], tags: [], forbids: [].}

Generates a global variable containing the signal for name:

var <name>Signal*: ThreadSignalPtr

Source   Edit  
proc asThreadVar(name: ThreadName): NimNode {....raises: [], tags: [], forbids: [].}

Generates a global variable containing the thread for name:

var <name>ButlerThread*: Thread[Server[<name>Message]]

Source   Edit  
proc enumName(name: ThreadName): string {....raises: [], tags: [], forbids: [].}
Infers the name of the enum-type associated with name from name Source   Edit  
proc fieldName(node: NimNode): string {....raises: [ValueError], tags: [],
                                        forbids: [].}
Infers the name of a field on a Message-object-variant-type from a type Source   Edit  
proc firstParamName(node: NimNode): string {....raises: [ValueError], tags: [],
    forbids: [].}
Extracts the name of the first parameter of a proc Source   Edit  
proc genDestroyChannelHubProc(): NimNode {....raises: [], tags: [], forbids: [].}

Generates a proc destroy for destroying a ChannelHub.

Closes each channel stored in the hub as part of that. Uses variantName to infer the name Message-object-variant-type. The proc is generated based on the registered threadnames according to this pattern:

 proc destroy\*(hub: ChannelHub) =
   --- Repeat per threadname - start ---
   hub.getChannel(<variantName>).close()
   --- Repeat per threadname - end ---

Source   Edit  
proc generateCode(name: ThreadName): NimNode {....raises: [ValueError], tags: [],
    forbids: [].}

Generates all types and procs needed for message-passing for name.

This proc should only be used by macros in this and other integration modules.

Source   Edit  
proc genInitServerProc(name: ThreadName): NimNode {....raises: [ValueError],
    tags: [], forbids: [].}

Generates a proc initServer(hub: ChannelHub, typ: typedesc[<name>Message]): Server[<name>Message].

These procs instantiate a threadServer object which can then be run, which starts the server.

Source   Edit  
proc genMessageRouter(name: ThreadName; routes: seq[NimNode];
                      types: seq[NimNode]): NimNode {....raises: [ValueError],
    tags: [], forbids: [].}

Generates a proc routeMessage for unpacking the object variant type for name and calling a handler proc with the unpacked value. The variantTypeName is inferred from name, see the proc variantName. The name of the killKind is inferred from name, see the proc killKindName. The name of msgField is inferred from type, see the proc fieldName. The proc is generated based on the registered routes according to this pattern:

 proc routeMessage\*(msg: <variantTypeName>, hub: ChannelHub) =
   case msg.kind:
   --- Repeat per route - start ---
   of <enumKind>:
     <handlerProc>(msg.<msgField>, hub) # if sync handlerProc
     asyncSpawn <handlerProc>(msg.<msgField>, hub) # if async handlerProc
   --- Repeat per route - end ---
   of <killKind>: shutDownServer()
This proc should only be used by macros in this and other integration modules.

Source   Edit  
proc genNewChannelHubProc(): NimNode {....raises: [], tags: [], forbids: [].}

Generates a proc new for instantiating a ChannelHub with a channel to send messages to each thread.

Uses channelHub: addChannel to instantiate, open and add a channel. Uses variantName to infer the name Message-object-variant-type. The proc is generated based on the registered threadnames according to this pattern:

 proc new\*(t: typedesc[ChannelHub], capacity: int = 500): ChannelHub =
   result = ChannelHub(channels: initTable[pointer, pointer]())
   --- Repeat per threadname - start ---
   result.addChannel(<variantName>)
   --- Repeat per threadname - end ---

Source   Edit  
proc genSendKillMessageProc(name: ThreadName): NimNode {....raises: [], tags: [],
    forbids: [].}

Generates a proc sendKillMessage.

These procs send a message that triggers the graceful shutdown of a thread. The thread to send the message to is inferred based on the object-variant for messages to that thread. The name of the object-variant is inferred from name via variantName.

Source   Edit  
proc getSections(body: NimNode): Table[Section, NimNode] {....raises: [ValueError],
    tags: [], forbids: [].}
Source   Edit  
proc killKindName(name: ThreadName): string {....raises: [], tags: [], forbids: [].}
Infers the name of the enum-kind for a message that kills the thread associated with name from name. Source   Edit  
proc kindName(node: NimNode): string {....raises: [ValueError], tags: [],
                                       forbids: [].}
Infers the name of a kind of an enum from a type Source   Edit  
proc signalName(name: ThreadName): string {....raises: [], tags: [], forbids: [].}
Infers the name of the thread-signal used to wake up the thread from sleeping Source   Edit  
proc threadVariableName(name: ThreadName): string {....raises: [], tags: [],
    forbids: [].}
Infers the name of the variable containing the Thread that runs the server from name Source   Edit  
proc variantName(name: ThreadName): string {....raises: [], tags: [], forbids: [].}
Infers the name of the Message-object-variant-type associated with name from name Source   Edit  

Macros

macro prepareServers()

Generates the remaining code that can only be provided once all threadServers have been defined and are known by threadButler.

The generated procs are:

  1. A routing proc for every registered thread. See genMessageRouter for specifics.
  2. A new(ChannelHub) proc to instantiate a ChannelHub
  3. A destroy proc to destroy a ChannelHub
Source   Edit  
macro threadServer(name: static string; body: untyped)

Defines a threadServer called name and registers it and its contents in body with threadButler.

The body may declare any of these 3 sections:

  • properties
  • messageTypes
  • handlers

procs in the handler sections must have the shape:

proc <procName>(msg: <YourMsgType>, hub: ChannelHub)

Generates all types and procs needed for message-passing for name:

  1. An enum based representing all different types of messages that can be sent to the thread name.
  2. An object variant that wraps any message to be sent through a channel to the thread name.
  3. Generic sendMessage procs for sending messages to name by:
    • receiving a message-type
    • wrapping it in the object variant from 2)
    • sending that to a channel to the thread name.
  4. Specific sendKillMessage procs for sending a "kill" message to name
  5. An (internal) initServer proc to create a Server
  6. A global variable that contains the thread that name runs on

Note, this does not include all generated code. See prepareServers for the remaining code that should be called after all threadServers have been declared.

Source   Edit  
macro toThreadVariable(name: static string): untyped
Generates the identifier for the global variable containing the thread for name. Source   Edit  
macro toVariantType(name: static string): untyped
Generate the typedesc identifier for the message-wrapper-type Source   Edit