Merge pull request #33 from rayaman/v15.2.0

V15.2.0
This commit is contained in:
Ryan Ward 2022-04-19 18:45:52 -04:00 committed by GitHub
commit 47d8ac30c4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 2133 additions and 2570 deletions

5
.gitignore vendored
View File

@ -3,8 +3,5 @@
*lua5.3
*lua5.4
*luajit
test2.lua
test.lua
test3.lua
*.code-workspace
*.dat
*.dat

View File

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

View File

@ -1,8 +1,9 @@
# Multi Version: 15.1.0 Hold the thread
# Multi Version: 15.2.0 Upgrade Complete
**Key Changes**
- thread.hold has been updated to allow all variants to work as well as some new features. Check the changelog or documentation for more info.
- multi:newProccesor() Creates a process that acts like the multi namespace that can be managed independently from the mainloop.
- Connections can be added together
- All objects now use connections internally
- Connections now about 23x faster!
- Updated getTasksDetails() to handle the new method of managing threads and processors
- Made lanes optional, install separately if needed
Found an issue? Please [submit it](https://github.com/rayaman/multi/issues) and someone will look into it!
@ -10,57 +11,65 @@ My multitasking library for lua. It is a pure lua binding, with exceptions of th
</br>
Progress is being made in [v15.2.0](https://github.com/rayaman/multi/tree/v15.2.0)
Progress is being made in [v15.3.0](https://github.com/rayaman/multi/tree/v15.3.0)
---
</br>
INSTALLING
----------
Links to dependencies:
Link to optional dependencies:
[lanes](https://github.com/LuaLanes/lanes)
[love2d](https://love2d.org/)
To install copy the multi folder into your environment and you are good to go</br>
If you want to use the system threads, then you'll need to install lanes!
If you want to use the system threads, then you'll need to install lanes or love2d game engine!
**or** use luarocks `luarocks install multi`
Going forward I will include a Release zip for love2d.
**The Network Manager rework is currently being worked on and the old version is not included in this version.**
Discord
-------
Have a question? Or need realtime assistance? Feel free to join the discord!</br>
https://discord.gg/U8UspuA</br>
Have a question or need realtime assistance? Feel free to join the discord!</br>
https://discord.gg/U8UspuA
Planned features/TODO
---------------------
- [x] ~~Finish Documentation~~ Finished
- [ ] Create test suite
- [ ] Create test suite (In progress, mostly done)
- [ ] Network Parallelism rework
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)
-----
```lua
package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path
local multi, thread = require("multi"):init()
GLOBAL, THREAD = require("multi.integration.threading"):init()
multi:newSystemThread("System Thread",function()
while true do
THREAD.sleep(1)
print("World!")
end
while true do
THREAD.sleep(.1)
io.write(" World")
THREAD.kill()
end
end)
multi:newThread("Coroutine Based Thread",function()
while true do
print("Hello")
thread.sleep(1)
end
while true do
io.write("Hello")
thread.sleep(.1)
thread.kill()
end
end)
multi:newTLoop(function(loop)
print("!")
loop:Destroy()
os.exit()
end,.3)
multi:mainloop()
--[[
while true do
multi:uManager()
multi:uManager()
end
]]
```

View File

@ -1,15 +1,323 @@
# Changelog
Table of contents
---
[Update 15.1.0 - Hold the thread!](#update-1510---hold-the-thread)</br>[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.2.0 - Upgrade Complete](#update-1520---upgrade-complete)</br>[Update 15.1.0 - Hold the thread!](#update-1510---hold-the-thread)</br>[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.2.0 - Upgrade Complete
Full Update Showcase
```lua
package.path = "./?/init.lua;"..package.path
multi, thread = require("multi"):init{print=true}
GLOBAL, THREAD = require("multi.integration.threading"):init()
-- Using a system thread, but both system and local threads support this!
-- Don't worry if you don't have lanes or love2d. PesudoThreading will kick in to emulate the threading features if you do not have access to system threading.
func = THREAD:newFunction(function(count)
print("Starting Status test: ",count)
local a = 0
while true do
a = a + 1
THREAD.sleep(.1)
-- Push the status from the currently running threaded function to the main thread
THREAD.pushStatus(a,count)
if a == count then break end
end
return "Done"
end)
thread:newThread("test",function()
local ret = func(10)
ret.OnStatus(function(part,whole)
print("Ret1: ",math.ceil((part/whole)*1000)/10 .."%")
end)
print("TEST",func(5).wait())
-- The results from the OnReturn connection is passed by thread.hold
print("Status:",thread.hold(ret.OnReturn))
print("Function Done!")
end).OnError(function(...)
print("Error:",...)
end)
local ret = func(10)
local ret2 = func(15)
local ret3 = func(20)
local s1,s2,s3 = 0,0,0
ret.OnError(function(...)
print("Error:",...)
end)
ret2.OnError(function(...)
print("Error:",...)
end)
ret3.OnError(function(...)
print("Error:",...)
end)
ret.OnStatus(function(part,whole)
s1 = math.ceil((part/whole)*1000)/10
print(s1)
end)
ret2.OnStatus(function(part,whole)
s2 = math.ceil((part/whole)*1000)/10
print(s2)
end)
ret3.OnStatus(function(part,whole)
s3 = math.ceil((part/whole)*1000)/10
print(s3)
end)
loop = multi:newTLoop()
function loop:testing()
print("testing haha")
end
loop:Set(1)
t = loop:OnLoop(function()
print("Looping...")
end):testing()
local proc = multi:newProcessor("Test")
local proc2 = multi:newProcessor("Test2")
local proc3 = proc2:newProcessor("Test3")
proc.Start()
proc2.Start()
proc3.Start()
proc:newThread("TestThread_1",function()
while true do
thread.sleep(1)
end
end)
proc:newThread("TestThread_2",function()
while true do
thread.sleep(1)
end
end)
proc2:newThread("TestThread_3",function()
while true do
thread.sleep(1)
end
end)
thread:newThread(function()
thread.sleep(1)
local tasks = multi:getStats()
for i,v in pairs(tasks) do
print("Process: " ..i.. "\n\tTasks:")
for ii,vv in pairs(v.tasks) do
print("\t\t"..vv:getName())
end
print("\tThreads:")
for ii,vv in pairs(v.threads) do
print("\t\t"..vv:getName())
end
end
thread.sleep(10) -- Wait 10 seconds then kill the process!
os.exit()
end)
multi:mainloop()
```
Added:
---
- `multi:getStats()`
- Returns a structured table where you can access data on processors there tasks and threads:
```lua
-- Upon calling multi:getStats() the table below is returned
get_Stats_Table {
proc_1 -- table
proc_2 -- table
...
proc_n -- table
}
proc_Table {
tasks = {alarms,steps,loops,etc} -- All multi objects
threads = {thread_1,thread_2,thread_3,etc} -- Thread objects
}
-- Refer to the objects documentation to see how you can interact with them
```
- Reference the Full update showcase for the method in action
- `multi:newProcessor(name, nothread)`
- If no thread is true auto sets the processor as Active, so proc.run() will start without the need for proc.Start()
- `multi:getProcessors()`
- Returns a list of all processors
- `multi:getName()`
- Returns the name of a processor
- `multi:getFullName()`
- Returns the fullname/entire process tree of a process
- Processors can be attached to processors
- `multi:getTasks()`
- Returns a list of all non thread based objects (loops, alarms, steps, etc)
- `multi:getThreads()`
- Returns a list of all threads on a process
- `multi:newProcessor(name, nothread).run()`
- New function run to the processor object to
- `multi:newProcessor(name, nothread):newFunction(func, holdme)`
- Acts like thread:newFunction(), but binds the execution of that threaded function to the processor
- `multi:newTLoop()` member functions
- `TLoop:Set(set)` - Sets the time to wait for the TLoop
- `multi:newStep()` member functions
- `Step:Count(count)` - Sets the amount a step should count by
- `multi:newTStep()` member functions
- `TStep:Set(set)` - Sets the time to wait for the TStep
Changed:
---
- `thread.hold(connectionObj)` now passes the returns of that connection to `thread.hold()`! See Exampe below:
```lua
multi, thread = require("multi"):init()
func = thread:newFunction(function(count)
local a = 0
while true do
a = a + 1
thread.sleep(.1)
thread.pushStatus(a,count)
if a == count then break end
end
return "Done", 1, 2, 3
end)
thread:newThread("test",function()
local ret = func(10)
ret.OnStatus(function(part,whole)
print("Ret1: ",math.ceil((part/whole)*1000)/10 .."%")
end)
print("Status:",thread.hold(ret.OnReturn))
print("Function Done!")
os.exit()
end).OnError(function(...)
print("Error:",...)
end)
multi:mainloop()
```
Output:
```
Ret1: 10%
Ret1: 20%
Ret1: 30%
Ret1: 40%
Ret1: 50%
Ret1: 60%
Ret1: 70%
Ret1: 80%
Ret1: 90%
Ret1: 100%
Status: Done 1 2 3 nil nil nil nil nil nil nil nil nil nil nil nil
Function Done!
```
- Modified how threads are handled internally. This changes makes it so threads "regardless of amount" should not impact performance. What you do in the threads might. This change was made by internally only processing one thread per step per processor. If you have 10 processors that are all active expect one step to process 10 threads. However if one processor has 10 threads each step will only process one thread. Simply put each addition of a thread shouldn't impact performance as it did before.
- Moved `multi:newThread(...)` into the thread interface (`thread:newThread(...)`), code using `multi:newThread(...)` will still work. Also using `process:newThread(...)` binds the thread to the process, meaning if the process the thread is bound to is paused so is the thread.
- multi:mainloop(~~settings~~)/multi:uManager(~~settings~~) no longer takes a settings argument, that has been moved to multi:init(settings)
| Setting | Description |
---|---
print | When set to true parts of the library will print out updates otherwise no internal printing will be done
priority | When set to true, the library will prioritize different objects based on their priority
- `multi:newProcessor(name,nothread)` The new argument allows you to tell the system you won't be using the Start() and Stop() functions, rather you will handle the process yourself. Using the proc.run() function. This function needs to be called to pump the events.
- Processors now also use lManager instead of uManager.
- `multi.hold(n,opt)` now supports an option table like thread.hold does.
- Connection Objects now pass on the parent object if created on a multiobj. This was to allow chaining to work properly with the new update
```lua
multi,thread = require("multi"):init()
loop = multi:newTLoop()
function loop:testing()
print("testing haha")
end
loop:Set(1)
t = loop:OnLoop(function()
print("Looping...")
end):testing()
multi:mainloop()
--[[Returns as expected:
testing haha
Looping...
Looping...
Looping...
...
Looping...
Looping...
Looping...
]]
```
While chaining on the OnSomeEventMethod() wasn't really a used feature, I still wanted to keep it just incase someone was relying on this working. And it does have it uses
- All Multi Objects now use Connection objects
`multiobj:OnSomeEvent(func)` or `multiobj.OnSomeEvent(func)`
- Connection Objects no longer Fire with syntax sugar when attached to an object:
`multiobj:OnSomeEvent(...)` No longer triggers the Fire event. As part of the update to make all objects use connections internally this little used feature had to be scrapped!
- multi:newTStep now derives it's functionality from multi:newStep (Cut's down on code length a bit)
Removed:
---
- `multi:getTasksDetails()` Remade completely and now called `multi:getStats()`
- `multi:getError()` Removed when setting protect was removed
- `multi:FreeMainEvent()` The new changes with connections make's this function unnecessary
- `multi:OnMainConnect(func)` See above
- `multi:connectFinal(func)` See above
- `multi:lightloop()` Cleaned up the mainloop/uManager method, actually faster than lightloop (Which should have been called liteloop)
- `multi:threadloop()` See above for reasons
- `multi setting: protect` This added extra complexity to the mainloop and not much benefit. If you feel a function will error use pcall yourself. This saves a decent amount of cycles, about 6.25% increase in performance.
- `multi:GetParentProcess()` use `multi.getCurrentProcess()` instead
- priority scheme 2, 3 and auto-priority have been removed! Only priority scheme 1 actually performed in a reasonable fashion so that one remained.
- `multi:newFunction(func)`
- `thread:newFunction(func)` Has many more features and replaces what multi:newFunction did
- `multi.holdFor()` Now that multi.hold takes the option table that thread.hold has this feature can be emulated using that.
- Calling Fire on a connection no longer returns anything! Now that internal features use connections, I noticed how slow connections are and have increased their speed quite a bit. From 50,000 Steps per seconds to almost 7 Million. All other features should work just fine. Only returning values has been removed
Fixed:
---
- [Issue](https://github.com/rayaman/multi/issues/30) with Lanes crashing the lua state. Issue seemed to be related to my filesystem, since remounting the drive caused the issue to stop. (Windows)
- [Issue](https://github.com/rayaman/multi/issues/29) where System threaded functions not being up to date with threaded functions
- Issue where gettasksdetails() would try to process a destroyed object causing it to crash
- Issue with multi.hold() not pumping the mainloop and only the scheduler
ToDo:
---
- Work on network parallelism
# Update 15.1.0 - Hold the thread!
Full Update Showcase
```lua
package.path = "./?/init.lua;"..package.path
multi,thread = require("multi"):init()
local multi,thread = require("multi"):init()
func = thread:newFunction(function(count)
local a = 0
@ -98,17 +406,17 @@ multi:mainloop()
Added:
---
## multi:newSystemThreadedJobQueue(n) isEmpty()
- returns true if the queue is empty, false if there are items in the queue.
- multi:newSystemThreadedJobQueue(n)
`queue:isEmpty()`
Returns true if the queue is empty, false if there are items in the queue.
**Note:** a queue might be empty, but the job may still be running and not finished yet! Also if a registered function is called directly instead of pushed, it will not reflect inside the queue until the next cycle!
Example:
```lua
package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path
package.cpath = [[C:\Program Files (x86)\Lua\5.1\systree\lib\lua\5.1\?.dll;C:\Program Files (x86)\Lua\5.1\systree\lib\lua\5.1\?\core.dll;]] ..package.cpath
multi,thread = require("multi"):init()
local 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
@ -153,8 +461,7 @@ multi:mainloop()
## multi:newProcessor(name)
```lua
package.path = "./?/init.lua;"..package.path
multi,thread = require("multi"):init()
local multi,thread = require("multi"):init()
-- Create a processor object, it works a lot like the multi object
sandbox = multi:newProcessor()
@ -227,8 +534,7 @@ Can be chained as long as you want! See example below
Example:
```lua
package.path = "./?/init.lua;"..package.path
multi,thread = require("multi"):init()
local multi,thread = require("multi"):init()
func = thread:newFunction(function(count)
local a = 0
@ -283,8 +589,7 @@ Changed:
holdMe(set) | Sets the holdme argument that existed at function creation
```lua
package.path = "./?/init.lua;"..package.path
multi, thread = require("multi"):init()
local multi, thread = require("multi"):init()
test = thread:newFunction(function(a,b)
thread.sleep(1)
@ -378,8 +683,7 @@ ToDo
Full Update Showcase
---
```lua
package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path
multi,thread = require("multi"):init()
local multi,thread = require("multi"):init()
GLOBAL,THREAD = require("multi.integration.threading"):init() -- Auto detects your enviroment and uses what's available
jq = multi:newSystemThreadedJobQueue(4) -- Job queue with 4 worker threads
@ -413,6 +717,7 @@ 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:
---
- multi:newISOThread(name,func,env)
@ -448,7 +753,6 @@ Todo:
Full Update Showcase
---
```lua
package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path
local multi,thread = require("multi"):init()
-- Testing destroying and fixed connections
@ -515,7 +819,6 @@ Fixed:
- Issue with connections not returning a handle for managing a specific conn object.
- Issue with connections where connection chaining wasn't working properly. This has been addressed.
```lua
package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path
local multi,thread = require("multi"):init()
test = multi:newConnection()
test(function(hmm)
@ -568,7 +871,6 @@ Full Update Showcase
---
Something I plan on doing each version going forward
```lua
package.path="?.lua;?/init.lua;?.lua;"..package.path
local multi, thread = require("multi"):init()
GLOBAL,THREAD = require("multi.integration.lanesManager"):init()
serv = multi:newService(function(self,data)
@ -790,19 +1092,21 @@ Added:
-- If the created function encounters an error, it will return nil, the error message!
- special variable multi.NIL was added to allow error handling in threaded functions.
-- multi.NIL can be used in to force a nil value when using thread.hold()
- All functions created in the root of a thread are now converted to threaded functions, which allow for wait and connect features. **Note:** these functions are local to the function! And are only converted if they aren't set as local! Otherwise the function
- All functions created in the root of a thread are now converted to threaded functions, which allow for wait and connect features.
**Note:** these functions are local to the function! And are only converted if they aren't set as local! Otherwise the function is converted into a threaded function
- lanes threads can now have their priority set using: sThread.priority =
-- thread.Priority_Core
-- thread.Priority_High
-- thread.Priority_Above_Normal
-- thread.Priority_Normal
-- thread.Priority_Below_Normal
-- thread.Priority_Low
-- thread.Priority_Idle
- thread.Priority_Core
- thread.Priority_High
- thread.Priority_Above_Normal
- thread.Priority_Normal
- thread.Priority_Below_Normal
- thread.Priority_Low
- thread.Priority_Idle
- thread.hold() and multi.hold() now accept connections as an argument. See example below
```lua
package.path = "./?/init.lua;"..package.path
local multi, thread = require("multi"):init()
conn = multi:newConnection()
multi:newThread(function()
@ -833,7 +1137,6 @@ end)
thread newFunction using auto convert
```lua
package.path = "./?/init.lua;" .. package.path
multi, thread = require("multi").init()
a=5
multi:newThread("Test",function()
@ -871,7 +1174,7 @@ Changed:
---
- Connections connect function can now chain connections
```lua
package.path = "./?/init.lua;"..package.path
local multi, thread = require("multi").init()
test = multi:newConnection()
test(function(a)
@ -1069,7 +1372,6 @@ Added:
- STC: FireTo(id,...) — Described above.
```lua
package.path="?/init.lua;?.lua;"..package.path
local multi = require("multi")
conn = multi:newConnector()
conn.OnTest = multi:newConnection()
@ -1141,7 +1443,6 @@ Going forward:
Example
---
```lua
package.path="?/init.lua;?.lua;"..package.path
multi = require("multi")
GLOBAL, THREAD = require("multi.integration.lanesManager").init()
jq = multi:newSystemThreadedJobQueue()
@ -1203,7 +1504,7 @@ L: 6543
I: 1635
~n=n*4
```
P3 Ignores using a basic funceion and instead bases its processing time on the amount of cpu time is there. If cpu-time is low and a process is set at a lower priority it will get its time reduced. There is no formula, at idle almost all process work at the same speed!
P3 Ignores using a basic function and instead bases its processing time on the amount of cpu time is there. If cpu-time is low and a process is set at a lower priority it will get its time reduced. There is no formula, at idle almost all process work at the same speed!
```
C: 2120906
H: 2120906
@ -1218,10 +1519,10 @@ Auto Priority works by seeing what should be set high or low. Due to lua not hav
**Improved:**
- Performance at the base level has been doubled! On my machine benchmark went from ~9mil to ~20 mil steps/s.
Note: If you write slow code this library's improbements wont make much of a difference.
Note: If you write slow code this library's improvements wont make much of a difference.
- Loops have been optimised as well! Being the most used objects I felt they needed to be made as fast as possible
I usually give an example of the changes made, but this time I have an explantion for multi.nextStep(). It's not an entirely new feature since multi:newJob() does something like this, but is completely different. nextStep addes a function that is executed first on the next step. If multiple things are added to next step, then they will be executed in the order that they were added.
I usually give an example of the changes made, but this time I have an explantion for `multi.nextStep()`. It's not an entirely new feature since multi:newJob() does something like this, but is completely different. nextStep adds a function that is executed first on the next step. If multiple things are added to next step, then they will be executed in the order that they were added.
Note:
The upper limit of this libraries performance on my machine is ~39mil. This is simply a while loop counting up from 0 and stops after 1 second. The 20mil that I am currently getting is probably as fast as it can get since its half of the max performance possible, and each layer I have noticed that it doubles complexity. Throughout the years with this library I have seen massive improvements in speed. In the beginning we had only ~2000 steps per second. Fast right? then after some tweaks we went to about 300000 steps per second, then 600000. Some more tweaks brought me to ~1mil steps per second, then to ~4 mil then ~9 mil and now finally ~20 mil... the doubling effect that i have now been seeing means that odds are I have reach the limit. I will aim to add more features and optimize individule objects. If its possible to make the library even faster then I will go for it.
@ -1235,10 +1536,9 @@ Fixed:
Changed:
---
- thread.hold() now returns the arguments that were pass by the event function
- event objexts now contain a copy of what returns were made by the function that called it in a table called returns that exist inside of the object
- event objects now contain a copy of what returns were made by the function that called it in a table called returns that exist inside of the object
```lua
package.path="?/init.lua;?.lua;"..package.path
multi = require("multi")
local a = 0
multi:newThread("test",function()
@ -1335,7 +1635,6 @@ Now there is a little trick you can do. If you combine both networkmanager and s
**NodeManager.lua**
```lua
package.path="?/init.lua;?.lua;"..package.path
multi = require("multi")
local GLOBAL, THREAD = require("multi.integration.lanesManager").init()
nGLOBAL = require("multi.integration.networkManager").init()
@ -1354,7 +1653,6 @@ Side note: I had a setting called cross talk that would allow nodes to talk to e
**Node.lua**
```lua
package.path="?/init.lua;?.lua;"..package.path
multi = require("multi")
local GLOBAL, THREAD = require("multi.integration.lanesManager").init()
nGLOBAL = require("multi.integration.networkManager").init()
@ -1376,10 +1674,8 @@ multi:mainloop(settings)
**Master.lua**
```lua
-- set up the package
package.path="?/init.lua;?.lua;"..package.path
-- Import the libraries
multi = require("multi")
local multi = require("multi")
local GLOBAL, THREAD = require("multi.integration.lanesManager").init()
nGLOBAL = require("multi.integration.networkManager").init()
-- Act as a master node
@ -1549,7 +1845,6 @@ Added:
Example of threaded connections
```lua
package.path="?/init.lua;?.lua;"..package.path
local GLOBAL,THREAD=require("multi.integration.lanesManager").init()
multi:newSystemThread("Test_Thread_1",function()
connOut = THREAD.waitFor("ConnectionNAMEHERE"):init()
@ -1586,7 +1881,6 @@ Fixed:
Example of threaded tables
```lua
package.path="?/init.lua;?.lua;"..package.path
local GLOBAL,sThread=require("multi.integration.lanesManager").init()
multi:newSystemThread("Test_Thread_1",function()
require("multi")
@ -1912,7 +2206,6 @@ Added:</br>
Using multi:systemThreadedBenchmark()
---
```lua
package.path="?/init.lua;"..package.path
local GLOBAL,sThread=require("multi.integration.lanesManager").init()
multi:systemThreadedBenchmark(3):OnBench(function(self,count)
print("First Bench: "..count)
@ -1935,41 +2228,6 @@ GLOBAL,sThread=require("multi.integration.loveManager").init() -- load the love2
-- Also, each thread has a .1 second delay! This is used to generate a random value for each thread!
require("core.GuiManager")
gui.ff.Color=Color.Black
function multi:newSystemThreadedQueue(name) -- in love2d this will spawn a channel on both ends
local c={}
c.name=name
if love then
if love.thread then
function c:init()
self.chan=love.thread.getChannel(self.name)
function self:push(v)
self.chan:push(v)
end
function self:pop()
return self.chan:pop()
end
GLOBAL[self.name]=self
return self
end
return c
else
error("Make sure you required the love.thread module!")
end
else
c.linda=lanes.linda()
function c:push(v)
self.linda:send("Q",v)
end
function c:pop()
return ({self.linda:receive(0,"Q")})[2]
end
function c:init()
return self
end
GLOBAL[name]=c
end
return c
end
queue=multi:newSystemThreadedQueue("QUEUE"):init()
queue:push("This is a test")
queue:push("This is a test2")
@ -2251,7 +2509,7 @@ Change:
Upcomming:
---
- Threaded objects wrapped in corutines, so you can hold/sleep without problems!
- Threaded objects wrapped in coroutines, so you can hold/sleep without problems!
# Update: 1.4.0 (3/20/2017)
Added:
@ -2325,8 +2583,10 @@ Added:
# Update: 1.2.0 (12.31.2016)
Added:
---
- connectionobj.getConnection(name) — returns a list of an instance (or instances) of a single connect made with connectionobj:connect(func,name) or connectionobj(func,name) if you can orginize data before hand you can route info to certain connections thus saving a lot of cpu time.
- connectionobj.getConnection(name) — returns a list of an instance (or instances) of a single connect made with connectionobj:connect(func,name) or connectionobj(func,name) if you can organize data before hand you can route info to certain connections thus saving a lot of cpu time.
**NOTE:** Only one name per each connection... you can't have 2 of the same names in a dictonary... the last one will be used
Changed:
---
- Started keeping track of dates
@ -2385,6 +2645,7 @@ Changed:
Changed:
---
- Everything, complete restructuring of the library from function based to object based. Resembles the modern version of the library
Added:
---
- Love2d support basic

19
makeENV.lua Normal file
View File

@ -0,0 +1,19 @@
commands = [[
mkdir luajit && python -m hererocks -j 2.1.0-beta3 -r latest --patch --compat all ./luajit && set "PATH=G:\VSCWorkspace\multi\luajit\bin;%PATH%" && lua -v && luarocks install multi
mkdir lua5.1 && python -m hererocks -l 5.1 -r latest --patch --compat all ./lua5.1 && set "PATH=G:\VSCWorkspace\multi\luajit\bin;%PATH%" && lua -v && luarocks install multi
mkdir lua5.2 && python -m hererocks -l 5.2 -r latest --patch --compat all ./lua5.2 && set "PATH=G:\VSCWorkspace\multi\luajit\bin;%PATH%" && lua -v && luarocks install multi
mkdir lua5.3 && python -m hererocks -l 5.3 -r latest --patch --compat all ./lua5.3 && set "PATH=G:\VSCWorkspace\multi\luajit\bin;%PATH%" && lua -v && luarocks install multi
mkdir lua5.4 && python -m hererocks -l 5.4 -r latest --patch --compat all ./lua5.4 && set "PATH=G:\VSCWorkspace\multi\luajit\bin;%PATH%" && lua -v && luarocks install multi
]]
function string.split (inputstr, sep)
local sep = sep or "\n"
local t={}
for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
table.insert(t, str)
end
return t
end
local run = commands:split()
for i=1,#run do
os.execute(run[i])
end

31
makeENV.sh Executable file
View File

@ -0,0 +1,31 @@
#!/bin/bash
mkdir luajit
hererocks -j 2.1.0-beta3 -r latest --compat all ./luajit
. luajit/bin/activate
echo | lua -v
luarocks install multi
deactivate-lua
mkdir lua5.1
hererocks -l 5.1 -r latest --patch --compat all ./lua5.1
. lua5.1/bin/activate
echo | lua -v
luarocks install multi
deactivate-lua
mkdir lua5.2
hererocks -l 5.2 -r latest --patch --compat all ./lua5.2
. lua5.2/bin/activate
echo | lua -v
luarocks install multi
deactivate-lua
mkdir lua5.3
hererocks -l 5.3 -r latest --patch --compat all ./lua5.3
. lua5.3/bin/activate
echo | lua -v
luarocks install multi
deactivate-lua
mkdir lua5.4
hererocks -l 5.4 -r latest --patch --compat all ./lua5.4
. lua5.4/bin/activate
echo | lua -v
luarocks install multi
deactivate-lua

View File

@ -1,273 +0,0 @@
--[[
MIT License
Copyright (c) 2020 Ryan Ward
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]]
if table.unpack then
unpack=table.unpack
end
function table.val_to_str ( v )
if "string" == type( v ) then
v = string.gsub( v, "\n", "\\n" )
if string.match( string.gsub(v,"[^'\"]",""), '^"+$' ) then
return "'" .. v .. "'"
end
return '"' .. string.gsub(v,'"', '\\"' ) .. '"'
else
return "table" == type( v ) and table.tostring( v ) or
tostring( v )
end
end
function table.key_to_str ( k )
if "string" == type( k ) and string.match( k, "^[_%a][_%a%d]*$" ) then
return k
else
return "[" .. table.val_to_str( k ) .. "]"
end
end
function table.tostring( tbl )
local result, done = {}, {}
for k, v in ipairs( tbl ) do
table.insert( result, table.val_to_str( v ) )
done[ k ] = true
end
for k, v in pairs( tbl ) do
if not done[ k ] then
table.insert( result,
table.key_to_str( k ) .. "=" .. table.val_to_str( v ) )
end
end
return "{" .. table.concat( result, "," ) .. "}"
end
function table.merge(t1, t2)
t1,t2= t1 or {},t2 or {}
for k,v in pairs(t2) do
if type(v) == "table" then
if type(t1[k] or false) == "table" then
table.merge(t1[k] or {}, t2[k] or {})
else
t1[k] = v
end
else
t1[k] = v
end
end
return t1
end
Library={}
function Library.inject(lib,dat,arg)
if type(lib)=="table" then
if type(dat)=="table" then
table.merge(lib,dat)
elseif type(dat)=="string" then
if lib.Version and dat:match("(%d-)%.(%d-)%.(%d-)") then
lib.Version={dat:match("(%d+)%.(%d+)%.(%d+)")}
elseif dat=="meta" and type(arg)=="table" then
local _mt=getmetatable(lib) or {}
local mt={}
table.merge(mt,arg)
table.merge(_mt,mt)
setmetatable(lib,_mt)
elseif dat=="compat" then
lib["getVersion"]=function(self) return self.Version[1].."."..self.Version[2].."."..self.Version[3] end
if not lib.Version then
lib.Version={1,0,0}
end
elseif dat=="inhert" then
if not(lib["!%"..arg.."%!"]) then print("Wrong Password!!") return end
lib["!%"..arg.."%!"].__index=lib["!!%"..arg.."%!!"]
end
elseif type(dat)=="function" then
for i,v in pairs(lib) do
dat(lib,i,v)
end
end
elseif type(lib)=="function" or type(lib)=="userdata" then
if lib==unpack then
print("function unpack cannot yet be injected!")
return unpack
elseif lib==pairs then
print("function pairs cannot yet be injected!")
return lib
elseif lib==ipairs then
print("function ipairs cannot yet be injected!")
return lib
elseif lib==type then
print("function type cannot yet be injected!")
return lib
end
temp={}
local mt={
__call=function(t,...)
local consume,MainRet,init={},{},{...}
local tt={}
for i=1,#t.__Link do
tt={}
if t.__Link[i]==t.__Main then
if #consume~=0 then
MainRet={t.__Link[i](unpack(consume))}
else
MainRet={t.__Link[i](unpack(init))}
end
else
if i==1 then
consume=(t.__Link[i](unpack(init)))
else
if type(MainRet)=="table" then
table.merge(tt,MainRet)
end
if type(consume)=="table" then
table.merge(tt,consume)
end
consume={t.__Link[i](unpack(tt))}
end
if i==#t.__Link then
return unpack(consume)
end
if consume then if consume[0]=="\1\7\6\3\2\99\125" then consume[0]=nil return unpack(consume) end end
end
end
if type(MainRet)=="table" then
table.merge(tt,MainRet)
end
if type(consume)=="table" then
table.merge(tt,consume)
end
return unpack(tt)
end,
}
temp.__Link={lib}
temp.__Main=lib
temp.__self=temp
function temp:inject(func,i)
if i then
table.insert(self.__Link,i,func)
else
table.insert(self.__Link,func)
end
end
function temp:consume(func)
for i=1,#self.__Link do
if self.__Link[i]==self.__Main then
self.__Link[i]=func
self.__self.__Main=func
return true
end
end
return false
end
setmetatable(temp,mt)
return temp
else
return "arg1 must be a table or a function"
end
end
function Library.convert(...)
local temp,rets={...},{}
for i=1,#temp do
if type(temp[i])=="function" then
table.insert(rets,Library.inject(temp[i]))
else
error("Takes only functions and returns in order from functions given. arg # "..i.." is not a function!!! It is a "..type(temp[i]))
end
end
return unpack(rets)
end
local link={MainLibrary=Library}
Library.inject(Library,"meta",{
__Link=link,
__call=function(self,func) func(link) end,
})
local multi, thread = require("multi").init()
os.sleep = love.timer.sleep
multi.drawF = {}
function multi:onDraw(func, i)
i = i or 1
table.insert(self.drawF, i, func)
end
multi.OnKeyPressed = multi:newConnection()
multi.OnKeyReleased = multi:newConnection()
multi.OnMousePressed = multi:newConnection()
multi.OnMouseReleased = multi:newConnection()
multi.OnMouseWheelMoved = multi:newConnection()
multi.OnMouseMoved = multi:newConnection()
multi.OnDraw = multi:newConnection()
multi.OnTextInput = multi:newConnection()
multi.OnUpdate = multi:newConnection()
multi.OnQuit = multi:newConnection()
multi.OnPreLoad(function()
local function Hook(func, conn)
if love[func] ~= nil then
love[func] = Library.convert(love[func])
love[func]:inject(function(...)
conn:Fire(...)
return {...}
end,1)
elseif love[func] == nil then
love[func] = function(...)
conn:Fire(...)
end
end
end
Hook("quit", multi.OnQuit)
Hook("keypressed", multi.OnKeyPressed)
Hook("keyreleased", multi.OnKeyReleased)
Hook("mousepressed", multi.OnMousePressed)
Hook("mousereleased", multi.OnMouseReleased)
Hook("wheelmoved", multi.OnMouseWheelMoved)
Hook("mousemoved", multi.OnMouseMoved)
Hook("draw", multi.OnDraw)
Hook("textinput", multi.OnTextInput)
Hook("update", multi.OnUpdate)
multi.OnDraw(function()
for i = 1, #multi.drawF do
love.graphics.setColor(255, 255, 255, 255)
multi.drawF[i]()
end
end)
end)
function multi:loveloop(light)
local link
link = multi:newThread(function()
local mainloop = love.run()
while true do
thread.yield()
pcall(mainloop)
end
end).OnError(function(...)
print(...)
end)
if light==false then
multi:mainloop()
else
multi:lightloop()
end
end
multi.OnQuit(function()
multi.Stop()
love.event.quit()
end)
return multi

View File

@ -1,281 +0,0 @@
--[[
MIT License
Copyright (c) 2020 Ryan Ward
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]]
if table.unpack then
unpack=table.unpack
end
function table.val_to_str ( v )
if "string" == type( v ) then
v = string.gsub( v, "\n", "\\n" )
if string.match( string.gsub(v,"[^'\"]",""), '^"+$' ) then
return "'" .. v .. "'"
end
return '"' .. string.gsub(v,'"', '\\"' ) .. '"'
else
return "table" == type( v ) and table.tostring( v ) or
tostring( v )
end
end
function table.key_to_str ( k )
if "string" == type( k ) and string.match( k, "^[_%a][_%a%d]*$" ) then
return k
else
return "[" .. table.val_to_str( k ) .. "]"
end
end
function table.tostring( tbl )
local result, done = {}, {}
for k, v in ipairs( tbl ) do
table.insert( result, table.val_to_str( v ) )
done[ k ] = true
end
for k, v in pairs( tbl ) do
if not done[ k ] then
table.insert( result,
table.key_to_str( k ) .. "=" .. table.val_to_str( v ) )
end
end
return "{" .. table.concat( result, "," ) .. "}"
end
function table.merge(t1, t2)
t1,t2= t1 or {},t2 or {}
for k,v in pairs(t2) do
if type(v) == "table" then
if type(t1[k] or false) == "table" then
table.merge(t1[k] or {}, t2[k] or {})
else
t1[k] = v
end
else
t1[k] = v
end
end
return t1
end
Library={}
function Library.inject(lib,dat,arg)
if type(lib)=="table" then
if type(dat)=="table" then
table.merge(lib,dat)
elseif type(dat)=="string" then
if lib.Version and dat:match("(%d-)%.(%d-)%.(%d-)") then
lib.Version={dat:match("(%d+)%.(%d+)%.(%d+)")}
elseif dat=="meta" and type(arg)=="table" then
local _mt=getmetatable(lib) or {}
local mt={}
table.merge(mt,arg)
table.merge(_mt,mt)
setmetatable(lib,_mt)
elseif dat=="compat" then
lib["getVersion"]=function(self) return self.Version[1].."."..self.Version[2].."."..self.Version[3] end
if not lib.Version then
lib.Version={1,0,0}
end
elseif dat=="inhert" then
if not(lib["!%"..arg.."%!"]) then print("Wrong Password!!") return end
lib["!%"..arg.."%!"].__index=lib["!!%"..arg.."%!!"]
end
elseif type(dat)=="function" then
for i,v in pairs(lib) do
dat(lib,i,v)
end
end
elseif type(lib)=="function" or type(lib)=="userdata" then
if lib==unpack then
print("function unpack cannot yet be injected!")
return unpack
elseif lib==pairs then
print("function pairs cannot yet be injected!")
return lib
elseif lib==ipairs then
print("function ipairs cannot yet be injected!")
return lib
elseif lib==type then
print("function type cannot yet be injected!")
return lib
end
temp={}
local mt={
__call=function(t,...)
local consume,MainRet,init={},{},{...}
local tt={}
for i=1,#t.__Link do
tt={}
if t.__Link[i]==t.__Main then
if #consume~=0 then
MainRet={t.__Link[i](unpack(consume))}
else
MainRet={t.__Link[i](unpack(init))}
end
else
if i==1 then
consume=(t.__Link[i](unpack(init)))
else
if type(MainRet)=="table" then
table.merge(tt,MainRet)
end
if type(consume)=="table" then
table.merge(tt,consume)
end
consume={t.__Link[i](unpack(tt))}
end
if i==#t.__Link then
return unpack(consume)
end
if consume then if consume[0]=="\1\7\6\3\2\99\125" then consume[0]=nil return unpack(consume) end end
end
end
if type(MainRet)=="table" then
table.merge(tt,MainRet)
end
if type(consume)=="table" then
table.merge(tt,consume)
end
return unpack(tt)
end,
}
temp.__Link={lib}
temp.__Main=lib
temp.__self=temp
function temp:inject(func,i)
if i then
table.insert(self.__Link,i,func)
else
table.insert(self.__Link,func)
end
end
function temp:consume(func)
for i=1,#self.__Link do
if self.__Link[i]==self.__Main then
self.__Link[i]=func
self.__self.__Main=func
return true
end
end
return false
end
setmetatable(temp,mt)
return temp
else
return "arg1 must be a table or a function"
end
end
function Library.convert(...)
local temp,rets={...},{}
for i=1,#temp do
if type(temp[i])=="function" then
table.insert(rets,Library.inject(temp[i]))
else
error("Takes only functions and returns in order from functions given. arg # "..i.." is not a function!!! It is a "..type(temp[i]))
end
end
return unpack(rets)
end
local link={MainLibrary=Library}
Library.inject(Library,"meta",{
__Link=link,
__call=function(self,func) func(link) end,
})
local multi, thread = require("multi").init()
os.sleep = lovr.timer.sleep
multi.drawF = {}
function multi:onDraw(func, i)
i = i or 1
table.insert(self.drawF, i, func)
end
multi.OnKeyPressed = multi:newConnection()
multi.OnKeyReleased = multi:newConnection()
multi.OnErrHand = multi:newConnection()
multi.OnFocus = multi:newConnection()
multi.OnLoad = multi:newConnection()
multi.OnLog = multi:newConnection()
multi.OnPermission = multi:newConnection()
multi.OnResize = multi:newConnection()
multi.OnRestart = multi:newConnection()
multi.OnThreadError = multi:newConnection()
multi.OnDraw = multi:newConnection()
multi.OnTextInput = multi:newConnection()
multi.OnUpdate = multi:newConnection()
multi.OnQuit = multi:newConnection()
multi.OnPreLoad(function()
local function Hook(func, conn)
if lovr[func] ~= nil then
lovr[func] = Library.convert(lovr[func])
lovr[func]:inject(function(...)
conn:Fire(...)
return {...}
end,1)
elseif lovr[func] == nil then
lovr[func] = function(...)
conn:Fire(...)
end
end
end
Hook("quit", multi.OnQuit)
Hook("keypressed", multi.OnKeyPressed)
Hook("keyreleased", multi.OnKeyReleased)
Hook("focus", multi.OnFocus)
Hook("log", multi.OnLog)
Hook("errhand", multi.OnErrHand)
Hook("load", multi.OnLoad)
Hook("draw", multi.OnDraw)
Hook("textinput", multi.OnTextInput)
Hook("update", multi.OnUpdate)
Hook("permission", multi.OnPermission)
Hook("resize", multi.OnResize)
Hook("restart", multi.OnRestart)
Hook("threaderror", multi.OnThreadError)
multi.OnDraw(function()
for i = 1, #multi.drawF do
lovr.graphics.setColor(255, 255, 255, 255)
multi.drawF[i]()
end
end)
end)
function multi:lovrloop(light)
local link
link = multi:newThread(function()
local mainloop = lovr.run()
while true do
thread.yield()
pcall(mainloop)
end
end).OnError(function(...)
print(...)
end)
if light==false then
multi:mainloop()
else
multi:lightloop()
end
end
multi.OnQuit(function()
multi.Stop()
lovr.event.quit()
end)
return multi

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
--[[
MIT License
Copyright (c) 2020 Ryan Ward
Copyright (c) 2022 Ryan Ward
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@ -113,7 +113,7 @@ function multi:newSystemThreadedJobQueue(n)
end)
end,holup),name
end
multi:newThread("JobQueueManager",function()
thread:newThread("JobQueueManager",function()
while true do
local job = thread.hold(function()
return queueReturn:pop()
@ -129,7 +129,7 @@ function multi:newSystemThreadedJobQueue(n)
local clock = os.clock
local ref = 0
setmetatable(_G,{__index = funcs})
multi:newThread("JobHandler",function()
thread:newThread("JobHandler",function()
while true do
local dat = thread.hold(function()
return queueJob:pop()
@ -141,7 +141,7 @@ function multi:newSystemThreadedJobQueue(n)
queueReturn:push{jid, funcs[name](unpack(args)),queue}
end
end)
multi:newThread("DoAllHandler",function()
thread:newThread("DoAllHandler",function()
while true do
local dat = thread.hold(function()
return doAll:peek()
@ -156,7 +156,7 @@ function multi:newSystemThreadedJobQueue(n)
end
end
end)
multi:newThread("IdleHandler",function()
thread:newThread("IdleHandler",function()
while true do
thread.hold(function()
return clock()-idle>3

View File

@ -1,7 +1,7 @@
--[[
MIT License
Copyright (c) 2020 Ryan Ward
Copyright (c) 2022 Ryan Ward
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@ -21,8 +21,9 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]]
package.path = "?/init.lua;?.lua;" .. package.path
multi, thread = require("multi").init() -- get it all and have it on all lanes
multi, thread = require("multi"):init() -- get it all and have it on all lanes
if multi.integration then -- This allows us to call the lanes manager from supporting modules without a hassle
return {
init = function()
@ -34,138 +35,68 @@ end
lanes = require("lanes").configure()
multi.SystemThreads = {}
multi.isMainThread = true
function multi:canSystemThread()
return true
end
function multi:getPlatform()
return "lanes"
end
-- Step 2 set up the Linda objects
local __GlobalLinda = lanes.linda() -- handles global stuff
local __SleepingLinda = lanes.linda() -- handles sleeping stuff
local __ConsoleLinda = lanes.linda() -- handles console stuff
multi:newLoop(function()
local _,data = __ConsoleLinda:receive(0, "Q")
if data then
print(unpack(data))
end
end)
local GLOBAL,THREAD = require("multi.integration.lanesManager.threads").init(__GlobalLinda,__SleepingLinda)
local threads = {}
local __StatusLinda = lanes.linda() -- handles pushstatus for stfunctions
local GLOBAL,THREAD = require("multi.integration.lanesManager.threads").init(__GlobalLinda, __SleepingLinda, __StatusLinda)
local count = 1
local started = false
local livingThreads = {}
function THREAD:newFunction(func,holdme)
local tfunc = {}
tfunc.Active = true
function tfunc:Pause()
self.Active = false
end
function tfunc:Resume()
self.Active = true
end
function tfunc:holdMe(b)
holdme = b
end
local function noWait()
return nil, "Function is paused"
end
local rets, err
local function wait(no)
if thread.isThread() and not (no) then
return multi.hold(function()
if err then
return nil, err
elseif rets then
return unpack(rets)
end
end)
else
while not rets and not err do
multi.scheduler:Act()
end
if err then
return nil,err
end
return unpack(rets)
end
end
tfunc.__call = function(t,...)
if not t.Active then
if holdme then
return nil, "Function is paused"
end
return {
isTFunc = true,
wait = noWait,
connect = function(f)
f(nil,"Function is paused")
end
}
end
local t = multi:newSystemThread("SystemThreadedFunction",func,...)
t.OnDeath(function(self,...) rets = {...} end)
t.OnError(function(self,e) err = e end)
if holdme then
return wait()
end
local temp = {
OnStatus = multi:newConnection(),
OnError = multi:newConnection(),
OnReturn = multi:newConnection(),
isTFunc = true,
wait = wait,
connect = function(f)
local tempConn = multi:newConnection()
t.OnDeath(function(self,...) if f then f(...) else tempConn:Fire(...) end end)
t.OnError(function(self,err) if f then f(nil,err) else tempConn:Fire(nil,err) end end)
return tempConn
end
}
t.OnDeath(function(self,...) temp.OnReturn:Fire(...) end)
t.OnError(function(self,err) temp.OnError:Fire(err) end)
t.linkedFunction = temp
t.statusconnector = temp.OnStatus
return temp
end
setmetatable(tfunc,tfunc)
return tfunc
return thread:newFunctionBase(function(...)
return multi:newSystemThread("TempSystemThread",func,...)
end,holdme)()
end
function multi:newSystemThread(name, func, ...)
multi.InitSystemThreadErrorHandler()
rand = math.random(1, 10000000)
local c = {}
local __self = c
local rand = math.random(1, 10000000)
local return_linda = lanes.linda()
c = {}
c.name = name
c.Name = name
c.Id = count
c.loadString = {"base","package","os","io","math","table","string","coroutine"}
livingThreads[count] = {true, name}
c.returns = return_linda
c.Type = "sthread"
c.creationTime = os.clock()
c.alive = true
c.priority = THREAD.Priority_Normal
local args = {...}
multi:newThread(function()
c.thread = lanes.gen(table.concat(c.loadString,","),
{
globals={ -- Set up some globals
THREAD_NAME=name,
THREAD_ID=count,
THREAD = THREAD,
GLOBAL = GLOBAL,
_Console = __ConsoleLinda
},
priority=c.priority
},func)(unpack(args))
thread.kill()
end)
c.thread = lanes.gen("*",
{
globals={ -- Set up some globals
THREAD_NAME = name,
THREAD_ID = count,
THREAD = THREAD,
GLOBAL = GLOBAL,
_Console = __ConsoleLinda
},
priority=c.priority
},function(...)
local has_error = true
return_linda:set("returns",{func(...)})
has_error = false
end)(...)
count = count + 1
function c:getName()
return c.Name
end
function c:kill()
self.thread:cancel()
multi.print("Thread: '" .. self.name .. "' has been stopped!")
self.alive = false
end
table.insert(multi.SystemThreads, c)
@ -175,59 +106,60 @@ function multi:newSystemThread(name, func, ...)
return c
end
local function detectLuaError(str)
return type(str)=="string" and str:match("%.lua:%d*:")
end
local function tableLen(tab)
local len = 0
for i,v in pairs(tab) do
len = len + 1
end
return len
end
THREAD.newSystemThread = multi.newSystemThread
function multi.InitSystemThreadErrorHandler()
if started == true then
return
end
started = true
multi:newThread("ThreadErrorHandler",function()
thread:newThread("SystemThreadScheduler",function()
local threads = multi.SystemThreads
local _,data,status,push,temp
while true do
thread.sleep(.1) -- switching states often takes a huge hit on performance. half a second to tell me there is an error is good enough.
thread.yield()
_,data = __ConsoleLinda:receive(0, "Q")
for i = #threads, 1, -1 do
local _,data = pcall(function()
return {threads[i].thread:join(1)}
end)
local v, err, t = data[1],data[2],data[3]
if detectLuaError(err) then
if err:find("Thread was killed!\1") then
livingThreads[threads[i].Id] = {false, threads[i].Name}
threads[i].alive = false
threads[i].OnDeath:Fire(threads[i],nil,"Thread was killed!")
GLOBAL["__THREADS__"] = livingThreads
table.remove(threads, i)
else
threads[i].OnError:Fire(threads[i], err, "Error in systemThread: '" .. threads[i].name .. "' <" .. err .. ">")
threads[i].alive = false
livingThreads[threads[i].Id] = {false, threads[i].Name}
GLOBAL["__THREADS__"] = livingThreads
table.remove(threads, i)
end
elseif tableLen(data)>0 then
livingThreads[threads[i].Id] = {false, threads[i].Name}
threads[i].alive = false
threads[i].OnDeath:Fire(threads[i],unpack(data))
temp = threads[i]
status = temp.thread.status
push = __StatusLinda:get(temp.Id)
if push then
temp.statusconnector:Fire(unpack(({__StatusLinda:receive(nil, temp.Id)})[2]))
end
if status == "done" or temp.returns:get("returns") then
livingThreads[temp.Id] = {false, temp.Name}
temp.alive = false
temp.OnDeath:Fire(unpack(({temp.returns:receive(0, "returns")})[2]))
GLOBAL["__THREADS__"] = livingThreads
table.remove(threads, i)
elseif status == "running" then
--
elseif status == "waiting" then
--
elseif status == "error" then
livingThreads[temp.Id] = {false, temp.Name}
temp.alive = false
temp.OnError:Fire(temp,nil,unpack(temp.returns:receive(0,"returns") or {"Thread Killed!"}))
GLOBAL["__THREADS__"] = livingThreads
table.remove(threads, i)
elseif status == "cancelled" then
livingThreads[temp.Id] = {false, temp.Name}
temp.alive = false
temp.OnError:Fire(temp,nil,"thread_cancelled")
GLOBAL["__THREADS__"] = livingThreads
table.remove(threads, i)
elseif status == "killed" then
livingThreads[temp.Id] = {false, temp.Name}
temp.alive = false
temp.OnError:Fire(temp,nil,"thread_killed")
GLOBAL["__THREADS__"] = livingThreads
table.remove(threads, i)
end
end
end
end).OnError(function(...)
print("Error!",...)
end)
end
multi.print("Integrated Lanes!")
multi.integration = {} -- for module creators
multi.integration.GLOBAL = GLOBAL

View File

@ -1,7 +1,7 @@
--[[
MIT License
Copyright (c) 2020 Ryan Ward
Copyright (c) 2022 Ryan Ward
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@ -28,7 +28,8 @@ local function getOS()
return "unix"
end
end
local function INIT(__GlobalLinda,__SleepingLinda)
local function INIT(__GlobalLinda, __SleepingLinda, __StatusLinda)
local THREAD = {}
THREAD.Priority_Core = 3
THREAD.Priority_High = 2
@ -37,12 +38,15 @@ local function INIT(__GlobalLinda,__SleepingLinda)
THREAD.Priority_Below_Normal = -1
THREAD.Priority_Low = -2
THREAD.Priority_Idle = -3
function THREAD.set(name, val)
__GlobalLinda:set(name, val)
end
function THREAD.get(name)
return __GlobalLinda:get(name)
end
function THREAD.waitFor(name)
local function wait()
math.randomseed(os.time())
@ -53,14 +57,17 @@ local function INIT(__GlobalLinda,__SleepingLinda)
until __GlobalLinda:get(name)
return __GlobalLinda:get(name)
end
if getOS() == "windows" then
THREAD.__CORES = tonumber(os.getenv("NUMBER_OF_PROCESSORS"))
else
THREAD.__CORES = tonumber(io.popen("nproc --all"):read("*n"))
end
function THREAD.getCores()
return THREAD.__CORES
end
function THREAD.getConsole()
local c = {}
c.queue = _Console
@ -73,28 +80,41 @@ local function INIT(__GlobalLinda,__SleepingLinda)
end
return c
end
function THREAD.getThreads()
return GLOBAL.__THREADS__
end
if os.getOS() == "windows" then
THREAD.__CORES = tonumber(os.getenv("NUMBER_OF_PROCESSORS"))
else
THREAD.__CORES = tonumber(io.popen("nproc --all"):read("*n"))
end
function THREAD.kill() -- trigger the lane destruction
error("Thread was killed!\1")
end
function THREAD.getName()
return THREAD_NAME
end
function THREAD.getID()
return THREAD_ID
end
function THREAD.pushStatus(...)
local args = {...}
__StatusLinda:send(nil,THREAD_ID, args)
end
_G.THREAD_ID = 0
function THREAD.sleep(n)
math.randomseed(os.time())
__SleepingLinda:receive(n, "__non_existing_variable")
end
function THREAD.hold(n)
local function wait()
math.randomseed(os.time())
@ -104,6 +124,7 @@ local function INIT(__GlobalLinda,__SleepingLinda)
wait()
until n()
end
local GLOBAL = {}
setmetatable(GLOBAL, {
__index = function(t, k)
@ -115,6 +136,7 @@ local function INIT(__GlobalLinda,__SleepingLinda)
})
return GLOBAL, THREAD
end
return {init = function(g,s)
return INIT(g,s)
return {init = function(g,s,st)
return INIT(g,s,st)
end}

View File

@ -1,7 +1,7 @@
--[[
MIT License
Copyright (c) 2020 Ryan Ward
Copyright (c) 2022 Ryan Ward
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@ -131,7 +131,7 @@ function multi:newSystemThreadedJobQueue(n)
end)
end,holup),name
end
multi:newThread("jobManager",function()
thread:newThread("jobManager",function()
while true do
thread.yield()
local dat = c.queueReturn:pop()
@ -155,7 +155,7 @@ function multi:newSystemThreadedJobQueue(n)
local queueAll = love.thread.getChannel("__JobQueue_"..jqc.."_queueAll")
local registry = {}
setmetatable(_G,{__index = funcs})
multi:newThread("startUp",function()
thread:newThread("startUp",function()
while true do
thread.yield()
local all = queueAll:peek()
@ -165,7 +165,7 @@ function multi:newSystemThreadedJobQueue(n)
end
end
end)
multi:newThread("runner",function()
thread:newThread("runner",function()
thread.sleep(.1)
while true do
thread.yield()
@ -187,7 +187,7 @@ function multi:newSystemThreadedJobQueue(n)
end):OnError(function(...)
error(...)
end)
multi:newThread("Idler",function()
thread:newThread("Idler",function()
while true do
thread.yield()
if clock()-lastProc> 2 then

View File

@ -1,7 +1,7 @@
--[[
MIT License
Copyright (c) 2020 Ryan Ward
Copyright (c) 2022 Ryan Ward
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@ -37,7 +37,7 @@ GLOBAL = THREAD.getGlobal()
multi, thread = require("multi").init()
stab["returns"] = {THREAD.loadDump(__FUNC__)(unpack(__IMPORTS))}
]]
local multi, thread = require("multi.compat.love2d"):init()
local multi, thread = require("multi"):init()
local THREAD = {}
__THREADID__ = 0
__THREADNAME__ = "MainThread"
@ -48,70 +48,7 @@ local GLOBAL = THREAD.getGlobal()
local THREAD_ID = 1
local OBJECT_ID = 0
local stf = 0
function THREAD:newFunction(func,holup)
local tfunc = {}
tfunc.Active = true
function tfunc:Pause()
self.Active = false
end
function tfunc:Resume()
self.Active = true
end
function tfunc:holdMe(b)
holdme = b
end
local function noWait()
return nil, "Function is paused"
end
local rets, err
local function wait(no)
if thread.isThread() and not (no) then
-- In a thread
else
-- Not in a thread
end
end
tfunc.__call = function(t,...)
if not t.Active then
if holdme then
return nil, "Function is paused"
end
return {
isTFunc = true,
wait = noWait,
connect = function(f)
f(nil,"Function is paused")
end
}
end
local t = multi:newSystemThread("SystemThreadedFunction",func,...)
t.OnDeath(function(self,...) rets = {...} end)
t.OnError(function(self,e) err = e end)
if holdme then
return wait()
end
local temp = {
OnStatus = multi:newConnection(),
OnError = multi:newConnection(),
OnReturn = multi:newConnection(),
isTFunc = true,
wait = wait,
connect = function(f)
local tempConn = multi:newConnection()
t.OnDeath(function(self,...) if f then f(...) else tempConn:Fire(...) end end)
t.OnError(function(self,err) if f then f(nil,err) else tempConn:Fire(nil,err) end end)
return tempConn
end
}
t.OnDeath(function(self,...) temp.OnReturn:Fire(...) end)
t.OnError(function(self,err) temp.OnError:Fire(err) end)
t.linkedFunction = temp
t.statusconnector = temp.OnStatus
return temp
end
setmetatable(tfunc,tfunc)
return tfunc
end
function multi:newSystemThread(name,func,...)
local c = {}
c.name = name
@ -119,38 +56,60 @@ function multi:newSystemThread(name,func,...)
c.thread=love.thread.newThread(ThreadFileData)
c.thread:start(THREAD.dump(func),c.ID,c.name,...)
c.stab = THREAD.createStaticTable(name)
GLOBAL["__THREAD_"..c.ID] = {ID=c.ID,Name=c.name,Thread=c.thread}
c.OnDeath = multi:newConnection()
c.OnError = multi:newConnection()
GLOBAL["__THREAD_"..c.ID] = {ID=c.ID, Name=c.name, Thread=c.thread}
GLOBAL["__THREAD_COUNT"] = THREAD_ID
THREAD_ID=THREAD_ID+1
multi:newThread(function()
while true do
thread.yield()
if c.stab["returns"] then
c.OnDeath:Fire(c,unpack(t.stab.returns))
t.stab.returns = nil
thread.kill()
end
local error = c.thread:getError()
if error then
if error:find("Thread Killed!\1") then
c.OnDeath:Fire(c,"Thread Killed!")
thread.kill()
else
c.OnError:Fire(c,error)
thread.kill()
THREAD_ID=THREAD_ID + 1
function c:getName()
return c.name
end
thread:newThread(function()
if name:find("TempSystemThread") then
local status_channel = love.thread.getChannel("__"..c.ID.."__MULTI__STATUS_CHANNEL__")
thread.hold(function()
-- While the thread is running we might as well do something in the loop
local status = status_channel
if status:peek()~=nil then
c.statusconnector:Fire(unpack(status:pop()))
end
end
return not c.thread:isRunning()
end)
else
thread.hold(function()
return not c.thread:isRunning()
end)
end
-- If the thread is not running let's handle that.
local thread_err = c.thread:getError()
if thread_err == "Thread Killed!\1" then
c.OnDeath:Fire("Thread Killed!")
elseif thread_err then
c.OnError:Fire(c,thread_err)
elseif c.stab.returns then
c.OnDeath:Fire(unpack(c.stab.returns))
c.stab.returns = nil
end
end)
return c
end
function love.threaderror(thread, errorstr)
print("Thread error!\n"..errorstr)
function THREAD:newFunction(func)
return thread:newFunctionBase(function(...)
return multi:newSystemThread("TempSystemThread"..THREAD_ID,func,...)
end)()
end
THREAD.newSystemThread = multi.newSystemThread
function love.threaderror(thread, errorstr)
mulit.print("Thread error!\n"..errorstr)
end
multi.integration.GLOBAL = GLOBAL
multi.integration.THREAD = THREAD
require("multi.integration.loveManager.extensions")
print("Integrated Love Threading!")
mulit.print("Integrated Love Threading!")
return {init=function()
return GLOBAL,THREAD
end}

View File

@ -1,7 +1,7 @@
--[[
MIT License
Copyright (c) 2020 Ryan Ward
Copyright (c) 2022 Ryan Ward
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@ -24,15 +24,19 @@ SOFTWARE.
require("love.timer")
require("love.system")
require("love.data")
require("love.thread")
local socket = require("socket")
local multi, thread = require("multi").init()
local threads = {}
function threads.loadDump(d)
return loadstring(d:getString())
end
function threads.dump(func)
return love.data.newByteData(string.dump(func))
end
local fRef = {"func",nil}
local function manage(channel, value)
channel:clear()
@ -44,6 +48,7 @@ local function manage(channel, value)
channel:push(value)
end
end
local function RandomVariable(length)
local res = {}
math.randomseed(socket.gettime()*10000)
@ -52,12 +57,14 @@ local function RandomVariable(length)
end
return table.concat(res)
end
local GNAME = "__GLOBAL_"
local proxy = {}
function threads.set(name,val)
if not proxy[name] then proxy[name] = love.thread.getChannel(GNAME..name) end
proxy[name]:performAtomic(manage, val)
end
function threads.get(name)
if not proxy[name] then proxy[name] = love.thread.getChannel(GNAME..name) end
local dat = proxy[name]:peek()
@ -67,6 +74,7 @@ function threads.get(name)
return dat
end
end
function threads.waitFor(name)
if thread.isThread() then
return thread.hold(function()
@ -82,18 +90,28 @@ function threads.waitFor(name)
end
return dat
end
function threads.package(name,val)
local init = val.init
val.init=threads.dump(val.init)
GLOBAL[name]=val
val.init=init
end
function threads.getCores()
return love.system.getProcessorCount()
end
function threads.kill()
error("Thread Killed!\1")
end
function THREAD.pushStatus(...)
local status_channel = love.thread.getChannel("__"..__THREADID__.."__MULTI__STATUS_CHANNEL__")
local args = {...}
status_channel:push(__THREADID__, args)
end
function threads.getThreads()
local t = {}
for i=1,GLOBAL["__THREAD_COUNT"] do
@ -101,18 +119,23 @@ function threads.getThreads()
end
return t
end
function threads.getThread(n)
return GLOBAL["__THREAD_"..n]
end
function threads.getName()
return __THREADNAME__
end
function threads.getID()
return __THREADID__
end
function threads.sleep(n)
love.timer.sleep(n)
end
function threads.getGlobal()
return setmetatable({},
{
@ -125,6 +148,7 @@ function threads.getGlobal()
}
)
end
function threads.createTable(n)
local _proxy = {}
local function set(name,val)
@ -151,6 +175,7 @@ function threads.createTable(n)
}
)
end
function threads.getConsole()
local c = {}
c.queue = love.thread.getChannel("__CONSOLE__")
@ -163,11 +188,12 @@ function threads.getConsole()
end
return c
end
if not ISTHREAD then
local clock = os.clock
local lastproc = clock()
local queue = love.thread.getChannel("__CONSOLE__")
multi:newThread("consoleManager",function()
thread:newThread("consoleManager",function()
while true do
thread.yield()
dat = queue:pop()
@ -181,6 +207,7 @@ if not ISTHREAD then
end
end)
end
function threads.createStaticTable(n)
local __proxy = {}
local function set(name,val)
@ -212,10 +239,12 @@ function threads.createStaticTable(n)
}
)
end
function threads.hold(n)
local dat
while not(dat) do
dat = n()
end
end
return threads

View File

@ -1,7 +1,7 @@
--[[
MIT License
Copyright (c) 2020 Ryan Ward
Copyright (c) 2022 Ryan Ward
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@ -129,7 +129,7 @@ function multi:newSystemThreadedJobQueue(n)
end)
end,holup),name
end
multi:newThread("jobManager",function()
thread:newThread("jobManager",function()
while true do
thread.yield()
local dat = c.queueReturn:pop()
@ -153,7 +153,7 @@ function multi:newSystemThreadedJobQueue(n)
local queueAll = lovr.thread.getChannel("__JobQueue_"..jqc.."_queueAll")
local registry = {}
setmetatable(_G,{__index = funcs})
multi:newThread("startUp",function()
thread:newThread("startUp",function()
while true do
thread.yield()
local all = queueAll:peek()
@ -163,7 +163,7 @@ function multi:newSystemThreadedJobQueue(n)
end
end
end)
multi:newThread("runner",function()
thread:newThread("runner",function()
thread.sleep(.1)
while true do
thread.yield()
@ -185,7 +185,7 @@ function multi:newSystemThreadedJobQueue(n)
end):OnError(function(...)
error(...)
end)
multi:newThread("Idler",function()
thread:newThread("Idler",function()
while true do
thread.yield()
if clock()-lastProc> 2 then

View File

@ -1,7 +1,7 @@
--[[
MIT License
Copyright (c) 2020 Ryan Ward
Copyright (c) 2022 Ryan Ward
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@ -76,6 +76,7 @@ function multi:newSystemThread(name,func,...)
THREAD_ID=THREAD_ID+1
return c
end
THREAD.newSystemThread = multi.newSystemThread
function lovr.threaderror(thread, errorstr)
print("Thread error!\n"..errorstr)
end

View File

@ -1,7 +1,7 @@
--[[
MIT License
Copyright (c) 2020 Ryan Ward
Copyright (c) 2022 Ryan Ward
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@ -168,7 +168,7 @@ if not ISTHREAD then
local clock = os.clock
local lastproc = clock()
local queue = lovr.thread.getChannel("__CONSOLE__")
multi:newThread("consoleManager",function()
thread:newThread("consoleManager",function()
while true do
thread.yield()
dat = queue:pop()

View File

@ -1,7 +1,7 @@
--[[
MIT License
Copyright (c) 2020 Ryan Ward
Copyright (c) 2022 Ryan Ward
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@ -116,6 +116,7 @@ local function _INIT(luvitThread, timer)
luvitThread.start(entry, package.path, name, c.func, ...)
return c
end
THREAD.newSystemThread = multi.newSystemThread
multi.print("Integrated Luvit!")
multi.integration = {} -- for module creators
multi.integration.GLOBAL = GLOBAL

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,7 @@
--[[
MIT License
Copyright (c) 2020 Ryan Ward
Copyright (c) 2022 Ryan Ward
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@ -72,7 +72,7 @@ function master:newNetworkThread(nodeName,func,...)
local ret
local nID = netID
local conn = multi:newConnection()
multi:newThread(function()
thread:newthread(function()
dat:addBlock{
args = args,
func = func,
@ -143,7 +143,7 @@ function multi:newMasterNode(cd)
else
c:getNodesFromBroadcast()
end
multi:newThread("CMDQueueProcessor",function()
thread:newthread("CMDQueueProcessor",function()
while true do
thread.skip(128)
local data = table.remove(c._queue,1)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,7 @@
--[[
MIT License
Copyright (c) 2020 Ryan Ward
Copyright (c) 2022 Ryan Ward
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@ -125,7 +125,7 @@ function multi:newSystemThreadedJobQueue(n)
end,holup),name
end
for i=1,c.cores do
multi:newThread("PesudoThreadedJobQueue_"..i,function()
thread:newthread("PesudoThreadedJobQueue_"..i,function()
while true do
thread.yield()
if #jobs>0 then

View File

@ -22,7 +22,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]]
package.path = "?/init.lua;?.lua;" .. package.path
local multi, thread = require("multi").init()
local multi, thread = require("multi"):init()
if multi.integration then
return {
@ -32,7 +32,7 @@ if multi.integration then
}
end
local GLOBAL, THREAD = require("multi.integration.pesudoManager.threads"):init()
local GLOBAL, THREAD = require("multi.integration.pesudoManager.threads").init(thread)
function multi:canSystemThread() -- We are emulating system threading
return true
@ -41,6 +41,7 @@ end
function multi:getPlatform()
return "pesudo"
end
local function split(str)
local tab = {}
for word in string.gmatch(str, '([^,]+)') do
@ -48,13 +49,16 @@ local function split(str)
end
return tab
end
THREAD.newFunction=thread.newFunction
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)
local id = 0
function multi:newSystemThread(name,func,...)
GLOBAL["$THREAD_NAME"] = name
GLOBAL["$__THREADNAME__"] = name
GLOBAL["$THREAD_ID"] = id
--GLOBAL["$thread"] = thread
GLOBAL["$thread"] = thread
local env = {
GLOBAL = GLOBAL,
THREAD = THREAD,
@ -64,21 +68,28 @@ function multi:newSystemThread(name,func,...)
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)
local th = thread:newISOThread(name,func,env,...)
id = id + 1
return th
end
THREAD.newSystemThread = multi.newSystemThread
-- 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!")
function THREAD:newFunction(func,holdme)
return thread:newFunctionBase(function(...)
return multi:newSystemThread("TempSystemThread",func,...)
end,holdme)()
end
multi.print("Integrated Pesudo Threading!")
multi.integration = {} -- for module creators
multi.integration.GLOBAL = GLOBAL
multi.integration.THREAD = THREAD

View File

@ -1,7 +1,7 @@
--[[
MIT License
Copyright (c) 2020 Ryan Ward
Copyright (c) 2022 Ryan Ward
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@ -21,6 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]]
local function getOS()
if package.config:sub(1, 1) == "\\" then
return "windows"
@ -28,7 +29,8 @@ local function getOS()
return "unix"
end
end
local function INIT(env)
local function INIT(thread)
local THREAD = {}
local GLOBAL = {}
THREAD.Priority_Core = 3
@ -38,24 +40,29 @@ local function INIT(env)
THREAD.Priority_Below_Normal = -1
THREAD.Priority_Low = -2
THREAD.Priority_Idle = -3
function THREAD.set(name, val)
GLOBAL[name] = val
end
function THREAD.get(name)
return GLOBAL[name]
end
function THREAD.waitFor(name)
print("Waiting",thread)
return thread.hold(function() return GLOBAL[name] end)
end
if getOS() == "windows" then
THREAD.__CORES = tonumber(os.getenv("NUMBER_OF_PROCESSORS"))
else
THREAD.__CORES = tonumber(io.popen("nproc --all"):read("*n"))
end
function THREAD.getCores()
return THREAD.__CORES
end
function THREAD.getConsole()
local c = {}
function c.print(...)
@ -66,31 +73,38 @@ local function INIT(env)
end
return c
end
function THREAD.getThreads()
return {}--GLOBAL.__THREADS__
end
THREAD.pushStatus = thread.pushStatus
if os.getOS() == "windows" then
THREAD.__CORES = tonumber(os.getenv("NUMBER_OF_PROCESSORS"))
else
THREAD.__CORES = tonumber(io.popen("nproc --all"):read("*n"))
end
function THREAD.kill()
error("Thread was killed!")
end
function THREAD.getName()
return GLOBAL["$THREAD_NAME"]
end
function THREAD.getID()
return GLOBAL["$THREAD_ID"]
end
function THREAD.sleep(n)
thread.sleep(n)
end
function THREAD.hold(n)
return thread.hold(n)
end
THREAD.sleep = thread.sleep
THREAD.hold = thread.hold
return GLOBAL, THREAD
end
return {init = function()
return INIT()
return {init = function(thread)
return INIT(thread)
end}

View File

@ -0,0 +1,39 @@
package = "multi"
version = "15.2-0"
source = {
url = "git://github.com/rayaman/multi.git",
tag = "v15.2.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 documentation.
]],
homepage = "https://github.com/rayaman/multi",
license = "MIT"
}
dependencies = {
"lua >= 5.1"
}
build = {
type = "builtin",
modules = {
["multi"] = "multi/init.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.lovrManager"] = "multi/integration/lovrManager/init.lua",
--["multi.integration.lovrManager.extensions"] = "multi/integration/lovrManager/extensions.lua",
--["multi.integration.lovrManager.threads"] = "multi/integration/lovrManager/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.threading"] = "multi/integration/threading.lua",
--["multi.integration.networkManager"] = "multi/integration/networkManager.lua",
}
}

200
test.lua
View File

@ -1,113 +1,111 @@
--package.path = "./?/init.lua;"..package.path
multi,thread = require("multi"):init()
package.path = "./?/init.lua;"..package.path
multi, thread = require("multi"):init{print=true}
GLOBAL, THREAD = require("multi.integration.threading"):init()
func = thread:newFunction(function(count)
local a = 0
while true do
a = a + 1
thread.sleep(.5)
thread.pushStatus(a,count)
if a == count then break end
end
return "Done"
-- Using a system thread, but both system and local threads support this!
-- Don't worry if you don't have lanes or love2d. PesudoThreading will kick in to emulate the threading features if you do not have access to system threading.
func = THREAD:newFunction(function(count)
print("Starting Status test: ",count)
local a = 0
while true do
a = a + 1
THREAD.sleep(.1)
-- Push the status from the currently running threaded function to the main thread
THREAD.pushStatus(a,count)
if a == count then break end
end
return "Done"
end)
multi:newThread("test",function()
local ret = func(10)
local ret2 = func(15)
local ret3 = func(20)
ret.OnStatus(function(part,whole)
print("Ret1: ",math.ceil((part/whole)*1000)/10 .."%")
end)
ret2.OnStatus(function(part,whole)
print("Ret2: ",math.ceil((part/whole)*1000)/10 .."%")
end)
ret3.OnStatus(function(part,whole)
print("Ret3: ",math.ceil((part/whole)*1000)/10 .."%")
end)
thread.hold(ret2.OnReturn + ret.OnReturn + ret3.OnReturn)
print("Function Done!")
os.exit()
end)
--GLOBAL,THREAD = require("multi.integration.threading"):init() -- Auto detects your environment and uses what's available
func2 = thread:newFunction(function()
thread.sleep(3)
print("Hello World!")
return true
end,true) -- set holdme to true
func2:holdMe(false) -- reset holdme to false
print("Calling func...")
print(func2())
test = thread:newFunction(function(a,b)
thread.sleep(1)
return a,b
end)
print(test(1,2).connect(function(...)
print(...)
end))
test:Pause()
print(test(1,2).connect(function(...)
print(...)
end))
test:Resume()
print(test(1,2).connect(function(...)
print(...)
end))
test = thread:newFunction(function()
return 1,2,nil,3,4,5,6,7,8,9
end,true)
print(test())
multi:newThread("testing",function()
print("#Test = ",test())
print(thread.hold(function()
print("Hello!")
return false
end,{
interval = 2,
cycles = 3
})) -- End result, 3 attempts within 6 seconds. If still false then timeout
print("held")
thread:newThread("test",function()
local ret = func(10)
ret.OnStatus(function(part,whole)
print("Ret1: ",math.ceil((part/whole)*1000)/10 .."%")
end)
print("TEST",func(5).wait())
-- The results from the OnReturn connection is passed by thread.hold
print("Status:",thread.hold(ret.OnReturn))
print("Function Done!")
end).OnError(function(...)
print(...)
print("Error:",...)
end)
sandbox = multi:newProcessor("Test Processor")
sandbox:newTLoop(function()
print("testing...")
end,1)
test2 = multi:newTLoop(function()
print("testing2...")
end,1)
sandbox:newThread("Test Thread",function()
local a = 0
while true do
thread.sleep(1)
a = a + 1
print("Thread Test: ".. multi.getCurrentProcess().Name)
if a == 10 then
sandbox.Stop()
end
end
end).OnError(function(...)
print(...)
local ret = func(10)
local ret2 = func(15)
local ret3 = func(20)
local s1,s2,s3 = 0,0,0
ret.OnError(function(...)
print("Error:",...)
end)
ret2.OnError(function(...)
print("Error:",...)
end)
ret3.OnError(function(...)
print("Error:",...)
end)
ret.OnStatus(function(part,whole)
s1 = math.ceil((part/whole)*1000)/10
print(s1)
end)
ret2.OnStatus(function(part,whole)
s2 = math.ceil((part/whole)*1000)/10
print(s2)
end)
ret3.OnStatus(function(part,whole)
s3 = math.ceil((part/whole)*1000)/10
print(s3)
end)
multi:newThread("Test Thread",function()
while true do
thread.sleep(1)
print("Thread Test: ".. multi.getCurrentProcess().Name)
end
end).OnError(function(...)
print(...)
loop = multi:newTLoop()
function loop:testing()
print("testing haha")
end
loop:Set(1)
t = loop:OnLoop(function()
print("Looping...")
end):testing()
local proc = multi:newProcessor("Test")
local proc2 = multi:newProcessor("Test2")
local proc3 = proc2:newProcessor("Test3")
proc.Start()
proc2.Start()
proc3.Start()
proc:newThread("TestThread_1",function()
while true do
thread.sleep(1)
end
end)
proc:newThread("TestThread_2",function()
while true do
thread.sleep(1)
end
end)
proc2:newThread("TestThread_3",function()
while true do
thread.sleep(1)
end
end)
sandbox.Start()
thread:newThread(function()
thread.sleep(1)
local tasks = multi:getStats()
for i,v in pairs(tasks) do
print("Process: " ..i.. "\n\tTasks:")
for ii,vv in pairs(v.tasks) do
print("\t\t"..vv:getName())
end
print("\tThreads:")
for ii,vv in pairs(v.threads) do
print("\t\t"..vv:getName())
end
end
thread.sleep(10) -- Wait 10 seconds then kill the process!
os.exit()
end)
multi:mainloop()

51
test3.lua Normal file
View File

@ -0,0 +1,51 @@
package.path = "./?.lua;?/init.lua;"..package.path
local multi,thread = require("multi"):init{print=true}
--local GLOBAL,THREAD = require("multi.integration.lanesManager"):init()
-- func = THREAD:newFunction(function(a,b,c)
-- print("Hello Thread!",a,b,c)
-- return 1,2,3
-- end)
-- func2 = THREAD:newFunction(function(a,b,c)
-- print("Hello Thread2!",a,b,c)
-- THREAD.sleep(1)
-- return 10,11,12
-- end)
-- multi:newThread("Test thread",function()
-- handler = func(4,5,6)
-- handler2 = func2(7,8,9)
-- thread.hold(handler.OnReturn + handler2.OnReturn)
-- print("Function Done",handler.getReturns())
-- print("Function Done",handler2.getReturns())
-- end)
-- multi:benchMark(1):OnBench(function(sec,steps)
-- print("Steps:",steps)
-- os.exit()
-- end)
print("Running benchmarks! ",_VERSION)
local sleep_for = 1
local a = 0
local c = 1
local function bench(t,step)
a = a + step
c = c + 1
os.exit()
end
--multi:benchMark(sleep_for,multi.Priority_Idle,"Idle:"):OnBench(bench)
--multi:benchMark(sleep_for,multi.Priority_Very_Low,"Very Low:"):OnBench(bench)
--multi:benchMark(sleep_for,multi.Priority_Low,"Low:"):OnBench()
--multi:benchMark(sleep_for,multi.Priority_Below_Normal,"Below Normal:"):OnBench(bench)
--multi:benchMark(sleep_for,multi.Priority_Normal,"Normal:"):OnBench(bench)
--multi:benchMark(sleep_for,multi.Priority_Above_Normal,"Above Normal:"):OnBench(bench)
--multi:benchMark(sleep_for,multi.Priority_High,"High:"):OnBench(bench)
--multi:benchMark(sleep_for,multi.Priority_Very_High,"Very High:"):OnBench(bench)
multi:benchMark(sleep_for,multi.Priority_Core,"Core:"):OnBench(bench)
multi.OnExit(function()
print("Total: ".. a)
end)
multi:mainloop()

View File

@ -1,3 +0,0 @@
return function objectTests(multi,thread)
print("Testing Alarms!")
end

View File

@ -1,4 +1,9 @@
package.path="../?.lua;../?/init.lua;../?.lua;../?/?/init.lua;"..package.path
if os.getenv("LOCAL_LUA_DEBUGGER_VSCODE") == "1" then
package.path="multi/?.lua;multi/?/init.lua;multi/?.lua;multi/?/?/init.lua;"..package.path
require("lldebugger").start()
else
package.path="./?.lua;../?/init.lua;../?.lua;../?/?/init.lua;"..package.path
end
--[[
This file runs all tests.
Format:
@ -15,7 +20,157 @@ package.path="../?.lua;../?/init.lua;../?.lua;../?/?/init.lua;"..package.path
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.
]]
local multi, thread = require("multi"):init()
function runTest(path)
local multi, thread = require("multi"):init{print=true}--{priority=true}
local good = false
local proc = multi:newProcessor("Test",true)
proc:newAlarm(3):OnRing(function()
good = true
end)
runTest = thread:newFunction(function()
local alarms,tsteps,steps,loops,tloops,updaters,events=false,0,0,0,0,0,false
print("Testing Basic Features. If this fails most other features will probably not work!")
proc:newAlarm(2):OnRing(function(a)
alarms = true
a:Destroy()
end)
proc:newTStep(1,10,1,.1):OnStep(function(t)
tsteps = tsteps + 1
end).OnEnd(function(step)
step:Destroy()
end)
proc:newStep(1,10):OnStep(function(s)
steps = steps + 1
end).OnEnd(function(step)
step:Destroy()
end)
local loop = proc:newLoop(function(l)
loops = loops + 1
end)
proc:newTLoop(function(t)
tloops = tloops + 1
end,.1)
local updater = proc:newUpdater(1):OnUpdate(function()
updaters = updaters + 1
end)
local event = proc:newEvent(function()
return alarms
end)
event.OnEvent(function(evnt)
evnt:Destroy()
events = true
print("Alarms: Ok")
print("Events: Ok")
if tsteps == 10 then print("TSteps: Ok") else print("TSteps: Bad!") end
if steps == 10 then print("Steps: Ok") else print("Steps: Bad!") end
if loops > 100 then print("Loops: Ok") else print("Loops: Bad!") end
if tloops > 10 then print("TLoops: Ok") else print("TLoops: Bad!") end
if updaters > 100 then print("Updaters: Ok") else print("Updaters: Bad!") end
end)
thread.hold(event.OnEvent)
print("Starting Connection and Thread tests!")
func = thread:newFunction(function(count)
print("Starting Status test: ",count)
local a = 0
while true do
a = a + 1
thread.sleep(.1)
thread.pushStatus(a,count)
if a == count then break end
end
return "Done"
end)
local ret = func(10)
local ret2 = func(15)
local ret3 = func(20)
local s1,s2,s3 = 0,0,0
ret.OnError(function(...)
print("Func 1:",...)
end)
ret2.OnError(function(...)
print("Func 2:",...)
end)
ret3.OnError(function(...)
print("Func 3:",...)
end)
ret.OnStatus(function(part,whole)
s1 = math.ceil((part/whole)*1000)/10
end)
ret2.OnStatus(function(part,whole)
s2 = math.ceil((part/whole)*1000)/10
end)
ret3.OnStatus(function(part,whole)
s3 = math.ceil((part/whole)*1000)/10
end)
ret.OnReturn(function()
print("Done 1")
end)
ret2.OnReturn(function()
print("Done 2")
end)
ret3.OnReturn(function()
print("Done 3")
end)
local err, timeout = thread.hold(ret.OnReturn + ret2.OnReturn + ret3.OnReturn)
if s1 == 100 and s2 == 100 and s3 == 100 then
print("Threads: Ok")
else
print("Threads OnStatus or thread.hold(conn) Error!")
end
if timeout then
print("Threads or Connection Error!")
else
print("Connection Test 1: Ok")
end
conn1 = proc:newConnection()
conn2 = proc:newConnection()
conn3 = proc:newConnection()
local c1,c2,c3,c4 = false,false,false,false
local a = conn1(function()
c1 = true
end)
local b = conn2(function()
c2 = true
end)
local c = conn3(function()
c3 = true
end)
local d = conn3(function()
c4 = true
end)
conn1:Fire()
conn2:Fire()
conn3:Fire()
if c1 and c2 and c3 and c4 then
print("Connection Test 2: Ok")
else
print("Connection Test 2: Error")
end
c3 = false
c4 = false
d:Destroy()
conn3:Fire()
if c3 and not(c4) then
print("Connection Test 3: Ok")
else
print("Connection Test 3: Error removing connection")
end
os.exit() -- End of tests
end)
runTest().OnError(function(...)
print("Error: Something went wrong with the test!")
print(...)
os.exit(1)
end)
print("Pumping proc")
while true do
proc.run()
end