
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 noobject
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 thepcall
function. pcall
- Function that behaves like
pcall
function of the Lua base library but allows calls ofcoroutine.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-routinecoroutine
. By default, this method raises an ordinary Lua error with messageerror
. If thedebug.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 tostep
orrun
. idle([timeout])
- Method called when there are no running threads available for scheduling for until the moment specified by
timeout
. If notimeout
is available, then the this method should return immediatly. By default this method execute a busy waiting at least until the momenttimeout
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 returntrue
. 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 methodstep()
). All its extra arguments are returned by the operation that suspended the co-routinecoroutine
. Such suspending operations are the methodsstart
,resume
,suspend
and the functioncoroutine.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 methodstep()
). If notimeout
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 methodstep()
). 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 notime
is provided, the co-routine is suspended until it is registered again in the scheduler by operationregister
or resumed by methodresume
. 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 theos.time
function of Lua and therefore has only precision of seconds. To use the scheduler with a higher precision, redefine this method.
Remarks
- This class can be used as an instance of itself, therefore all methods can be executed over the class itself.
- Since standard Lua does not provide operations to suspend execution for some time, the behavior of method
idle
is to perform a busy-waiting until the time specifield. This is extremelly undesired in time-shared systems. Therefore, it is strongly recommended to replace this implementation for a more efficient one. For example, using thesocket.sleep
function provided by LuaSocket package.function scheduler:idle(timeout) socket.sleep(timeout - self:time()) end
- This class provides a verbose message manager (instance of
Verbose
) in fieldverbose
that offers the following flags that can be activated to generate messages about the internal actions performed by the scheduler.concurrency
- Group of all message flags described below.
copcall
- Messages describing the actions related to the management of the co-routine-compatiable pcall implementation.
threads
- Messages describing the actions related to the management of scheduled threads, e.g. resuming, suspending, registration, etc.
scheduler
- Messages describing the actions related to the internal control of the scheduling mechanism, e.g. resuming cycles, idle time, etc.
--[[VERBOSE]]
in the implementation of this class. An easy way to turn the verbose support on or off is to replace all occourences of "--[[VERBOSE]]
" by "-- [[VERBOSE]]
" and vice-versa.
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.