http://pastebin.com/9N7Jdhk0
I rewrote this and now it's a lot more cleaner and modular. (Not sure about performance) and has a working windowed object so you can actually use it for multitasking shells!
Examples (Usage):
Spoiler
os.loadAPI("multitask") --#load it
thread = multitask.constructLayered(multitask.threadLayers) --#this creates an empty thread (think of it like a container for now)
f = function() while true do print(os.pullEvent()) end end --#function to put inside the thread
thread.init(f) --#init thread, now it has the function inside
thread.resume() --#Always resume after initing! This will execute code to the first yield
--#here it's the same but for other function:
thread2 = multitask.constructLayered(multitask.threadLayers) --#don't use the same thread because tables are pointer based
f2 = function() os.pullEvent() print("2") os.pullEvent() print("1") end
thread2.init(f2)
thread2.resume()
--#so here comes the interesting part
system = multitask.constructLayered(multitask.threadingSystemLayers) --#this is a system, a container for multiple threads
system.init() --#always init everything!
system.add(thread)
system.add(thread2)
for i=1,10 do
system.resume(os.pullEvent()) --#resume ten times
end
(All functions use dot notation: system.init() etc.)
Threads:
Spoiler
Creating a thread: (f is function)thread = multitask.constructLayered(multitask.threadLayers) --#this creates an empty thread (think of it like a container for now)
thread.init(f) --#init thread, now it has the function inside
thread.resume() --#Always resume after initing! This will execute code to the first yield
Creating a thread with window: (f is function, w is window)thread = multitask.constructLayered(multitask.threadLayers) --#this creates an empty thread (think of it like a container for now)
thread.init(f) --#init thread, now it has the function inside
thread.assignWindow(w) --#Do it before first resuming
thread.resume() --#Always resume after initing! This will execute code to the first yield
Resuming a thread with event:thread.resume("key",28)
Get thread window if assigned:
thread.getWindow()
all functions: see code and notes in the bottom of system documentation
Spoiler
init - must be called before usage, no output or argumentsadd(thread) -adds thread to the system, returns nothing
resume(event, par1, …) -resumes all (unpaused) threads
kill(index) - kills thread with index of the first arg, index
count() - returns how many alive threads exist (well, dead threads live one extra step after they died because of how it's implemented but you won't notice)
getID(index) - returns identifier for index (because indexes change when a thread is killed but identifiers don't)
getDataForIndex(index, key) - returns data assigned with index with key
setDataForIndex(index, key, value) - self explanatory
… (documentation is WIP)
also you can read the code (because function names are self explanatory) just remember:
obj and child (sometimes _) are passed by the layer construction, not you
functions are packed into layers that you add when calling constructLayered
there's threads and thread systems
Changlelog
Spoiler
13. 6. 2016Fixed some things, not that much but myshell (see in comments) actually works and added experimental multi window showed support
201606idk:
http://pastebin.com/h3akBGna
Old post:
Spoiler
So how this works?My multitasking library is layered, so you have to change minimal functionality to e.g. switch from the coroutine to a new one (like states).
Function list (documentation)
Spoiler
Please note: my namings are not really good, where I say 'thread' or 'thread table' I probably mean the table containing the coroutine and other data.Thread functions (You will probably not want to use these):
Spoiler
Each thread object has a resume function and a status function which will be always the most advanced layer. You can leave out layers (From 20151123), you'll want it when you want to make a pausable non-CC thread table.These functions (except newThread) accept a function or any table that has resume and status functions (thread tables generated by these will all have it) so you can stack your own functionality and make custom layers and re-order them (From 20151123). When you give them a function, however, they will add all layers that are below them (e.g. advthread will add thread, ccthread and pausable, too)
newThread(function)
Spoiler
Returns a thread table with these:-fields:
co - coroutine
-functions:
cresume - resumes thread with args (args: self, event)
cstatus - gets coroutine status (args: self)
Spoiler
Returned thread table has all functions and fields of Basic Thread, has extra ones:-ccresume(self,event) - only resumes thread if its filter is equal to event, automatically sets new filter returned by the coroutine
-filter - string or nil, thread's filter
Spoiler
This layer adds pausing functionality to the thread table generated:Functions:
presume(self,event) - calls the below layer's (less intelligent = below) resume only when not paused, when paused queues it in his own event queue
clearEvents(self) - clears event queue, might take a while when the event queue has lots of items
setPause(self,pause) pause is boolean indicating the new paused status (true = paused), when unpausing event queue is automatically cleared
Fields:
events - table, the thread's event queue
paused - boolean, paused status
Spoiler
This is a very simple layer when talking about single thread tables (not very simple when about its system):Adds 2 fields:
msgs - table, systems use this for inter-proccess messanging
data - table, systems use this for adding support for the user to enter its custom values (like thread's name)
Spoiler
All thread systems listed here will have these functions:kill(self,n) - kills thread with index of n - use INDEX
resume(self,event) - resumes all threads with event by calling resume (always the top layer)
add(self,function or thread table) - adds a new thread to the system
ended(self,id) - checks for thread status of ID, returns true if ID is killed
onKill - delegate, called when a thread is killed
onAdd - delegate, called when a thread is added
getID(self,index) - converts index to id
findIndex(self,id) - converts id to index, please note this is VERY SLOW compared to getting id, so
threads - table with all thread tables
anyended - boolean, true if a thread already ended
allended - boolean, true if all threads ended
count - count of threads
running - id of running thread, nil if none
Index vs id - (From version 20151122) indexes change when a thread is killed but ids don't so use ids for identification.
What you should probably do is having a custom index to id table created with the findIndex function and recalculate when onAdd or onKill is called.
These two have only the universal functions:
newCThreadSystem - don't use this. I implemented this because it was just one line, use it only in non-CC lua programs
newCCThreadSystem - use this for parallel (see example)
These have more functions than the universal:
newPausableSystem - system of pausables
Spoiler
more functions:setPause(self,index,bool) - self explantory, use INDEX
getPause(self,index) - use INDEX
When a thread is paused he will receive all events in his event queue, the event queue will be emptied when unpaused
queueEventAsync(self,index,eventdata) - sends event to his queue async, so next unpausing he will receive it. eventdata is a table, use INDEX
queueEventSync(self,index,eventdata) - sends event async and unpauses thread if not paused and returns true, if paused it will queue it async (nov 22 2015 - whoops and return nothing)
onPauseChange - delegate, args: index, bool (new pause)
newAdvancedSystem - system of advthreads
Spoiler
This thread system has all functions of pausable but has extra support for the user implementing inter-thread messangingaddMessage = function(self,i,msg) - i is INDEX, msg is table
viewMessage = function(self,i) - returns the oldest message but doesn't delete it - i is INDEX
getMessage = function(self,i) - returns the oldest message and deletes it - i is INDEX
viewMessages = function(self,i) - returns table of all messages, i is INDEX
getMessages = function(self,i) - returns table of all messages and clears messages of i, i is INDEX
This thread system also has a data table attached to each thread table where you can save your own variables
setData = function(self,i,key,value) - i is INDEX
getData = function(self,i,key) - i is INDEX
onDataChange - delegate, args: index, key, value
newWinowedSystem - system of advthreads with windows - This one has very custom functions, and is pretty buggy (nov 22 2015, it is a WIP) so please don't use it
delegate() and event(), for more information on these please check out their own separate thread found in APIs and Utilities.
newLayer(table or function, converter) - does things you need when making custom threads: give it a table or function as first argument and a converter function as second argument and it will convert the first arg if it's a function and return thread table and parent interface (has resume and status functions), does type checking and erroring for you, when types are invalid it will error at both 2 and 3 levels (the place where you called newLayer and that place's caller)
Please note: when you see a date in brackets it means that there is something that will be changed or something that is only actual for that date, the documentation is not done yet, and it will change. Function names will maybe change
Example:
Parallel API rewrite:
os.loadAPI("multitask")
function waitForAny(...)
local system = newCCThreadSystem(...)
while not system.anyended do
system:resume({os.pullEvent()})
end
end
function waitForAll(...)
local system = newCCThreadSystem(...)
while system.count > 0 do
system:resume({os.pullEvent()})
end
end
Please note, if you published system you could call system:add(function) to add a function and next resuming you runned that extra function.
Another example: adding custom layer on top of Advanced layer without the CC layer
--#f is a function, multitask lib. is loaded by running it (in default env)
local th = newThread(f) --#First we create a thread from f
local th = newPausable(th) --#We are doing this one by one because we don't want the CC Thread layer.
local th = newAdvThread(th)
--#Now we add our custom layer on top of advanced layer(we could add it below, too) that when our coroutine dies it logs it.
--#If we had a logging API loaded in Log with function Debug that takes one param: string:
local function newLogging(t,conv) --#t can be a function and a table
t,parent = newLayer(t,conv or newAdvThread) --#Use the new function to get parent and auto convert if neccessary
t.resume = function(self,...)
parent.resume(self,...)
if parent.status(self) == "dead" then
Log.Debug("This thread is dead :(/>/>/>/>/>")
end
end
--#You can modify everything, but always return a table with a resume and status functions (can be inherited or custom)
return t
end
th = newLogging(th) --#Done!
Changelog:
Spoiler
version 20151128:http://pastebin.com/EVE61iuL
http://pastie.org/10586854
Error messages now are more intelligent
Now almost all functions in this library have type checking
Added config option at the top of the code to disable type checking (speeds up in average 50% now)
Fixed 1 bug and 1 wrong design decision leading to another bug
Added 2 extra delegates (more coming in future, maybe with a special auto-delegate generating system)
version 20151124:
http://pastebin.com/YbYQVA08
http://pastie.org/10578463
!!!Renamed in Pausables setPause to setPaused!!!
!!!Thread tables now need an event table not unpacked event!!!
Thread systems now are threads too so you can have thread systems of thread systems too
Optimized by 15% (Yes, I measured it), more optimizations will come because I figured out what's the slowest part
I also modularized the code, so you now have a newLayer function that does interesting things (check the documentation)
version 20151123:
http://pastebin.com/2FCAC7qU
http://pastie.org/10576407
Added support for leaving out threads, finally tested newXThread accepting lower (and higher) layer threads, added ability to have lower layer threads in higher layer systems so it won't crash (it will return false or nil like if you enter invalid index)
version 20151122:
http://pastebin.com/cEdQ4DM6
Or if above fails try:
http://pastie.org/10574286
Added new index mechanisms, fixed some bugs (with count, kill, ended), added alive function to systems
Original release:
http://pastebin.com/V6pfZAcf
Please report any bugs or unneccessary features found and please share your opinion!