Thread Scheduler

loop.thread.Scheduler


Class of objects that provides a scheduling policy for management of a collection of co-routines that represent independent threads of execution. This class also provides some basic operations for synchronization of the scheduled threads. This class is useful for implementation of multi-threading support in Lua applications.

Each class maintains two lists of threads, one for the ones ready for execution (i.e. running) and another for those suspended for some time (i.e. sleeping). The scheduling policy is round-robin, resuming all threads ready for running one after the other. When a thread finishes it is removed from the scheduler and an optional trap function is called to handle its last results or any error raised during its execution. The scheduler also automatically moves threads from the list of sleeping threads to the running list when their sleeping time are finished.

Behavior

Initialization

Scheduler([object])
Creates a new instance using the table object adding to it all proper data strucutures used to store the lists of running and sleeping threads and the set of optional trap functions of each scheduled thread. Additionally, it also initialize the internal state of the new scheduler instance. If no object is provided then a new table is used.

Fields

current
Co-routine of the thread that currenly holds the right to execute. This co-routine is not always the result of coroutine.running() because in some circustances other internal co-routines related to it are acctually executing. That is the case when a thread executes the pcall function.
pcall
Function that behaves like pcall function of the Lua base library but allows calls of coroutine.yield().
traps
Table that maps registered co-routine to its trap function. A trap function is a function called when a co-routine finishes. The trap function receives a boolean value indicating if the call ended normally or not, i.e. due to an error raised. As additional argument, the trap function receives the results of the co-routine. However, if the co-routine ended with an error, the error message is the only additional parameter of the trap function.

Methods

error(coroutine, error)
Method used to handle the error message error of co-routine coroutine. By default, this method raises an ordinary Lua error with message error. If the debug.traceback function is available at the time this class is first required, then the error message raised contains the original stack of the error produced. This method may be redefined for produce better error messages.
halt()
Method used to stop the scheduling loop performed by method run. The scheduling then may be resumed by future calls to step or run.
idle([timeout])
Method called when there are no running threads available for scheduling for until the moment specified by timeout. If no timeout is available, then the this method should return immediatly. By default this method execute a busy waiting at least until the moment timeout is reached. It is strongly recommended that such implementation be replaced by a more efficient one, like the following code in Linux environments:
funtion scheduler:idle(timeout)
 if timeout then
 os.execute("sleep "..(timeout - self:time()))
 end end
register(coroutine)
Register a co-routine to be scheduled for execution. This method simply inserts the co-routine co-routine at the end of the running list of the scheduler and return true. However, if the co-routine is already registered then this method has no effect and returns no value.
remove(coroutine)
Removes a co-routine from the scheduler, therefore it is no more scheduled for execution. This method simply removes the co-routine co-routine from the lists of the scheduler (i.e. running or sleeping) and return the removed co-routine plus the time it was scheduled to be waken if it was sleeping. However, if the co-routine is not registered then this method has no effect and returns no value.
resume(coroutine, ...)
This method can only be called inside a scheduled co-routine. It immediatetly switches the execution to the co-routine coroutine. The calling co-routine is suspended until the next resuming cycle (see method step()). All its extra arguments are returned by the operation that suspended the co-routine coroutine. Such suspending operations are the methods start, resume, suspend and the function coroutine.yield of the Lua Base Library. If the resumed co-routine is already scheduled for execution then its execution is advanced in the running thread list but it is not scheduled again until the next resuming cycle. If the resumed co-routine was already executed in the current resuming cycle, then it is executed once more in the same cycle. This operation simulates an API for symetric co-routines using the asymetric co-routines of Lua.
run([timeout])
Starts to schedule registered co-routines until the moment given by timeout is reached by performing continuous resuming cycles (see method step()). If no timeout is provided, it schedules all co-routines until they all finish. If there are no registered co-routines, the call has no effect.
start(function, ...)
This method can only be called inside a scheduled co-routine. It creates a new co-routine for execution of function function with the arguments provided and immediatetly starts it. The calling co-routine is suspended until the next resuming cycle (see method step()).
step()
Performs a resuming cycle that consists of executing each scheduled co-routine ready for execution (i.e. running) at least once. Since, each co-routine is responsible to notify the moment for execution switch, a resuming cycle may last for an arbitrary long time, even forever. Additionally, a co-routine may be resumed more than once if resumed by method resume. Specifically, two co-routines may alternate execution forever inside a single resuming cycle if they resume each other mutually indefinetly. In such case, no other co-routine is resumed.
suspend([time])
This method can only be called inside a scheduled co-routine. It suspends the execution of the current co-routine for at least time seconds. If no time is provided, the co-routine is suspended until it is registered again in the scheduler by operation register or resumed by method resume.
time()
Method used to get the number of seconds counted by the system. This counting may be relative to any point in the past. This method is used to check the current moment of the system and therefore percieve time slapses. To define relative timouts use the formula scheduler:time() + seconds. By default, this method is based in the os.time function of Lua and therefore has only precision of seconds. To use the scheduler with a higher precision, redefine this method.

Remarks

Examples

Light control system

local scheduler = require "loop.thread.Scheduler"
function control(room)
 local c = 0
 while true do
 if room.light and not room.presence then
 c = c + 1
 if c > 5 then
 c = 0
 room.light = false
 else
 scheduler:suspend(1)
 end
 else
 c = 0
 scheduler:suspend(6)
 end
 end end function person(rooms)
 local room = 0
 while true do
 room = 1 + (room + math.random(#rooms-1)) % #rooms
 rooms[room].presence = true
 rooms[room].light = true
 scheduler:suspend(math.random(6))
 rooms[room].presence = false
 end end function printer(rooms)
 while true do
 for _, room in ipairs(rooms) do
 io.write(" ", room.presence and "*" or room.light and "." or " ")
 end
 print()
 scheduler:suspend(1)
 end end scheduler:register(coroutine.create(function()
 local rooms = {}
 for i = 1, 9 do rooms[i] = {} end
 scheduler:start(printer, rooms)
 scheduler:start(person, rooms)
 scheduler:suspend(60)
 print "starting light control ..."
 for _, room in ipairs(rooms) do
 scheduler:start(control, room)
 end end))
scheduler:run()

Copyright (C) 2004-2008 Tecgraf, PUC-RioThis project is currently being maintained by Tecgraf at PUC-Rio.