Fixed issues

This commit is contained in:
Ryan Ward 2021-06-19 20:24:23 -04:00
commit b2ae7181d6
14 changed files with 375 additions and 95 deletions

1
.gitignore vendored
View File

@ -2,6 +2,7 @@
test2.lua test2.lua
*.mp3 *.mp3
*.exe *.exe
*.dll
lanestestclient.lua lanestestclient.lua
lanestest.lua lanestest.lua
sample-node.lua sample-node.lua

View File

@ -1,4 +1,4 @@
Current Multi Version: 14.2.0 Current Multi Version: 15.0.0
# Multi static variables # Multi static variables
`multi.Version` — The current version of the library `multi.Version` — The current version of the library
@ -14,7 +14,7 @@ Current Multi Version: 14.2.0
</br>`multi.Priority_Idle` — Lowest level of pirority that can be given to a process </br>`multi.Priority_Idle` — Lowest level of pirority that can be given to a process
# Multi Runners # Multi Runners
`multi:lightloop()` — A light version of the mainloop `multi:lightloop()` — A light version of the mainloop doesn't run Coroutine based threads
</br>`multi:loveloop([BOOLEAN: light true])` — Run's all the love related features as well </br>`multi:loveloop([BOOLEAN: light true])` — Run's all the love related features as well
</br>`multi:mainloop([TABLE settings])` — This runs the mainloop by having its own internal while loop running </br>`multi:mainloop([TABLE settings])` — This runs the mainloop by having its own internal while loop running
</br>`multi:threadloop([TABLE settings])` — This runs the mainloop by having its own internal while loop running, but prioritizes threads over multi-objects </br>`multi:threadloop([TABLE settings])` — This runs the mainloop by having its own internal while loop running, but prioritizes threads over multi-objects
@ -153,7 +153,7 @@ The connect feature has some syntax sugar to it as seen below
Example: Example:
```lua ```lua
local multi = require("multi") multi,thread = require("multi"):init()
-- Lets create the events -- Lets create the events
yawn={} yawn={}
OnCustomSafeEvent=multi:newConnection(true) -- lets pcall the calls in case something goes wrong default OnCustomSafeEvent=multi:newConnection(true) -- lets pcall the calls in case something goes wrong default
@ -201,7 +201,7 @@ Timeouts are a collection of methods that allow you to handle timeouts. These on
```lua ```lua
package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path
multi = require("multi") multi,thread = require("multi"):init()
loop = multi:newLoop(function() loop = multi:newLoop(function()
-- do stuff -- do stuff
@ -224,7 +224,7 @@ loop:OnTimerResolved(function(self,...)
print(...) print(...)
end) end)
multi:lightloop() multi:mainloop()
``` ```
As mentioned above this is made much easier using threads As mentioned above this is made much easier using threads
```lua ```lua
@ -255,14 +255,14 @@ print(func(0))
Example: Example:
```lua ```lua
package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path
multi = require("multi") multi,thread = require("multi"):init()
multi:scheduleJob({min = 30},function() -- Every hour at minute 30 this event will be triggered! You can mix and match as well! multi:scheduleJob({min = 30},function() -- Every hour at minute 30 this event will be triggered! You can mix and match as well!
print("Hi") print("Hi")
end) end)
multi:scheduleJob({min = 30,hour = 0},function() -- Every day at 12:30AM this event will be triggered multi:scheduleJob({min = 30,hour = 0},function() -- Every day at 12:30AM this event will be triggered
print("Hi") print("Hi")
end) end)
multi:lightloop() multi:mainloop()
``` ```
# Universal Actor methods # Universal Actor methods
@ -287,7 +287,7 @@ All of these functions are found on actors
Example: Example:
```lua ```lua
local multi = require("multi") multi,thread = require("multi"):init()
count=0 count=0
-- A loop object is used to demostrate how one could use an event object. -- A loop object is used to demostrate how one could use an event object.
loop=multi:newLoop(function(self,dt) loop=multi:newLoop(function(self,dt)
@ -298,7 +298,7 @@ event:OnEvent(function(self) -- connect to the event object
loop:Destroy() -- destroys the loop from running! loop:Destroy() -- destroys the loop from running!
print("Stopped that loop!",count) print("Stopped that loop!",count)
end) -- events like alarms need to be reset the Reset() command works here as well end) -- events like alarms need to be reset the Reset() command works here as well
multi:lightloop() multi:mainloop()
``` ```
# Actor: Updaters # Actor: Updaters
@ -311,12 +311,12 @@ Updaters are a mix between both loops and steps. They were a way to add basic pr
Example: Example:
```lua ```lua
local multi = require("multi") multi,thread = require("multi"):init()
updater=multi:newUpdater(5000) -- simple, think of a loop with the skip feature of a step updater=multi:newUpdater(5000) -- simple, think of a loop with the skip feature of a step
updater:OnUpdate(function(self) updater:OnUpdate(function(self)
print("updating...") print("updating...")
end) end)
multi:lightloop() multi:mainloop()
``` ```
# Actor: Alarms # Actor: Alarms
@ -328,13 +328,13 @@ Alarms ring after a certain amount of time, but you need to reset the alarm ever
Example: Example:
```lua ```lua
local multi = require("multi") multi,thread = require("multi"):init()
alarm=multi:newAlarm(3) -- in seconds can go to .001 uses the built in os.clock() alarm=multi:newAlarm(3) -- in seconds can go to .001 uses the built in os.clock()
alarm:OnRing(function(a) alarm:OnRing(function(a)
print("3 Seconds have passed!") print("3 Seconds have passed!")
a:Reset(n) -- if n were nil it will reset back to 3, or it would reset to n seconds a:Reset(n) -- if n were nil it will reset back to 3, or it would reset to n seconds
end) end)
multi:lightloop() multi:mainloop()
``` ```
# Actor: Loops # Actor: Loops
@ -346,7 +346,7 @@ Loops are events that happen over and over until paused. They act like a while l
Example: Example:
```lua ```lua
package.path="?/init.lua;?.lua;"..package.path package.path="?/init.lua;?.lua;"..package.path
local multi = require("multi") multi,thread = require("multi"):init()
local a = 0 local a = 0
loop = multi:newLoop(function() loop = multi:newLoop(function()
a = a + 1 a = a + 1
@ -355,7 +355,7 @@ loop = multi:newLoop(function()
loop:Pause() loop:Pause()
end end
end) end)
multi:lightloop() multi:mainloop()
``` ```
# Actor: TLoops # Actor: TLoops
@ -366,7 +366,7 @@ multi:lightloop()
Example: Example:
```lua ```lua
package.path="?/init.lua;?.lua;"..package.path package.path="?/init.lua;?.lua;"..package.path
local multi = require("multi") multi,thread = require("multi"):init()
local a = 0 local a = 0
loop = multi:newTLoop(function() loop = multi:newTLoop(function()
a = a + 1 a = a + 1
@ -375,7 +375,7 @@ loop = multi:newTLoop(function()
loop:Pause() loop:Pause()
end end
end,1) end,1)
multi:lightloop() multi:mainloop()
``` ```
# Actor: Steps # Actor: Steps
@ -390,13 +390,13 @@ multi:lightloop()
Example: Example:
```lua ```lua
package.path="?/init.lua;?.lua;"..package.path package.path="?/init.lua;?.lua;"..package.path
local multi = require("multi") multi,thread = require("multi"):init()
multi:newStep(1,10,1,0):OnStep(function(step,pos) multi:newStep(1,10,1,0):OnStep(function(step,pos)
print(step,pos) print(step,pos)
end):OnEnd(fucntion(step) end):OnEnd(fucntion(step)
step:Destroy() step:Destroy()
end) end)
multi:lightloop() multi:mainloop()
``` ```
# Actor: TSteps # Actor: TSteps
@ -411,13 +411,13 @@ multi:lightloop()
Example: Example:
```lua ```lua
package.path="?/init.lua;?.lua;"..package.path package.path="?/init.lua;?.lua;"..package.path
local multi = require("multi") multi,thread = require("multi"):init()
multi:newTStep(1,10,1,1):OnStep(function(step,pos) multi:newTStep(1,10,1,1):OnStep(function(step,pos)
print(step,pos) print(step,pos)
end):OnEnd(fucntion(step) end):OnEnd(fucntion(step)
step:Destroy() step:Destroy()
end) end)
multi:lightloop() multi:mainloop()
``` ```
# Coroutine based Threading (CBT) # Coroutine based Threading (CBT)
@ -474,7 +474,7 @@ Example:
-- Jobs are not natively part of the multi library. I planned on adding them, but decided against it. Below is the code that would have been used. -- Jobs are not natively part of the multi library. I planned on adding them, but decided against it. Below is the code that would have been used.
-- Implementing a job manager using services -- Implementing a job manager using services
package.path="?/init.lua;?.lua;"..package.path package.path="?/init.lua;?.lua;"..package.path
local multi = require("multi") multi,thread = require("multi"):init()
multi.Jobs = multi:newService(function(self,jobs) multi.Jobs = multi:newService(function(self,jobs)
local job = table.remove(jobs,1) local job = table.remove(jobs,1)
if job and job.removed==nil then if job and job.removed==nil then
@ -532,6 +532,7 @@ jobsn[1]:removeJob() -- Select a job and remove it
multi.Jobs:removeJobs("test2") -- Remove all jobs names 'test2' multi.Jobs:removeJobs("test2") -- Remove all jobs names 'test2'
multi.Jobs.SetScheme(1) -- Jobs are internally a service, so setting scheme and priority multi.Jobs.SetScheme(1) -- Jobs are internally a service, so setting scheme and priority
multi.Jobs.SetPriority(multi.Priority_Core) multi.Jobs.SetPriority(multi.Priority_Core)
multi:mainloop()
``` ```
# CBT: newThread() # CBT: newThread()
@ -556,21 +557,82 @@ Constants
Examples: Examples:
```lua ```lua
package.path="?/init.lua;?.lua;"..package.path package.path="?/init.lua;?.lua;"..package.path
local multi = require("multi") multi,thread = require("multi"):init()
multi:newThread("Example of basic usage",function() multi:newThread("Example of basic usage",function()
while true do while true do
thread.sleep(1) thread.sleep(1)
print("We just made an alarm!") print("We just made an alarm!")
end end
end) end)
multi:lightloop() multi:mainloop()
``` ```
# CBT: newISOThread()
`th = multi:newThread([STRING name,] FUNCTION func, TABLE: env)` — Creates a new thread with name and function func. Sets the enviroment of the func to env. Both the thread.* and multi.* are automatically placed in the enviroment.
When within a thread, if you have any holding code you will want to use thread.* to give time to other threads while your code is running. This type of thread does not have access to outside local or globals. Only what is in the env can be seen. (This thread was made so pesudo threading could work)
Constants
---
- `th.Name` — Name of thread
- `th.Type` — Type="thread"
- `th.TID` — Thread ID
- `conn = th.OnError(FUNCTION: callback)` — Connect to an event which is triggered when an error is encountered within a thread
- `conn = th.OnDeath(FUNCTION: callback)` — Connect to an event which is triggered when the thread had either been killed or stopped running. (Not triggered when there is an error!)
- `boolean = th:isPaused()`\* — Returns true if a thread has been paused
- `self = th:Pause()`\* — Pauses a thread
- `self = th:Resume()`\* — Resumes a paused thread
- `self = th:Kill()`\* — Kills a thread
- `self = th:Destroy()`\* — Destroys a thread
<b>*</b>Using these methods on a thread directly you are making a request to a thread! The thread may not accept your request, but it most likely will. You can contorl the thread flow within the thread's function itself
```lua
package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path
multi,thread = require("multi"):init()
GLOBAL,THREAD = require("multi.integration.threading"):init() -- Auto detects your enviroment and uses what's available
jq = multi:newSystemThreadedJobQueue(5) -- Job queue with 4 worker threads
func = jq:newFunction("test",function(a,b)
THREAD.sleep(2)
return a+b
end)
for i = 1,10 do
func(i,i*3).connect(function(data)
print(data)
end)
end
local a = true
b = false
multi:newThread("Standard Thread 1",function()
while true do
thread.sleep(1)
print("Testing 1 ...",a,b,test)
end
end).OnError(function(self,msg)
print(msg)
end)
-- All upvalues are stripped! no access to the global, multi and thread are exposed however
multi:newISOThread("ISO Thread 2",function()
while true do
thread.sleep(1)
print("Testing 2 ...",a,b,test) -- a and b are nil, but test is true
end
end,{test=true,print=print})
.OnError(function(self,msg)
print(msg)
end)
multi:mainloop()
```
# System Threads (ST) - Multi-Integration Getting Started # System Threads (ST) - Multi-Integration Getting Started
The system threads need to be required seperatly. The system threads need to be required seperatly.
```lua ```lua
-- I recommend keeping these as globals. When using lanes you can use local and things will work, but if you use love2d and locals, upvalues are not transfered over threads and this can be an issue -- I recommend keeping these as globals. When using lanes you can use local and things will work, but if you use love2d and locals, upvalues are not transfered over threads and this can be an issue
GLOBAL, THREAD = require("multi.integration.lanesManager"):init() -- We will talk about the global and thread interface that is returned GLOBAL, THREAD = require("multi.integration.threading"):init() -- We will talk about the global and thread interface that is returned
GLOBAL, THREAD = require("multi.integration.loveManager"):init() GLOBAL, THREAD = require("multi.integration.loveManager"):init()
GLOBAL, THREAD = require("luvitManager") --* GLOBAL, THREAD = require("luvitManager") --*
``` ```
@ -609,8 +671,8 @@ ST - System Threads
System Threads are the feature that allows a user to interact with systen threads. It differs from regular coroutine based thread in how it can interact with variables. When using system threads the GLOBAL table is the "only way"* to send data. Spawning a System thread is really simple once all the required libraries are in place. See example below: System Threads are the feature that allows a user to interact with systen threads. It differs from regular coroutine based thread in how it can interact with variables. When using system threads the GLOBAL table is the "only way"* to send data. Spawning a System thread is really simple once all the required libraries are in place. See example below:
```lua ```lua
local multi = require("multi") -- keep this global when using lanes or implicitly define multi within the spawned thread multi,thread = require("multi"):init() -- keep this global when using lanes or implicitly define multi within the spawned thread
local GLOBAL, THREAD = require("multi.integration.lanesManager").init() local GLOBAL, THREAD = require("multi.integration.threading").init()
multi:newSystemThread("Example thread",function() multi:newSystemThread("Example thread",function()
local multi = require("multi") -- we are in a thread so lets not refer to that upvalue! local multi = require("multi") -- we are in a thread so lets not refer to that upvalue!
print("We have spawned a thread!") print("We have spawned a thread!")
@ -624,7 +686,7 @@ end,"A message that we are passing") -- There are restrictions on what can be pa
tloop = multi:newTLoop(function() tloop = multi:newTLoop(function()
print("I'm still kicking!") print("I'm still kicking!")
end,1) end,1)
multi:lightloop() multi:mainloop()
``` ```
<b>*</b>This isn't entirely true, as of right now the compatiablity with the lanes library and love2d engine have their own methods to share data, but if you would like to have your code work in both enviroments then using the GLOBAL table and the data structures provided by the multi library will ensure this happens. If you do not plan on having support for both platforms then feel free to use linda's in lanes and channels in love2d. <b>*</b>This isn't entirely true, as of right now the compatiablity with the lanes library and love2d engine have their own methods to share data, but if you would like to have your code work in both enviroments then using the GLOBAL table and the data structures provided by the multi library will ensure this happens. If you do not plan on having support for both platforms then feel free to use linda's in lanes and channels in love2d.
@ -638,8 +700,8 @@ When creating objects with a name they are automatically exposed to the GLOBAL t
```lua ```lua
-- Exposing a queue -- Exposing a queue
multi = require("multi") multi,thread = require("multi"):init()
local GLOBAL, THREAD = require("multi.integration.lanesManager").init() -- The standard setup above local GLOBAL, THREAD = require("multi.integration.threading").init() -- The standard setup above
queue = multi:newSystemThreadedQueue("myQueue"):init() -- We create and initiate the queue for the main thread queue = multi:newSystemThreadedQueue("myQueue"):init() -- We create and initiate the queue for the main thread
queue:push("This is a test!") -- We push some data onto the queue that other threads can consume and do stuff with queue:push("This is a test!") -- We push some data onto the queue that other threads can consume and do stuff with
multi:newSystemThread("Example thread",function() -- Create a system thread multi:newSystemThread("Example thread",function() -- Create a system thread
@ -647,7 +709,7 @@ multi:newSystemThread("Example thread",function() -- Create a system thread
local data = queue:pop() -- Get the data local data = queue:pop() -- Get the data
print(data) -- print the data print(data) -- print the data
end) end)
multi:lightloop() multi:mainloop()
``` ```
# ST - SystemThreadedQueue # ST - SystemThreadedQueue
@ -659,9 +721,9 @@ multi:lightloop()
Let's get into some examples: Let's get into some examples:
```lua ```lua
multi = require("multi") multi,thread = require("multi"):init()
thread_names = {"Thread_A","Thread_B","Thread_C","Thread_D"} thread_names = {"Thread_A","Thread_B","Thread_C","Thread_D"}
local GLOBAL, THREAD = require("multi.integration.lanesManager"):init() local GLOBAL, THREAD = require("multi.integration.threading"):init()
queue = multi:newSystemThreadedQueue("myQueue"):init() queue = multi:newSystemThreadedQueue("myQueue"):init()
for _,n in pairs(thread_names) do for _,n in pairs(thread_names) do
multi:newSystemThread(n,function() multi:newSystemThread(n,function()
@ -683,11 +745,21 @@ end):OnEvent(function()
print("No more data within the queue!") print("No more data within the queue!")
os.exit() os.exit()
end) end)
multi:lightloop() multi:mainloop()
``` ```
You have probable noticed that the output from this is a total mess! Well I though so too, and created the system threaded console! You have probable noticed that the output from this is a total mess! Well I though so too, and created the system threaded console!
# ST - Using the Console
`console = THREAD.getConsole()`
This does guarantee an order to console output, it does ensure that all things are on nice neat lines
```lua
multi,thread = require("multi"):init()
local GLOBAL, THREAD = require("multi.integration.threading"):init()
console.print("Hello World!")
```
# ST - SystemThreadedJobQueue # ST - SystemThreadedJobQueue
`jq = multi:newSystemThreadedJobQueue([NUMBER: threads])` — Creates a system threaded job queue with an optional number of threads `jq = multi:newSystemThreadedJobQueue([NUMBER: threads])` — Creates a system threaded job queue with an optional number of threads
- `jq.cores = (supplied number) or (the number of cores on your system*2)` - `jq.cores = (supplied number) or (the number of cores on your system*2)`
@ -703,8 +775,8 @@ You have probable noticed that the output from this is a total mess! Well I thou
Example: Example:
```lua ```lua
package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path
multi = require("multi") multi,thread = require("multi"):init()
GLOBAL, THREAD = require("multi.integration.lanesManager"):init() GLOBAL, THREAD = require("multi.integration.threading"):init()
local jq = multi:newSystemThreadedJobQueue(4) -- job queue using 4 cores local jq = multi:newSystemThreadedJobQueue(4) -- job queue using 4 cores
jq:doToAll(function() jq:doToAll(function()
Important = 15 Important = 15
@ -727,7 +799,7 @@ func(5,5).connect(function(ret)
print("Connected",ret) print("Connected",ret)
os.exit() os.exit()
end) end)
multi:lightloop() multi:mainloop()
``` ```
# ST - SystemThreadedTable # ST - SystemThreadedTable
`stt = multi:newSystemThreadedTable(STRING: name)` `stt = multi:newSystemThreadedTable(STRING: name)`
@ -738,15 +810,15 @@ multi:lightloop()
Example: Example:
```lua ```lua
package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path
multi = require("multi") multi,thread = require("multi"):init()
GLOBAL, THREAD = require("multi.integration.lanesManager"):init() GLOBAL, THREAD = require("multi.integration.threading"):init()
local stt = multi:newSystemThreadedTable("stt") local stt = multi:newSystemThreadedTable("stt")
stt["hello"] = "world" stt["hello"] = "world"
multi:newSystemThread("test thread",function() multi:newSystemThread("test thread",function()
local stt = GLOBAL["stt"]:init() local stt = GLOBAL["stt"]:init()
print(stt["hello"]) print(stt["hello"])
end) end)
multi:lightloop() multi:mainloop()
``` ```
# Network Threads - Multi-Integration WIP Being Reworked # Network Threads - Multi-Integration WIP Being Reworked
More of a fun project of mine then anything core to to the library it will be released and documented when it is ready. I do not have a timeframe for this More of a fun project of mine then anything core to to the library it will be released and documented when it is ready. I do not have a timeframe for this

View File

@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2020 Ryan Ward Copyright (c) 2021 Ryan Ward
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@ -1,6 +1,8 @@
# Multi Version: 15.0.0 Fake it, until you make it # Multi Version: 15.0.0 Fake it till you make it
**Key Changes** **Key Changes**
- Emulating system threading on a single thread - Emulating system threading on a single thread
- Purpose to allow consistant code that can scale when threading is available. Check out the changelog for more details
- Proper support for lua versions above 5.1 (More testing is needed, a full test suite is being developed and should be made available soon)
Found an issue? Please [submit it](https://github.com/rayaman/multi/issues) and I'll look into it! Found an issue? Please [submit it](https://github.com/rayaman/multi/issues) and I'll look into it!
@ -8,7 +10,7 @@ My multitasking library for lua. It is a pure lua binding, with exceptions of th
INSTALLING INSTALLING
---------- ----------
Links to dependicies: Links to dependencies:
[lanes](https://github.com/LuaLanes/lanes) [lanes](https://github.com/LuaLanes/lanes)
To install copy the multi folder into your environment and you are good to go</br> To install copy the multi folder into your environment and you are good to go</br>
@ -26,20 +28,29 @@ https://discord.gg/U8UspuA</br>
Planned features/TODO Planned features/TODO
--------------------- ---------------------
- [x] ~~Finish Documentation~~ Finished - [x] ~~Finish Documentation~~ Finished
- [ ] Create test suite
- [ ] Network Parallelism rework - [ ] Network Parallelism rework
- [ ] Fix some bugs
Usage: [Check out the documentation for more info](https://github.com/rayaman/multi/blob/master/Documentation.md)</br> Usage: [Check out the documentation for more info](https://github.com/rayaman/multi/blob/master/Documentation.md)</br>
----- -----
```lua ```lua
local multi, thread = require("multi").init() package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path
mutli:newThread("Example",function() local multi, thread = require("multi"):init()
GLOBAL, THREAD = require("multi.integration.threading"):init()
multi:newSystemThread("System Thread",function()
while true do while true do
thread.sleep(1) THREAD.sleep(1)
print("Hello!") print("World!")
end end
end) end)
multi:lightloop() multi:newThread("Coroutine Based Thread",function()
--multi:mainloop() while true do
print("Hello")
thread.sleep(1)
end
end)
multi:mainloop()
--[[ --[[
while true do while true do
multi:uManager() multi:uManager()

View File

@ -1,8 +1,7 @@
# Changelog # Changelog
Table of contents Table of contents
--- ---
[Update 14.2.0 - Bloatware Removed](#update-1420---bloatware-removed)</br>[Update 14.1.0 - A whole new world of possibilities](#update-1410---a-whole-new-world-of-possibilities)</br>[Update 14.0.0 - Consistency, Additions and Stability](#update-1400---consistency-additions-and-stability)</br>[Update 13.1.0 - Bug fixes and features added](#update-1310---bug-fixes-and-features-added)</br>[Update 13.0.0 - Added some documentation, and some new features too check it out!](#update-1300---added-some-documentation-and-some-new-features-too-check-it-out)</br>[Update 12.2.2 - Time for some more bug fixes!](#update-1222---time-for-some-more-bug-fixes)</br>[Update 12.2.1 - Time for some bug fixes!](#update-1221---time-for-some-bug-fixes)</br>[Update 12.2.0 - The chains of binding](#update-1220---the-chains-of-binding)</br>[Update 12.1.0 - Threads just can't hold on anymore](#update-1210---threads-just-cant-hold-on-anymore)</br>[Update: 12.0.0 - Big update (Lots of additions some changes)](#update-1200---big-update-lots-of-additions-some-changes)</br>[Update: 1.11.1 - Small Clarification on Love](#update-1111---small-clarification-on-love)</br>[Update: 1.11.0](#update-1110)</br>[Update: 1.10.0](#update-1100)</br>[Update: 1.9.2](#update-192)</br>[Update: 1.9.1 - Threads can now argue](#update-191---threads-can-now-argue)</br>[Update: 1.9.0](#update-190)</br>[Update: 1.8.7](#update-187)</br>[Update: 1.8.6](#update-186)</br>[Update: 1.8.5](#update-185)</br>[Update: 1.8.4](#update-184)</br>[Update: 1.8.3 - Mainloop recieves some needed overhauling](#update-183---mainloop-recieves-some-needed-overhauling)</br>[Update: 1.8.2](#update-182)</br>[Update: 1.8.1](#update-181)</br>[Update: 1.7.6](#update-176)</br>[Update: 1.7.5](#update-175)</br>[Update: 1.7.4](#update-174)</br>[Update: 1.7.3](#update-173)</br>[Update: 1.7.2](#update-172)</br>[Update: 1.7.1 - Bug Fixes Only](#update-171---bug-fixes-only)</br>[Update: 1.7.0 - Threading the systems](#update-170---threading-the-systems)</br>[Update: 1.6.0](#update-160)</br>[Update: 1.5.0](#update-150)</br>[Update: 1.4.1 (4/10/2017) - First Public release of the library](#update-141-4102017---first-public-release-of-the-library)</br>[Update: 1.4.0 (3/20/2017)](#update-140-3202017)</br>[Update: 1.3.0 (1/29/2017)](#update-130-1292017)</br>[Update: 1.2.0 (12.31.2016)](#update-120-12312016)</br>[Update: 1.1.0](#update-110)</br>[Update: 1.0.0](#update-100)</br>[Update: 0.6.3](#update-063)</br>[Update: 0.6.2](#update-062)</br>[Update: 0.6.1-6](#update-061-6)</br>[Update: 0.5.1-6](#update-051-6)</br>[Update: 0.4.1](#update-041)</br>[Update: 0.3.0 - The update that started it all](#update-030---the-update-that-started-it-all)</br>[Update: EventManager 2.0.0](#update-eventmanager-200)</br>[Update: EventManager 1.2.0](#update-eventmanager-120)</br>[Update: EventManager 1.1.0](#update-eventmanager-110)</br>[Update: EventManager 1.0.0 - Error checking](#update-eventmanager-100---error-checking)</br>[Version: EventManager 0.0.1 - In The Beginning things were very different](#version-eventmanager-001---in-the-beginning-things-were-very-different) [Update 15.0.0 - The art of faking it](#update-1500---the-art-of-faking-it)</br>[Update 14.2.0 - Bloatware Removed](#update-1420---bloatware-removed)</br>[Update 14.1.0 - A whole new world of possibilities](#update-1410---a-whole-new-world-of-possibilities)</br>[Update 14.0.0 - Consistency, Additions and Stability](#update-1400---consistency-additions-and-stability)</br>[Update 13.1.0 - Bug fixes and features added](#update-1310---bug-fixes-and-features-added)</br>[Update 13.0.0 - Added some documentation, and some new features too check it out!](#update-1300---added-some-documentation-and-some-new-features-too-check-it-out)</br>[Update 12.2.2 - Time for some more bug fixes!](#update-1222---time-for-some-more-bug-fixes)</br>[Update 12.2.1 - Time for some bug fixes!](#update-1221---time-for-some-bug-fixes)</br>[Update 12.2.0 - The chains of binding](#update-1220---the-chains-of-binding)</br>[Update 12.1.0 - Threads just can't hold on anymore](#update-1210---threads-just-cant-hold-on-anymore)</br>[Update: 12.0.0 - Big update (Lots of additions some changes)](#update-1200---big-update-lots-of-additions-some-changes)</br>[Update: 1.11.1 - Small Clarification on Love](#update-1111---small-clarification-on-love)</br>[Update: 1.11.0](#update-1110)</br>[Update: 1.10.0](#update-1100)</br>[Update: 1.9.2](#update-192)</br>[Update: 1.9.1 - Threads can now argue](#update-191---threads-can-now-argue)</br>[Update: 1.9.0](#update-190)</br>[Update: 1.8.7](#update-187)</br>[Update: 1.8.6](#update-186)</br>[Update: 1.8.5](#update-185)</br>[Update: 1.8.4](#update-184)</br>[Update: 1.8.3 - Mainloop recieves some needed overhauling](#update-183---mainloop-recieves-some-needed-overhauling)</br>[Update: 1.8.2](#update-182)</br>[Update: 1.8.1](#update-181)</br>[Update: 1.7.6](#update-176)</br>[Update: 1.7.5](#update-175)</br>[Update: 1.7.4](#update-174)</br>[Update: 1.7.3](#update-173)</br>[Update: 1.7.2](#update-172)</br>[Update: 1.7.1 - Bug Fixes Only](#update-171---bug-fixes-only)</br>[Update: 1.7.0 - Threading the systems](#update-170---threading-the-systems)</br>[Update: 1.6.0](#update-160)</br>[Update: 1.5.0](#update-150)</br>[Update: 1.4.1 (4/10/2017) - First Public release of the library](#update-141-4102017---first-public-release-of-the-library)</br>[Update: 1.4.0 (3/20/2017)](#update-140-3202017)</br>[Update: 1.3.0 (1/29/2017)](#update-130-1292017)</br>[Update: 1.2.0 (12.31.2016)](#update-120-12312016)</br>[Update: 1.1.0](#update-110)</br>[Update: 1.0.0](#update-100)</br>[Update: 0.6.3](#update-063)</br>[Update: 0.6.2](#update-062)</br>[Update: 0.6.1-6](#update-061-6)</br>[Update: 0.5.1-6](#update-051-6)</br>[Update: 0.4.1](#update-041)</br>[Update: 0.3.0 - The update that started it all](#update-030---the-update-that-started-it-all)</br>[Update: EventManager 2.0.0](#update-eventmanager-200)</br>[Update: EventManager 1.2.0](#update-eventmanager-120)</br>[Update: EventManager 1.1.0](#update-eventmanager-110)</br>[Update: EventManager 1.0.0 - Error checking](#update-eventmanager-100---error-checking)</br>[Version: EventManager 0.0.1 - In The Beginning things were very different](#version-eventmanager-001---in-the-beginning-things-were-very-different)
# Update 15.0.0 - The art of faking it # Update 15.0.0 - The art of faking it
Full Update Showcase Full Update Showcase
@ -12,35 +11,67 @@ package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path
multi,thread = require("multi"):init() multi,thread = require("multi"):init()
GLOBAL,THREAD = require("multi.integration.threading"):init() -- Auto detects your enviroment and uses what's available GLOBAL,THREAD = require("multi.integration.threading"):init() -- Auto detects your enviroment and uses what's available
jq = multi:newSystemThreadedJobQueue(100) -- Job queue with 4 worker threads jq = multi:newSystemThreadedJobQueue(4) -- Job queue with 4 worker threads
func = jq:newFunction("test",function(a,b) func = jq:newFunction("test",function(a,b)
THREAD.sleep(2) THREAD.sleep(2)
return a+b return a+b
end) end)
for i = 1,100 do for i = 1,10 do
func(i,i*3).connect(function(data) func(i,i*3).connect(function(data)
print(data) print(data)
end) end)
end end
multi:lightloop() multi:newThread("Standard Thread 1",function()
``` while true do
thread.sleep(1)
print("Testing 1 ...")
end
end)
multi:newISOThread("ISO Thread 2",{test=true},function()
while true do
thread.sleep(1)
print("Testing 2 ...")
end
end)
multi:mainloop()
```
Note:
---
This was supposed to be released over a year ago, but work and other things got in my way. Pesudo Threading now works. The goal of this is so you can write modules that can be scaled up to utilize threading features when available.
Added: Added:
--- ---
- multi:newISOThread(name,func,env)
- Creates an isolated thread that prevents both locals and globals from being accessed.
- Was designed for the pesudoManager so it can emulate threads. You can use it as a super sandbox, but remember upvalues are also stripped which was intened for what I wanted them to do!
- Added new integration: pesudoManager, functions just like lanesManager and loveManager, but it's actually single threaded - Added new integration: pesudoManager, functions just like lanesManager and loveManager, but it's actually single threaded
- This was implemented because, you may want to build your code around being multi threaded, but some systems/implemetations of lua may not permit this. Since we now have a "single threaded" implementation of multi threading. We can actually create scalable code where things automatcally are threaded if built correctly. I am planning on adding more threadedOjbects. - This was implemented because, you may want to build your code around being multi threaded, but some systems/implemetations of lua may not permit this. Since we now have a "single threaded" implementation of multi threading. We can actually create scalable code where things automatcally are threaded if built correctly. I am planning on adding more threadedOjbects.
- In addition to adding pesudo Threading `multi.integration.threading` can now be used to autodetect which enviroment you are on and use the threading features. - In addition to adding pesudo Threading `multi.integration.threading` can now be used to autodetect which enviroment you are on and use the threading features.
``` ```
GLOBAL,THREAD = require("multi.integration.threading"):init() GLOBAL,THREAD = require("multi.integration.threading"):init()
``` ```
If you are using love2d it will use that, if you have lanes avaialble then it will use lanes. Otherwise it will use pesudo threading. This allows module creators to implement scalable features without having to worry about which enviroment they are in. And it's my job to ensure everything works properly within reason. If you are using love2d it will use that, if you have lanes avaialble then it will use lanes. Otherwise it will use pesudo threading. This allows module creators to implement scalable features without having to worry about which enviroment they are in. Can now require a consistant module: `require("multi.integration.threading"):init()`
Changed:
---
- Documentation to reflect the changes made
Removed:
---
- CBT (Coroutine Based threading) has lost a feature, one that hasn't been used much, but broke compatiblity with anything above lua 5.1. My goal is to make my library work with all versions of lua above 5.1, including 5.4. Lua 5.2+ changed how enviroments worked which means that you can no longer modify an enviroment of function without using the debug library. This isn't ideal for how things in my library worked, but it is what it is. The feature lost is the one that converted all functions within a threaded enviroment into a threadedfunction. This in hindsight wasn't the best pratice and if it is the desired state you as the user can manually do that anyway. This shouldn't affect anyones code in a massive way.
Fixed: Fixed:
--- ---
- pseudoThreading and threads had an issue where they weren't executing properly
- lanesManager THREAD:get(STRING: name) not returning the value - lanesManager THREAD:get(STRING: name) not returning the value
Todo:
---
- Add more details to the documentation
# Update 14.2.0 - Bloatware Removed # Update 14.2.0 - Bloatware Removed
Full Update Showcase Full Update Showcase
--- ---
@ -101,7 +132,7 @@ Quality Of Life:
Added: Added:
--- ---
- Type: destroyed - Type: destroyed
- A special state of an object that causes that object to become immutable and callable. The object Type is always "destroyed" it cannot be changed. The object can be indexed to infinity without issue. Every part of the object can be called as if it were a function including the indexed parts. This is done incase you destroy an object and still use it somewhere. However, if you are expecting something from the object then you may still encounter an error, though the returned type is an instance of the destroyed object which can be indexed and called like normal. This object can be used in any way and no errors will come about with it. - A special state of an object that causes that object to become immutable and callable. The object Type is always "destroyed" it cannot be changed. The object can be indexed to infinity without issue. Every part of the object can be called as if it were a function including the indexed parts. This is done incase you destroy an object and still "use" it somewhere. However, if you are expecting something from the object then you may still encounter an error, though the returned type is an instance of the destroyed object which can be indexed and called like normal. This object can be used in any way and no errors will come about with it.
Fixed: Fixed:
--- ---

View File

@ -163,6 +163,20 @@ function multi:getTasksDetails(t)
end end
--Helpers --Helpers
-- Used with ISO Threads
local function isolateFunction(func,env)
local dmp = string.dump(func)
local env = env or {}
if setfenv then
local f = loadstring(dmp,"IsolatedThread_PesudoThreading")
setfenv(f,env)
return f
else
return load(dmp,"IsolatedThread_PesudoThreading","bt",env)
end
end
function multi:Break() function multi:Break()
self:Pause() self:Pause()
self.Active=nil self.Active=nil
@ -963,8 +977,13 @@ function thread.yield()
return thread.sleep(0) return thread.sleep(0)
end end
function thread.isThread() function thread.isThread()
if _VERSION~="Lua 5.1" then
local a,b = coroutine.running()
return not(b)
else
return coroutine.running()~=nil return coroutine.running()~=nil
end end
end
function thread.getCores() function thread.getCores()
return thread.__CORES return thread.__CORES
end end
@ -1072,25 +1091,21 @@ function thread:newFunction(func,holdme)
return temp return temp
end end
end end
-- A cross version way to set enviroments, not the same as fenv though
function multi.setEnv(func,env)
local f = string.dump(func)
local chunk = load(f,"env","bt",env)
return chunk
end
function multi:newThread(name,func,...) function multi:newThread(name,func,...)
multi.OnLoad:Fire() multi.OnLoad:Fire()
local func = func or name local func = func or name
if type(name) == "function" then if type(name) == "function" then
name = "Thread#"..threadCount name = "Thread#"..threadCount
end end
local env = {}
setmetatable(env,{
__index = Gref,
__newindex = function(t,k,v)
if type(v)=="function" then
rawset(t,k,thread:newFunction(v))
else
Gref[k]=v
end
end
})
setfenv(func,env)
local c={} local c={}
local env = {self=c}
c.TempRets = {nil,nil,nil,nil,nil,nil,nil,nil,nil,nil} c.TempRets = {nil,nil,nil,nil,nil,nil,nil,nil,nil,nil}
c.startArgs = {...} c.startArgs = {...}
c.ref={} c.ref={}
@ -1103,6 +1118,7 @@ function multi:newThread(name,func,...)
c.timer=multi:newTimer() c.timer=multi:newTimer()
c._isPaused = false c._isPaused = false
c.returns = {} c.returns = {}
c.isError = false
c.OnError = multi:newConnection(true,nil,true) c.OnError = multi:newConnection(true,nil,true)
c.OnDeath = multi:newConnection(true,nil,true) c.OnDeath = multi:newConnection(true,nil,true)
function c:isPaused() function c:isPaused()
@ -1166,6 +1182,22 @@ function multi:newThread(name,func,...)
self:create(c) self:create(c)
return c return c
end end
function multi:newISOThread(name,func,_env,...)
multi.OnLoad:Fire()
local func = func or name
local env = _env or {}
if not env.thread then
env.thread = thread
end
if not env.multi then
env.multi = multi
end
if type(name) == "function" then
name = "Thread#"..threadCount
end
local func = isolateFunction(func,env)
return self:newThread(name,func)
end
function multi.initThreads(justThreads) function multi.initThreads(justThreads)
initT = true initT = true
multi.scheduler=multi:newLoop():setName("multi.thread") multi.scheduler=multi:newLoop():setName("multi.thread")
@ -1255,10 +1287,12 @@ function multi.initThreads(justThreads)
multi.scheduler:OnLoop(function(self) multi.scheduler:OnLoop(function(self)
for i=#threads,1,-1 do for i=#threads,1,-1 do
if threads[i].isError then if threads[i].isError then
if coroutine.status(threads[i].thread)=="dead" then
threads[i].OnError:Fire(threads[i],unpack(threads[i].TempRets)) threads[i].OnError:Fire(threads[i],unpack(threads[i].TempRets))
multi.setType(threads[i],multi.DestroyedObj) multi.setType(threads[i],multi.DestroyedObj)
table.remove(threads,i) table.remove(threads,i)
end end
end
if threads[i] and not threads[i].__started then if threads[i] and not threads[i].__started then
if coroutine.running() ~= threads[i].thread then if coroutine.running() ~= threads[i].thread then
_,ret,r1,r2,r3,r4,r5,r6=coroutine.resume(threads[i].thread,unpack(threads[i].startArgs)) _,ret,r1,r2,r3,r4,r5,r6=coroutine.resume(threads[i].thread,unpack(threads[i].startArgs))

View File

@ -23,6 +23,16 @@ SOFTWARE.
]] ]]
local multi, thread = require("multi"):init() local multi, thread = require("multi"):init()
local GLOBAL, THREAD = multi.integration.GLOBAL,multi.integration.THREAD local GLOBAL, THREAD = multi.integration.GLOBAL,multi.integration.THREAD
local function stripUpValues(func)
local dmp = string.dump(func)
if setfenv then
return loadstring(dmp,"IsolatedThread_PesudoThreading")
else
return load(dmp,"IsolatedThread_PesudoThreading","bt")
end
end
function multi:newSystemThreadedQueue(name) function multi:newSystemThreadedQueue(name)
local c = {} local c = {}
function c:push(v) function c:push(v)
@ -85,6 +95,7 @@ function multi:newSystemThreadedJobQueue(n)
end end
local nFunc = 0 local nFunc = 0
function c:newFunction(name,func,holup) -- This registers with the queue function c:newFunction(name,func,holup) -- This registers with the queue
local func = stripUpValues(func)
if type(name)=="function" then if type(name)=="function" then
holup = func holup = func
func = name func = name

View File

@ -41,9 +41,42 @@ end
function multi:getPlatform() function multi:getPlatform()
return "pesudo" return "pesudo"
end end
local function split(str)
local tab = {}
for word in string.gmatch(str, '([^,]+)') do
table.insert(tab,word)
end
return tab
end
THREAD.newFunction=thread.newFunction THREAD.newFunction=thread.newFunction
multi.newSystemThread = multi.newThread local id = 0
function multi:newSystemThread(name,func,...)
GLOBAL["$THREAD_NAME"] = name
GLOBAL["$__THREADNAME__"] = name
GLOBAL["$THREAD_ID"] = id
--GLOBAL["$thread"] = thread
local env = {
GLOBAL = GLOBAL,
THREAD = THREAD,
THREAD_NAME = name,
__THREADNAME__ = name,
THREAD_ID = id,
thread = thread
}
local tab = [[_VERSION,io,os,require,load,debug,assert,collectgarbage,error,getfenv,getmetatable,ipairs,loadstring,module,next,pairs,pcall,print,rawequal,rawget,rawset,select,setfenv,setmetatable,tonumber,tostring,type,unpack,xpcall,math,coroutine,string,table]]
tab = split(tab)
for i = 1,#tab do
env[tab[i]] = _G[tab[i]]
end
--setmetatable(env,{__index=env})
multi:newISOThread(name,func,env,...).OnError(function(self,msg)
print("ERROR:",msg)
end)
id = id + 1
end
-- System threads as implemented here cannot share memory, but use a message passing system.
-- An isolated thread allows us to mimic that behavior so if access data from the "main" thread happens things will not work. This behavior is in line with how the system threading works
print("Integrated Pesudo Threading!") print("Integrated Pesudo Threading!")
multi.integration = {} -- for module creators multi.integration = {} -- for module creators

View File

@ -28,7 +28,7 @@ local function getOS()
return "unix" return "unix"
end end
end end
local function INIT() local function INIT(env)
local THREAD = {} local THREAD = {}
local GLOBAL = {} local GLOBAL = {}
THREAD.Priority_Core = 3 THREAD.Priority_Core = 3
@ -45,6 +45,7 @@ local function INIT()
return GLOBAL[name] return GLOBAL[name]
end end
function THREAD.waitFor(name) function THREAD.waitFor(name)
print("Waiting",thread)
return thread.hold(function() return GLOBAL[name] end) return thread.hold(function() return GLOBAL[name] end)
end end
function THREAD.getCores() function THREAD.getCores()
@ -54,7 +55,7 @@ local function INIT()
local c = {} local c = {}
c.print = print c.print = print
function c.error(err) function c.error(err)
error("ERROR in <"..__THREADNAME__..">: "..err) error("ERROR in <"..GLOBAL["$__THREADNAME__"]..">: "..err)
end end
return c return c
end end
@ -63,17 +64,17 @@ local function INIT()
end end
THREAD.__CORES = thread.__CORES THREAD.__CORES = thread.__CORES
function THREAD.getName() function THREAD.getName()
local t = thread.getRunningThread() return GLOBAL["$THREAD_NAME"]
return t.Name
end end
function THREAD.getID() function THREAD.getID()
local t = thread.getRunningThread() return GLOBAL["$THREAD_ID"]
return t.TID end
function THREAD.sleep(n)
thread.sleep(n)
end
function THREAD.hold(n)
return thread.hold(n)
end end
_G.THREAD_ID = 0
THREAD.kill = thread.kill
THREAD.sleep = thread.sleep
THREAD.hold = thread.hold
return GLOBAL, THREAD return GLOBAL, THREAD
end end
return {init = function() return {init = function()

View File

@ -1,2 +0,0 @@
-- Planned system threaded objects
-- multi:newSystemThreadedConnection(name, protect)

View File

@ -0,0 +1,38 @@
package = "multi"
version = "15.0-0"
source = {
url = "git://github.com/rayaman/multi.git",
tag = "v15.0.0",
}
description = {
summary = "Lua Multi tasking library",
detailed = [[
This library contains many methods for multi tasking. Features non coroutine based multi-tasking, coroutine based multi-tasking, and system threading (Requires use of an integration).
Check github for how to use.
]],
homepage = "https://github.com/rayaman/multi",
license = "MIT"
}
dependencies = {
"lua >= 5.1",
"lanes",
}
build = {
type = "builtin",
modules = {
["multi"] = "multi/init.lua",
["multi.compat.love2d"] = "multi/compat/love2d.lua",
["multi.integration"] = "multi/integration/threading.lua",
["multi.integration.lanesManager"] = "multi/integration/lanesManager/init.lua",
["multi.integration.lanesManager.extensions"] = "multi/integration/lanesManager/extensions.lua",
["multi.integration.lanesManager.threads"] = "multi/integration/lanesManager/threads.lua",
["multi.integration.loveManager"] = "multi/integration/loveManager/init.lua",
["multi.integration.loveManager.extensions"] = "multi/integration/loveManager/extensions.lua",
["multi.integration.loveManager.threads"] = "multi/integration/loveManager/threads.lua",
["multi.integration.pesudoManager"] = "multi/integration/pesudoManager/init.lua",
["multi.integration.pesudoManager.extensions"] = "multi/integration/pesudoManager/extensions.lua",
["multi.integration.pesudoManager.threads"] = "multi/integration/pesudoManager/threads.lua",
["multi.integration.luvitManager"] = "multi/integration/luvitManager.lua",
--["multi.integration.networkManager"] = "multi/integration/networkManager.lua",
}
}

View File

@ -1,7 +1,40 @@
package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path
multi,thread = require("multi"):init() multi,thread = require("multi"):init()
GLOBAL,THREAD = require("multi.integration.pesudoManager"):init() GLOBAL,THREAD = require("multi.integration.threading"):init() -- Auto detects your enviroment and uses what's available
jq = multi:newSystemThreadedJobQueue(5) -- Job queue with 4 worker threads
func = jq:newFunction("test",function(a,b)
THREAD.sleep(2)
return a+b
end)
for i = 1,10 do
func(i,i*3).connect(function(data)
print(data)
end)
end
multi:mainloop({print=true}) local a = true
b = false
multi:newThread("Standard Thread 1",function()
while true do
thread.sleep(1)
print("Testing 1 ...",a,b,test)
end
end).OnError(function(self,msg)
print(msg)
end)
-- All upvalues are stripped! no access to the global, multi and thread are exposed however
multi:newISOThread("ISO Thread 2",function()
while true do
thread.sleep(1)
print("Testing 2 ...",a,b,test) -- a and b are nil, but test is true
end
end,{test=true,print=print})
.OnError(function(self,msg)
print(msg)
end)
multi:mainloop()

17
tests/runtests.lua Normal file
View File

@ -0,0 +1,17 @@
package.path="../?.lua;../?/init.lua;../?.lua;../?/?/init.lua;"..package.path
--[[
This file runs all tests.
Format:
Expected:
...
...
...
Actual:
...
...
...
Each test that is ran should have a 5 second pause after the test is complete
The expected and actual should "match" (Might be impossible when playing with threads)
This will be pushed directly to the master as tests start existing.
]]