All Projects → jackhftang → threadproxy.nim

jackhftang / threadproxy.nim

Licence: other
Simplify Nim Inter-Thread Communication

Programming Languages

nim
578 projects

Projects that are alternatives of or similar to threadproxy.nim

Sobjectizer
An implementation of Actor, Publish-Subscribe, and CSP models in one rather small C++ framework. With performance, quality, and stability proved by years in the production.
Stars: ✭ 172 (+647.83%)
Mutual labels:  thread
Preact Worker Demo
Demo of preact rendering an entire app in a Web Worker.
Stars: ✭ 204 (+786.96%)
Mutual labels:  thread
Thread
type safe multi-threading made easier
Stars: ✭ 34 (+47.83%)
Mutual labels:  thread
Useworker
⚛️ useWorker() - A React Hook for Blocking-Free Background Tasks
Stars: ✭ 2,233 (+9608.7%)
Mutual labels:  thread
Adi
ADI(Android Debug Intensive) 是通过 JVMTI 实现的 Android 应用开发调试的增强工具集,目前主要提供性能相关的监控能力。
Stars: ✭ 194 (+743.48%)
Mutual labels:  thread
Wasm Worker
Move a WebAssembly module into its own thread
Stars: ✭ 215 (+834.78%)
Mutual labels:  thread
Kommander Ios
A lightweight, pure-Swift library for manage the task execution in different threads. Through the definition a simple but powerful concept, Kommand.
Stars: ✭ 167 (+626.09%)
Mutual labels:  thread
openthread-mqttsn
This repository contains examples using MQTT-SN client implementation for Thread network based on OpenThread SDK.
Stars: ✭ 22 (-4.35%)
Mutual labels:  thread
Gear Lib
Gear-Lib, C library for IOT Embedded Multimedia and Network
Stars: ✭ 2,381 (+10252.17%)
Mutual labels:  thread
CorePartition
Universal Cooperative Multithread Lib with real time Scheduler that was designed to work, virtually, into any modern micro controller or Microchip and, also, for user space applications for modern OS (Mac, Linux, Windows) or on FreeRTOS as well. Supports C and C++
Stars: ✭ 18 (-21.74%)
Mutual labels:  thread
Libchef
🍀 c++ standalone header-only basic library. || c++头文件实现无第三方依赖基础库
Stars: ✭ 178 (+673.91%)
Mutual labels:  thread
React Native Multithreading
🧵 Fast and easy multithreading for React Native using JSI
Stars: ✭ 164 (+613.04%)
Mutual labels:  thread
Duka
duka - Dukascopy historical data downloader
Stars: ✭ 241 (+947.83%)
Mutual labels:  thread
Java Concurrency Examples
Java Concurrency/Multithreading Tutorial with Examples for Dummies
Stars: ✭ 173 (+652.17%)
Mutual labels:  thread
youtube-dl-nas
youtube download queue websocket server with login for private NAS.
Stars: ✭ 136 (+491.3%)
Mutual labels:  thread
Nevercrash
🌍 全局捕获Crash。信NeverCrash,永不Crash。
Stars: ✭ 170 (+639.13%)
Mutual labels:  thread
Refire Forum
Serverless discussion forum built with React, Redux and Firebase
Stars: ✭ 206 (+795.65%)
Mutual labels:  thread
concurrent-programming-for-java
🚀Java并发编程实战
Stars: ✭ 55 (+139.13%)
Mutual labels:  thread
jstackSeries.sh
Script for capturing a series of thread dumps from a Java process using jstack (on Linux and Windows)
Stars: ✭ 28 (+21.74%)
Mutual labels:  thread
python-PooledProcessMixIn
Fast Concurrent Pool of preforked-processes and threads MixIn for python's socket server
Stars: ✭ 31 (+34.78%)
Mutual labels:  thread

ThreadProxy

Simplify Nim Inter-Thread Communication

Overview

ThreadProxy help you manage threads and channels. You don't have to declare them explicitly. ThreadProxy also provide a little thread-name-system and do name resolution for you.

Some key-points:

  • JSON is the ONLY data exchange format.
  • Each thread has a unique name assigned at creation.
  • Each thread can register a handle on action with on(...)
  • Each thread start processesing its messages with poll().
  • Threads can talk with each other with name reference.
  • Threads can talk with each other in one-way with send(...): Future[void].
  • Threads can talk with each other in two-way with ask(...): Future[JsonNode].
  • The order of creation of threads does not matter as long as the target thread is running at the time of calling send or ask.

Internally:

  • After 0.2.0, each thread is associated with two channels. One is for internal messages that need to preempt other messages. The other one is for normal messages.

Usage

The typical pattern should look like the following.

import threadproxy                                    # ---- 1

proc fooMain(proxy: ThreadProxy) {.thread.} =         # ---- 2 
  # setup and then poll threadProxy
  proxy.on "action1", proc(data: JsonNode): Future[JsonNode] {.gcsafe.} =   # ---- 3 
    result = action1(data)
  
  proxy.onData "action2":                             # ---- 4
    result = action2(data)

  proxy.onDefault proc(action: string, data: JsonNode): Future[JsonNode] {.gcsafe.} =  # ---- 5
    reuslt = ...

  # ... 
  asyncCheck proxy.poll()                             # ---- 6

  # ... 
  # do something here
  # ... 
  
  runForever()

proc main() =
  # create, setup and then poll mainThreadProxy
  let proxy = newMainThreadProxy("main")              # ---- 7
  proxy.onData "action1": result = action1(data)      # ---- 8
  proxy.onDefaultData: result = handler(action, data) 
  # ...
  asyncCheck proxy.poll()                             # ---- 9

  # create threads
  proxy.createThread("foo1", fooMain)                 # ---- 10
  proxy.createThread("foo2", fooMain)
  #... 

  # ...
  # do something here
  # ...

  runForever()

when isMainModeul:
  main()
  1. import threadproxy will also import json, asyncdispatch. They are almost always used together.
  2. Define an entry function for threads. The argument have to be a ThreadProxy unless you want to create thread manually. Also note that the {.thread.} pragma must be present.
  3. Define a handler for action. The handler function is responsible for both send and ask handling. A nil return value will be converted to JNull
  4. onData is a template version of on. It saves you from typing proc... everytime. The argument data is injected and so the name onData
  5. Default handler for all unregistered actions. There is also a template version onDefaultData that work similarly. Only the last ONE default hanlder is registered.
  6. Start processing messages on channels asychronously. This must be called exactly once. If poll is not called, nothing will happen. If it is called more than one time during running, a PollConflictError will raise.
  7. Create a MainThreadProxy with a name. MainThreadProxy is also a ThreadProxy but with responsibilities to handle threads and channels.
  8. Define handlers for MainThreadProxy similar to that in fooMain.
  9. Start processing messages similar to that in fooMain.
  10. Create thread with a name and entry function.

Examples

Example 1: simple ask

import threadproxy

proc workerMain(proxy: ThreadProxy) {.thread.} =
  # register action handler
  proxy.onData "sum":
    var x = 0
    for n in data:
      x += n.getInt()
    return %x

  # start processing channel
  waitFor proxy.poll()

proc main() =
  let proxy = newMainThreadProxy("master")
  asyncCheck proxy.poll()

  # create worker thread
  proxy.createThread("worker_0", workerMain)

  # ask worker_0 to double 10
  let answer = waitFor proxy.ask("worker_0", "sum", %[2,3,5,7])
  assert answer == %17

when isMainModule:
  main()

Example 2: pulling M jobs from N workers and collect result in collector

import threadproxy, deques

const M = 40

proc fib(x: int): int = 
  if x <= 1: 1 
  else: fib(x-1) + fib(x-2)

proc collectorMain(proxy: ThreadProxy) {.thread.} =
  var done = 0
  proxy.onData "result":
    let name = data["name"].getStr()
    let x = data["x"].getInt()
    let y = data["y"].getInt()
    echo "collector receive job ", x, " result from ", name, " fib(", x, ") = ", y
    done += 1
    if done >= M:
      # all done
      asyncCheck proxy.send("master", "stop")
  waitFor proxy.poll()

proc workerMain(proxy: ThreadProxy) {.thread.} =
  # start processing channel
  asyncCheck proxy.poll()

  proc process() {.async.} =
    let job = await proxy.ask("master", "job")
    if job.kind == JNull: 
      # no more job
      proxy.stop()
    else:
      # process job
      let x = job.getInt()
      echo proxy.name, " is finding fib(", x, ")"
      await proxy.send("collector", "result", %*{
        "name": proxy.name,
        "x": x,
        "y": fib(x)
      })

  while proxy.isRunning:
    waitFor process()

proc main() =
  # prepare jobs
  var jobs = initDeque[int]()
  for i in 1..M: jobs.addLast i

  # create and setup MainThreadProxy
  let proxy = newMainThreadProxy("master")

  proxy.onData "stop": proxy.stop()
  
  proxy.onData "job":
    if jobs.len > 0:
      result = %jobs.popFirst
    else:
      # return null if no more job
      result = newJNull()

  # create collector thread
  proxy.createThread("collector", collectorMain)
  
  # create N threads
  let N = 4
  for i in 0 ..< N:
    proxy.createThread("worker_" & $i, workerMain)

  # poll until proxy stop
  waitFor proxy.poll()

when isMainModule:
  main()

see /examples for more examples.

Manually Create Thread

If you want to pass more things into the main procedure of threads, you need to generate a token by calling createToken in mainThreadProxy and then pass the token to the main procedure and then call newThreadProxy in that threads.

Example

import threadproxy, asyncdispatch

type
  WorkerThreadArgs = object
    multiplier: int
    token: ThreadToken

proc workerMain(args: WorkerThreadArgs) {.thread.} =
  let proxy = newThreadProxy(args.token)

  # register action handler
  proxy.onData "multiply":
    let x = data.getInt()
    return %(args.multiplier*x)

  # start processing channel
  waitFor proxy.poll()

proc main() =
  let proxy = newMainThreadProxy("master")
  asyncCheck proxy.poll()

  # create thread token
  let token = proxy.createToken("worker_0")

  # NOTE: You are responsible to make sure that
  # the variable workerThread live longer than the thread itself
  var workerThread: Thread[WorkerThreadArgs]
  createThread(workerThread, workerMain, WorkerThreadArgs(
    multiplier: 3,
    token: token
  ))


  # ask worker_0 to double 10
  let answer = waitFor proxy.ask("worker_0", "multiply", %10)
  assert answer == %30

when isMainModule:
  main()

API

see here

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].