Workers and multi-threading
Allure has "Worker" in it's title, you possibly came here because of that.
It really provides support for workers, and that's a big feature that comes in hand with other parallel features.
This is a bonus feature, which allows you to create workers, enqueue functions, or make entire master-worker setups with microservices, queues and management of worker workload.
Allure:Workers
A worker is a specific object that wraps a coroutine thread.
It's merged with a queue and some utilities, like enqueuing and dequeuing.
local worker1, worker2, worker3 = Allure:Workers(3)It creates and returns the specified amount of workers.
Enqueuing functions
A worker has Worker.Queue - a table of functions, tasks to be executed.
Tasks are executed one by one, and you can always add a new task:
local worker = Allure:Workers(1)
worker:Enqueue(function()
print("Working!")
end)
worker:Enqueue(function()
print("Working again!")
end)Working!
Working again!:Enqueue also accepts arguments, if you have some function predefined:
local fn = function(a, b)
print(a + b)
end
worker:Enqueue(fn, 10, 15)
worker:Enqueue(fn, 25, -10)25
15Dequeuing
Analogically, dequeue the last enqueued task:
local worker = Allure:Workers(1)
worker:Enqueue(function()
print("Working!")
end)
worker:Dequeue()And dequeue a specific function at position n via :Dequeue(n)
But the code snippet above possibly won't work, because the function is consumed immediately, so nothing will be dequeued.
We need a waiting method.
Yielding inside of worker tasks
The problem is, task.wait will stop the running task but will not stop the queue.
So here we have a custom yielding method that entirely replicates task.wait but also sets a flag.
local worker = Allure:Workers(1)
worker:Enqueue(function()
print("Working")
worker:Yield(5)
end)
worker:Enqueue(function() end)
worker:Dequeue()Now our first task will yield the worker for 5 seconds, and our dequeue will actually find the time to dequeue the second task.
Hooking functions to the queue
Any Worker has .Queue and .QueueHook.
All functions within .QueueHook are called whenever some task is dequeued.
Notice, all hooks are called on the coroutine thread.
local worker1, worker2 = Allure:Workers(2)
worker1.QueueHook["test"] = function()
print("Some task was dequeued")
end
worker1:Enqueue(function()
print("This is the first task for worker1")
end)This is the first task for worker1
Some task was dequeuedKilling the Worker
To kill the Worker, simply call Worker:Kill()
This will close the Worker.Coroutine, empty the Worker.Queue and set some flags.
local worker = Allure:Workers(1)
worker:Enqueue(function()
print("This is the first task for worker")
worker:Yield(2)
end)
worker.QueueHook["consumeAndClose"] = function()
worker:Kill()
endWorkers within Nodes
Workers aren't just some arbitrary utility given.
You can easily create a Worker node by having all functions enqueue themselves into workers, or a Master node with Worker children nodes, or Microservice Nodes, etc.
We have a shortcut for that.
local module = Allure:Node() {} {}
local worker = module:Workers(1)
module.AsyncFunc = worker:Function(function(self, a, b)
print("This is being executed on worker!")
print(a + b)
end)
return module()local Node = require(path.to.Node)
Node:AsyncFunc(10, 5)
Node:AsyncFunc(7, 14)This is being executed on worker!
15
This is being executed on worker!
21Very neat.
Type safety also does not go anywhere.