Compare commits
No commits in common. "master" and "v15.3.0" have entirely different histories.
25
.github/workflows/love.yml
vendored
25
.github/workflows/love.yml
vendored
@ -1,25 +0,0 @@
|
|||||||
name: Build & Run tests Love2d
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ master ]
|
|
||||||
pull_request:
|
|
||||||
branches: [ master ]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
runs-on: "ubuntu-latest"
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
with:
|
|
||||||
submodules: recursive
|
|
||||||
- name: Install love2d
|
|
||||||
run: |
|
|
||||||
sudo apt install fuse
|
|
||||||
wget https://github.com/love2d/love/releases/download/11.4/love-11.4-x86_64.AppImage -O love.AppImage
|
|
||||||
sudo chmod +x love.AppImage
|
|
||||||
- name: Run Tests
|
|
||||||
run: |
|
|
||||||
./love.AppImage tests
|
|
||||||
41
.github/workflows/nix_ci.yml
vendored
41
.github/workflows/nix_ci.yml
vendored
@ -1,41 +0,0 @@
|
|||||||
name: Build & Run tests Ubuntu
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ master ]
|
|
||||||
pull_request:
|
|
||||||
branches: [ master ]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
build-type: [Release] # Debug
|
|
||||||
lua: ["lua 5.1", "lua 5.2", "lua 5.3", "lua 5.4", "luajit 2.1.0-beta3"]
|
|
||||||
os: ["ubuntu-latest"]
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
with:
|
|
||||||
submodules: recursive
|
|
||||||
|
|
||||||
- uses: actions/setup-python@v2
|
|
||||||
with:
|
|
||||||
python-version: '3.10'
|
|
||||||
|
|
||||||
- name: Setup env
|
|
||||||
run: |
|
|
||||||
pip install hererocks
|
|
||||||
hererocks lua-pkg --${{ matrix.lua }} -rlatest
|
|
||||||
|
|
||||||
- name: Install lanes and multi
|
|
||||||
run: |
|
|
||||||
source ${{github.workspace}}/lua-pkg/bin/activate
|
|
||||||
luarocks install lanes
|
|
||||||
luarocks install rockspecs/multi-16.0-0.rockspec
|
|
||||||
|
|
||||||
- name: Run Tests
|
|
||||||
run: |
|
|
||||||
source ${{github.workspace}}/lua-pkg/bin/activate
|
|
||||||
lua tests/runtests.lua
|
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,2 @@
|
|||||||
*.code-workspace
|
*.code-workspace
|
||||||
lua5.4/*
|
lua5.4/*
|
||||||
test.lua
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
# NTHREAD Namespace
|
|
||||||
- [ ] NTHREAD.set(name, val)
|
|
||||||
- [ ] NTHREAD.get(name, val)
|
|
||||||
- [ ] NTHREAD.waitFor(name)
|
|
||||||
- [ ] NTHREAD.getCores()*
|
|
||||||
- [ ] NTHREAD.getConsole()
|
|
||||||
- [ ] NTHREAD.getThreads()
|
|
||||||
- [ ] NTHREAD.kill()
|
|
||||||
- [ ] NTHREAD.getName()
|
|
||||||
- [ ] NTHREAD.getID()
|
|
||||||
- [ ] NTHREAD.pushStatus(...)
|
|
||||||
- [ ] NTHREAD.sleep(n)
|
|
||||||
- [ ] NTHREAD.hold(n)
|
|
||||||
- [ ] NTHREAD.setENV(env)
|
|
||||||
- [ ] NTHREAD.getENV()
|
|
||||||
|
|
||||||
# Extensions
|
|
||||||
- [ ] multi:newNetworkThreadedQueue(name)
|
|
||||||
- [ ] multi:newNetworkThreadedTable(name)
|
|
||||||
- [ ] multi:newNetworkThreadedJobQueue(n)
|
|
||||||
- [ ] multi:newNetworkThreadedConnection(name)
|
|
||||||
|
|
||||||
# Core
|
|
||||||
- [ ] NTHREAD:newFunction(func, holdme)
|
|
||||||
- [ ] NTHREAD:newNetworkThread(name, func, ...)
|
|
||||||
- [ ] mulit:newNetworkThread(name, func, ...)
|
|
||||||
28
README.md
28
README.md
@ -1,4 +1,8 @@
|
|||||||
# Multi Version: 16.0.1 - Bug fix
|
# Multi Version: 15.3.0 A world of Connections
|
||||||
|
**Key Changes**
|
||||||
|
- SystemThreadedConnections
|
||||||
|
- Restructured the directory structure of the repo (Allows for keeping multi as a submodule and being able to require it as is)
|
||||||
|
- Bug fixes
|
||||||
|
|
||||||
Found an issue? Please [submit it](https://github.com/rayaman/multi/issues) and someone will look into it!
|
Found an issue? Please [submit it](https://github.com/rayaman/multi/issues) and someone will look into it!
|
||||||
|
|
||||||
@ -6,7 +10,7 @@ My multitasking library for lua. It is a pure lua binding, with exceptions of th
|
|||||||
|
|
||||||
</br>
|
</br>
|
||||||
|
|
||||||
Progress is being made in [v16.1.0](https://github.com/rayaman/multi/tree/v16.1.0)
|
Progress is being made in [v15.4.0](https://github.com/rayaman/multi/tree/v15.4.0)
|
||||||
---
|
---
|
||||||
|
|
||||||
</br>
|
</br>
|
||||||
@ -14,20 +18,10 @@ Progress is being made in [v16.1.0](https://github.com/rayaman/multi/tree/v16.1.
|
|||||||
INSTALLING
|
INSTALLING
|
||||||
----------
|
----------
|
||||||
Link to optional dependencies:
|
Link to optional dependencies:
|
||||||
- [lanes](https://github.com/LuaLanes/lanes) `luarocks install lanes`
|
- [lanes](https://github.com/LuaLanes/lanes)
|
||||||
|
|
||||||
- [chronos](https://github.com/ldrumm/chronos) `luarocks install chronos`
|
|
||||||
|
|
||||||
- [love2d](https://love2d.org/)
|
- [love2d](https://love2d.org/)
|
||||||
|
|
||||||
When using love2d add multi:uManager() or any processor to love.update()
|
|
||||||
|
|
||||||
```lua
|
|
||||||
function love.update(dt)
|
|
||||||
multi:uManager()
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
To install copy the multi folder into your environment and you are good to go</br>
|
To install copy the multi folder into your environment and you are good to go</br>
|
||||||
If you want to use the system threads, then you'll need to install lanes or love2d game engine!
|
If you want to use the system threads, then you'll need to install lanes or love2d game engine!
|
||||||
|
|
||||||
@ -42,18 +36,12 @@ https://discord.gg/U8UspuA
|
|||||||
|
|
||||||
Planned features/TODO
|
Planned features/TODO
|
||||||
---------------------
|
---------------------
|
||||||
- [x] ~~Create test suite (In progress, mostly done)~~
|
- [ ] Create test suite (In progress, mostly done)
|
||||||
- [ ] Network Parallelism rework
|
- [ ] Network Parallelism rework
|
||||||
|
|
||||||
Usage: [Check out the documentation for more info](https://github.com/rayaman/multi/blob/master/Documentation.md)
|
Usage: [Check out the documentation for more info](https://github.com/rayaman/multi/blob/master/Documentation.md)
|
||||||
-----
|
-----
|
||||||
|
|
||||||
You can run tests in 2 ways:
|
|
||||||
```
|
|
||||||
lua tests/runtests.lua (Runs all tests, attempts to use lanes)
|
|
||||||
love tests (Runs all tests in love2d env)
|
|
||||||
```
|
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
local multi, thread = require("multi"):init()
|
local multi, thread = require("multi"):init()
|
||||||
GLOBAL, THREAD = require("multi.integration.threading"):init()
|
GLOBAL, THREAD = require("multi.integration.threading"):init()
|
||||||
|
|||||||
632
docs/changes.md
632
docs/changes.md
@ -1,637 +1,9 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
Table of contents
|
Table of contents
|
||||||
---
|
---
|
||||||
[Update 16.0.1 - Bug fix](#update-1531---bug-fix)</br>
|
|
||||||
[Update 16.0.0 - Connecting the dots](#update-1600---getting-the-priorities-straight)</br>
|
|
||||||
[Update 15.3.1 - Bug fix](#update-1531---bug-fix)</br>
|
|
||||||
[Update 15.3.0 - A world of connections](#update-1530---a-world-of-connections)</br>
|
[Update 15.3.0 - A world of connections](#update-1530---a-world-of-connections)</br>
|
||||||
[Update 15.2.1 - Bug fix](#update-1521---bug-fix)</br>
|
[Update 15.2.1 - Bug fix](#update-1521---bug-fix)</br>
|
||||||
[Update 15.2.0 - Upgrade Complete](#update-1520---upgrade-complete)</br>
|
[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.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 16.0.1 - Bug fix
|
|
||||||
Fixed
|
|
||||||
---
|
|
||||||
- thread.pushStatus() wasn't properly working when forwarding events from THREAD.pushStatus OnStatus connection. This bug also caused stack overflow errors with the following code
|
|
||||||
```lua
|
|
||||||
func = thread:newFunction(function()
|
|
||||||
for i=1,10 do
|
|
||||||
thread.sleep(1)
|
|
||||||
thread.pushStatus(i)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
func2 = thread:newFunction(function()
|
|
||||||
local ref = func()
|
|
||||||
ref.OnStatus(function(num)
|
|
||||||
-- do stuff with this data
|
|
||||||
|
|
||||||
thread.pushStatus(num*2) -- Technically this is not ran within a thread. This is ran outside of a thread inside the thread handler.
|
|
||||||
end)
|
|
||||||
end)
|
|
||||||
|
|
||||||
local handler = func2()
|
|
||||||
handler.OnStatus(function(num)
|
|
||||||
print(num)
|
|
||||||
end)
|
|
||||||
|
|
||||||
multi:mainloop()
|
|
||||||
```
|
|
||||||
|
|
||||||
# Update 16.0.0 - Getting the priorities straight
|
|
||||||
|
|
||||||
## Added New Integration: **priorityManager**
|
|
||||||
|
|
||||||
Allows the user to have multi auto set priorities (Requires chronos). Also adds the functionality to create your own runners (multi:mainloop(), multi:umanager()) that you can set using the priority manager. Even if you do not have `chronos` installed all other features will still work!
|
|
||||||
- Allows the creation of custom priorityManagers
|
|
||||||
|
|
||||||
Added
|
|
||||||
---
|
|
||||||
- thread.defer(func) -- When using a co-routine thread or co-routine threaded function, defer will call it's function at the end of the the threads life through normal execution or an error. In the case of a threaded function, when the function returns or errors.
|
|
||||||
- multi:setTaskDelay(delay), Tasks which are now tied to a processor can have an optional delay between the execution between each task. Useful perhaps for rate limiting. Without a delay all grouped tasks will be handled in one step. `delay` can be a function as well and will be processed as if thread.hold was called.
|
|
||||||
- processor's now have a boost function which causes it to run its processes the number of times specified in the `boost(count)` function
|
|
||||||
- thread.hold will now use a custom hold method for objects with a `Hold` method. This is called like `obj:Hold(opt)`. The only argument passed is the optional options table that thread.hold can pass. There is an exception for connection objects. While they do contain a Hold method, the Hold method isn't used and is there for proxy objects, though they can be used in non proxy/thread situations. Hold returns all the arguments that the connection object was fired with.
|
|
||||||
- shared_table = STP:newSharedTable(tbl_name) -- Allows you to create a shared table that all system threads in a process have access to. Returns a reference to that table for use on the main thread. Sets `_G[tbl_name]` on the system threads so you can access it there.
|
|
||||||
```lua
|
|
||||||
package.path = "?/init.lua;?.lua;"..package.path
|
|
||||||
|
|
||||||
multi, thread = require("multi"):init({print=true})
|
|
||||||
THREAD, GLOBAL = require("multi.integration.lanesManager"):init()
|
|
||||||
|
|
||||||
stp = multi:newSystemThreadedProcessor(8)
|
|
||||||
|
|
||||||
local shared = stp:newSharedTable("shared")
|
|
||||||
|
|
||||||
shared["test"] = "We work!"
|
|
||||||
|
|
||||||
for i=1,5 do
|
|
||||||
-- There is a bit of overhead when creating threads on a process. Takes some time, mainly because we are creating a proxy.
|
|
||||||
stp:newThread(function()
|
|
||||||
local multi, thread = require("multi"):init()
|
|
||||||
local shared = _G["shared"]
|
|
||||||
print(THREAD_NAME, shared.test, shared.test2)
|
|
||||||
multi:newAlarm(.5):OnRing(function() -- Play around with the time. System threads do not create instantly. They take quite a bit of time to get spawned.
|
|
||||||
print(THREAD_NAME, shared.test, shared.test2)
|
|
||||||
end)
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
shared["test2"] = "We work!!!"
|
|
||||||
|
|
||||||
multi:mainloop()
|
|
||||||
```
|
|
||||||
|
|
||||||
Output:
|
|
||||||
```
|
|
||||||
INFO: Integrated Lanes Threading!
|
|
||||||
STJQ_cPXT8GOx We work! nil
|
|
||||||
STJQ_hmzdYDVr We work! nil
|
|
||||||
STJQ_3lwMhnfX We work! nil
|
|
||||||
STJQ_hmzdYDVr We work! nil
|
|
||||||
STJQ_cPXT8GOx We work! nil
|
|
||||||
STJQ_cPXT8GOx We work! We work!!!
|
|
||||||
STJQ_hmzdYDVr We work! We work!!!
|
|
||||||
STJQ_3lwMhnfX We work! We work!!!
|
|
||||||
STJQ_hmzdYDVr We work! We work!!!
|
|
||||||
STJQ_cPXT8GOx We work! We work!!!
|
|
||||||
```
|
|
||||||
|
|
||||||
- multi:chop(obj) -- We cannot directly interact with a local object on lanes, so we chop the object and set some globals on the thread side. Should use like: `mulit:newProxy(multi:chop(multi:newThread(function() ... end)))`
|
|
||||||
- multi:newProxy(ChoppedObject) -- Creates a proxy object that allows you to interact with an object on a thread
|
|
||||||
|
|
||||||
**Note:** Objects with __index=table do not work with the proxy object! The object must have that function in it's own table for proxy to pick it up and have it work properly. Connections on a proxy allow you to subscribe to an event on the thread side of things. The function that is being connected to happens on the thread!
|
|
||||||
- multi:newSystemThreadedProcessor(name) -- Works like newProcessor(name) each object created returns a proxy object that you can use to interact with the objects on the system thread
|
|
||||||
```lua
|
|
||||||
package.path = "?/init.lua;?.lua;"..package.path
|
|
||||||
|
|
||||||
multi, thread = require("multi"):init({print=true})
|
|
||||||
THREAD, GLOBAL = require("multi.integration.lanesManager"):init()
|
|
||||||
|
|
||||||
stp = multi:newSystemThreadedProcessor("Test STP")
|
|
||||||
|
|
||||||
alarm = stp:newAlarm(3)
|
|
||||||
|
|
||||||
alarm._OnRing:Connect(function(alarm)
|
|
||||||
print("Hmm...", THREAD_NAME)
|
|
||||||
end)
|
|
||||||
```
|
|
||||||
Output:
|
|
||||||
```
|
|
||||||
Hmm... SystemThreadedJobQueue_A5tp
|
|
||||||
```
|
|
||||||
Internally the SystemThreadedProcessor uses a JobQueue to handle things. The proxy function allows you to interact with these objects as if they were on the main thread, though there actions are carried out on the main thread.
|
|
||||||
|
|
||||||
Proxies can also be shared between threads, just remember to use proxy:getTransferable() before transferring and proxy:init() on the other end. (We need to avoid copying over coroutines)
|
|
||||||
|
|
||||||
The work done with proxies negates the usage of multi:newSystemThreadedConnection(), the only difference is you lose the metatables from connections.
|
|
||||||
|
|
||||||
You cannot connect directly to a proxy connection on the non proxy thread, you can however use proxy_conn:Hold() or thread.hold(proxy_conn) to emulate this, see below.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
package.path = "?/init.lua;?.lua;"..package.path
|
|
||||||
|
|
||||||
multi, thread = require("multi"):init({print=true, warn=true, error=true})
|
|
||||||
THREAD, GLOBAL = require("multi.integration.lanesManager"):init()
|
|
||||||
|
|
||||||
stp = multi:newSystemThreadedProcessor(8)
|
|
||||||
|
|
||||||
tloop = stp:newTLoop(nil, 1)
|
|
||||||
|
|
||||||
multi:newSystemThread("Testing proxy copy",function(tloop)
|
|
||||||
local function tprint (tbl, indent)
|
|
||||||
if not indent then indent = 0 end
|
|
||||||
for k, v in pairs(tbl) do
|
|
||||||
formatting = string.rep(" ", indent) .. k .. ": "
|
|
||||||
if type(v) == "table" then
|
|
||||||
print(formatting)
|
|
||||||
tprint(v, indent+1)
|
|
||||||
else
|
|
||||||
print(formatting .. tostring(v))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local multi, thread = require("multi"):init()
|
|
||||||
tloop = tloop:init()
|
|
||||||
print("tloop type:",tloop.Type)
|
|
||||||
print("Testing proxies on other threads")
|
|
||||||
thread:newThread(function()
|
|
||||||
while true do
|
|
||||||
thread.hold(tloop.OnLoop)
|
|
||||||
print(THREAD_NAME,"Loopy")
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
tloop.OnLoop(function(a)
|
|
||||||
print(THREAD_NAME, "Got loop...")
|
|
||||||
end)
|
|
||||||
multi:mainloop()
|
|
||||||
end, tloop:getTransferable()).OnError(multi.error)
|
|
||||||
|
|
||||||
print("tloop", tloop.Type)
|
|
||||||
|
|
||||||
thread:newThread(function()
|
|
||||||
print("Holding...")
|
|
||||||
thread.hold(tloop.OnLoop)
|
|
||||||
print("Held on proxied no proxy connection 1")
|
|
||||||
end).OnError(print)
|
|
||||||
|
|
||||||
thread:newThread(function()
|
|
||||||
tloop.OnLoop:Hold()
|
|
||||||
print("held on proxied no proxy connection 2")
|
|
||||||
end)
|
|
||||||
|
|
||||||
tloop.OnLoop(function()
|
|
||||||
print("OnLoop",THREAD_NAME)
|
|
||||||
end)
|
|
||||||
|
|
||||||
thread:newThread(function()
|
|
||||||
while true do
|
|
||||||
tloop.OnLoop:Hold()
|
|
||||||
print("OnLoop",THREAD_NAME)
|
|
||||||
end
|
|
||||||
end).OnError(multi.error)
|
|
||||||
|
|
||||||
multi:mainloop()
|
|
||||||
```
|
|
||||||
Output:
|
|
||||||
```
|
|
||||||
INFO: Integrated Lanes Threading! 1
|
|
||||||
tloop proxy
|
|
||||||
Holding...
|
|
||||||
tloop type: proxy
|
|
||||||
Testing proxies on other threads
|
|
||||||
OnLoop STJQ_W9SZGB6Y
|
|
||||||
STJQ_W9SZGB6Y Got loop...
|
|
||||||
OnLoop MAIN_THREAD
|
|
||||||
Testing proxy copy Loopy
|
|
||||||
Held on proxied no proxy connection 1
|
|
||||||
held on proxied no proxy connection 2
|
|
||||||
OnLoop STJQ_W9SZGB6Y
|
|
||||||
STJQ_W9SZGB6Y Got loop...
|
|
||||||
Testing proxy copy Loopy
|
|
||||||
OnLoop MAIN_THREAD
|
|
||||||
OnLoop STJQ_W9SZGB6Y
|
|
||||||
STJQ_W9SZGB6Y Got loop...
|
|
||||||
|
|
||||||
... (Will repeat every second)
|
|
||||||
|
|
||||||
Testing proxy copy Loopy
|
|
||||||
OnLoop MAIN_THREAD
|
|
||||||
OnLoop STJQ_W9SZGB6Y
|
|
||||||
STJQ_W9SZGB6Y Got loop...
|
|
||||||
|
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
The proxy version can only subscribe to events on the proxy thread, which means that connection metamethods will not work with the proxy version (`_OnRing` on the non proxy thread side), but the (`OnRing`) version will work. Cleverly handling the proxy thread and the non proxy thread will allow powerful connection logic. Also this is not a full system threaded connection. **Proxies should only be used between 2 threads!** To keep things fast I'm using simple queues to transfer data. There is no guarantee that things will work!
|
|
||||||
|
|
||||||
Currently supporting:
|
|
||||||
- proxyLoop = STP:newLoop(...)
|
|
||||||
- proxyTLoop = STP:newTLoop(...)
|
|
||||||
- proxyUpdater = STP:newUpdater(...)
|
|
||||||
- proxyEvent = STP:newEvent(...)
|
|
||||||
- proxyAlarm = STP:newAlarm(...)
|
|
||||||
- proxyStep = STP:newStep(...)
|
|
||||||
- proxyTStep = STP:newTStep(...)
|
|
||||||
- proxyThread = STP:newThread(...)
|
|
||||||
- proxyService = STP:newService(...)
|
|
||||||
- threadedFunction = STP:newFunction(...)
|
|
||||||
|
|
||||||
Unique:
|
|
||||||
- STP:newSharedTable(name)
|
|
||||||
|
|
||||||
</br>
|
|
||||||
|
|
||||||
**STP** functions (The ones above) cannot be called within coroutine based thread when using lanes. This causes thread.hold to break. Objects(proxies) returned by these functions are ok to use in coroutine based threads!
|
|
||||||
```lua
|
|
||||||
package.path = "?/init.lua;?.lua;"..package.path
|
|
||||||
|
|
||||||
multi, thread = require("multi"):init({print=true})
|
|
||||||
THREAD, GLOBAL = require("multi.integration.lanesManager"):init()
|
|
||||||
|
|
||||||
stp = multi:newSystemThreadedProcessor()
|
|
||||||
|
|
||||||
alarm = stp:newAlarm(3)
|
|
||||||
|
|
||||||
alarm.OnRing:Connect(function(alarm)
|
|
||||||
print("Hmm...", THREAD_NAME)
|
|
||||||
end)
|
|
||||||
|
|
||||||
thread:newThread(function()
|
|
||||||
print("Holding...")
|
|
||||||
local a = thread.hold(alarm.OnRing) -- it works :D
|
|
||||||
print("We work!")
|
|
||||||
end)
|
|
||||||
|
|
||||||
multi:mainloop()
|
|
||||||
```
|
|
||||||
|
|
||||||
- multi.OnObjectDestroyed(func(obj, process)) now supplies obj, process just like OnObjectCreated
|
|
||||||
- thread:newProcessor(name) -- works mostly like a normal process, but all objects are wrapped within a thread. So if you create a few loops, you can use thread.hold() call threaded functions and wait and use all features that using coroutines provide.
|
|
||||||
- multi.Processors:getHandler() -- returns the thread handler for a process
|
|
||||||
- multi.OnPriorityChanged(self, priority) -- Connection is triggered whenever the priority of an object is changed!
|
|
||||||
- multi.setClock(clock_func) -- If you have access to a clock function that works like os.clock() you can set it using this function. The priorityManager if chronos is installed sets the clock to it's current version.
|
|
||||||
- multi:setCurrentTask() -- Used to set the current processor. Used in custom processors.
|
|
||||||
- multi:setCurrentProcess() -- Used to set the current processor. It should only be called on a processor object
|
|
||||||
- multi.success(...) -- Sends a success. Green `SUCCESS` mainly used for tests
|
|
||||||
- multi.warn(...) -- Sends a warning. Yellow `WARNING`
|
|
||||||
- multi.error(err) -- When called this function will gracefully kill multi, cleaning things up. Red `ERROR`
|
|
||||||
|
|
||||||
**Note:** If you want to have multi.print, multi.warn and multi.error to work you need to enable them in settings
|
|
||||||
```lua
|
|
||||||
multi, thread = require("multi"):init {
|
|
||||||
print=true,
|
|
||||||
warn=true,
|
|
||||||
error=true -- Errors will throw regardless. Setting to true will
|
|
||||||
-- cause the library to force hard crash itself!
|
|
||||||
}
|
|
||||||
```
|
|
||||||
- THREAD.exposeEnv(name) -- Merges set env into the global namespace of the system thread it was called in.
|
|
||||||
- THREAD.setENV(table [, name]) -- Set a simple table that will be merged into the global namespace. If a name is supplied the global namespace will not be merged. Call THREAD.exposeEnv(name) to expose that namespace within a thread.
|
|
||||||
|
|
||||||
**Note:** To maintain compatibility between each integration use simple tables. No self references, and string indices only.
|
|
||||||
```lua
|
|
||||||
THREAD.setENV({
|
|
||||||
shared_function = function()
|
|
||||||
print("I am shared!")
|
|
||||||
end
|
|
||||||
})
|
|
||||||
```
|
|
||||||
When this function is used it writes to a special variable that is read at thread spawn time. If this function is then ran later it can be used to set a different env and be applied to future spawned threads.
|
|
||||||
- THREAD.getENV() can be used to manage advanced uses of the setENV() functionality
|
|
||||||
- Connection objects now support the % function. This supports a function % connection object. What it does is allow you to **mod**ify the incoming arguments of a connection event.
|
|
||||||
```lua
|
|
||||||
local conn1 = multi:newConnection()
|
|
||||||
local conn2 = function(a,b,c) return a*2, b*2, c*2 end % conn1
|
|
||||||
conn2(function(a,b,c)
|
|
||||||
print("Conn2",a,b,c)
|
|
||||||
end)
|
|
||||||
conn1(function(a,b,c)
|
|
||||||
print("Conn1",a,b,c)
|
|
||||||
end)
|
|
||||||
conn1:Fire(1,2,3)
|
|
||||||
conn2:Fire(1,2,3)
|
|
||||||
```
|
|
||||||
Output:
|
|
||||||
```
|
|
||||||
Conn2 2 4 6
|
|
||||||
Conn1 1 2 3
|
|
||||||
Conn2 1 2 3
|
|
||||||
```
|
|
||||||
**Note:** Conn1 does not get modified, however firing conn1 will also fire conn2 and have it's arguments modified. Also firing conn2 directly **does not** modify conn2's arguments!
|
|
||||||
See it's implementation below:
|
|
||||||
```lua
|
|
||||||
__mod = function(obj1, obj2)
|
|
||||||
local cn = multi:newConnection()
|
|
||||||
if type(obj1) == "function" and type(obj2) == "table" then
|
|
||||||
obj2(function(...)
|
|
||||||
cn:Fire(obj1(...))
|
|
||||||
end)
|
|
||||||
else
|
|
||||||
error("Invalid mod!", type(obj1), type(obj2),"Expected function, connection(table)")
|
|
||||||
end
|
|
||||||
return cn
|
|
||||||
end
|
|
||||||
```
|
|
||||||
- The len operator `#` will return the number of connections in the object!
|
|
||||||
```
|
|
||||||
local conn = multi:newConnection()
|
|
||||||
conn(function() print("Test 1") end)
|
|
||||||
conn(function() print("Test 2") end)
|
|
||||||
conn(function() print("Test 3") end)
|
|
||||||
conn(function() print("Test 4") end)
|
|
||||||
print(#conn)
|
|
||||||
```
|
|
||||||
Output:
|
|
||||||
```
|
|
||||||
4
|
|
||||||
```
|
|
||||||
- Connection objects can be negated -conn returns self so conn = -conn, reverses the order of connection events
|
|
||||||
```lua
|
|
||||||
local conn = multi:newConnection()
|
|
||||||
conn(function() print("Test 1") end)
|
|
||||||
conn(function() print("Test 2") end)
|
|
||||||
conn(function() print("Test 3") end)
|
|
||||||
conn(function() print("Test 4") end)
|
|
||||||
|
|
||||||
print("Fire 1")
|
|
||||||
conn:Fire()
|
|
||||||
conn = -conn
|
|
||||||
print("Fire 2")
|
|
||||||
conn:Fire()
|
|
||||||
```
|
|
||||||
Output:
|
|
||||||
```
|
|
||||||
Fire 1
|
|
||||||
Test 1
|
|
||||||
Test 2
|
|
||||||
Test 3
|
|
||||||
Test 4
|
|
||||||
Fire 2
|
|
||||||
Test 4
|
|
||||||
Test 3
|
|
||||||
Test 2
|
|
||||||
Test 1
|
|
||||||
```
|
|
||||||
- Connection objects can be divided, function / connection
|
|
||||||
This is a mix between the behavior between mod and concat, where the original connection can forward it's events to the new one as well as do a check like concat can. View it's implementation below:
|
|
||||||
```lua
|
|
||||||
__div = function(obj1, obj2) -- /
|
|
||||||
local cn = self:newConnection()
|
|
||||||
local ref
|
|
||||||
if type(obj1) == "function" and type(obj2) == "table" then
|
|
||||||
obj2(function(...)
|
|
||||||
local args = {obj1(...)}
|
|
||||||
if args[1] then
|
|
||||||
cn:Fire(multi.unpack(args))
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
else
|
|
||||||
multi.error("Invalid divide! ", type(obj1), type(obj2)," Expected function/connection(table)")
|
|
||||||
end
|
|
||||||
return cn
|
|
||||||
end
|
|
||||||
```
|
|
||||||
- Connection objects can now be concatenated with functions, not each other. For example:
|
|
||||||
```lua
|
|
||||||
multi, thread = require("multi"):init{print=true,findopt=true}
|
|
||||||
|
|
||||||
local conn1, conn2 = multi:newConnection(), multi:newConnection()
|
|
||||||
conn3 = conn1 + conn2
|
|
||||||
|
|
||||||
conn1(function()
|
|
||||||
print("Hi 1")
|
|
||||||
end)
|
|
||||||
|
|
||||||
conn2(function()
|
|
||||||
print("Hi 2")
|
|
||||||
end)
|
|
||||||
|
|
||||||
conn3(function()
|
|
||||||
print("Hi 3")
|
|
||||||
end)
|
|
||||||
|
|
||||||
function test(a,b,c)
|
|
||||||
print("I run before all and control if execution should continue!")
|
|
||||||
return a>b
|
|
||||||
end
|
|
||||||
|
|
||||||
conn4 = test .. conn1
|
|
||||||
|
|
||||||
conn5 = conn2 .. function() print("I run after it all!") end
|
|
||||||
|
|
||||||
conn4:Fire(3,2,3)
|
|
||||||
|
|
||||||
-- This second one won't trigger the Hi's
|
|
||||||
conn4:Fire(1,2,3)
|
|
||||||
|
|
||||||
conn5(function()
|
|
||||||
print("Test 1")
|
|
||||||
end)
|
|
||||||
|
|
||||||
conn5(function()
|
|
||||||
print("Test 2")
|
|
||||||
end)
|
|
||||||
|
|
||||||
conn5(function()
|
|
||||||
print("Test 3")
|
|
||||||
end)
|
|
||||||
|
|
||||||
conn5:Fire()
|
|
||||||
```
|
|
||||||
|
|
||||||
Output:
|
|
||||||
```
|
|
||||||
I run before all and control if things go!
|
|
||||||
Hi 3
|
|
||||||
Hi 1
|
|
||||||
Test 1
|
|
||||||
Test 2
|
|
||||||
Test 3
|
|
||||||
I run after it all!
|
|
||||||
```
|
|
||||||
|
|
||||||
**Note:** Concat of connections does modify internal events on both connections depending on the direction func .. conn or conn .. func See implemention below:
|
|
||||||
```lua
|
|
||||||
__concat = function(obj1, obj2)
|
|
||||||
local cn = multi:newConnection()
|
|
||||||
local ref
|
|
||||||
if type(obj1) == "function" and type(obj2) == "table" then
|
|
||||||
cn(function(...)
|
|
||||||
if obj1(...) then
|
|
||||||
obj2:Fire(...)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
cn.__connectionAdded = function(conn, func)
|
|
||||||
cn:Unconnect(conn)
|
|
||||||
obj2:Connect(func)
|
|
||||||
end
|
|
||||||
elseif type(obj1) == "table" and type(obj2) == "function" then
|
|
||||||
ref = cn(function(...)
|
|
||||||
obj1:Fire(...)
|
|
||||||
obj2(...)
|
|
||||||
end)
|
|
||||||
cn.__connectionAdded = function()
|
|
||||||
cn.rawadd = true
|
|
||||||
cn:Unconnect(ref)
|
|
||||||
ref = cn(function(...)
|
|
||||||
if obj2(...) then
|
|
||||||
obj1:Fire(...)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
error("Invalid concat!", type(obj1), type(obj2),"Expected function/connection(table), connection(table)/function")
|
|
||||||
end
|
|
||||||
return cn
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
Changed
|
|
||||||
---
|
|
||||||
- multi:newTask(task) is not tied to the processor it is created on.
|
|
||||||
- `multi:getTasks()` renamed to `multi:getRunners()`, should help with confusion between multi:newTask()
|
|
||||||
- changed how multi adds unpack to the global namespace. Instead we capture that value into multi.unpack.
|
|
||||||
- multi:newUpdater(skip, func) -- Now accepts func as the second argument. So you don't need to call OnUpdate(func) after creation.
|
|
||||||
- multi errors now internally call `multi.error` instead of `multi.print`
|
|
||||||
- Actors Act() method now returns true when the main event is fired. Steps/Loops always return true. Nil is returned otherwise.
|
|
||||||
- Connection:Connect(func, name) Now you can supply a name and name the connection.
|
|
||||||
- Connection:getConnection(name) This will return the connection function which you can do what you will with it.
|
|
||||||
- Fast connections are the only connections. Legacy connections have been removed completely. Not much should change on the users end. Perhaps some minor changes.
|
|
||||||
- conn:Lock(conn) When supplied with a connection reference (What is returned by Connect(func)) it will only lock that connection Reference and not the entire connection. Calling without any arguments will lock the entire connection.
|
|
||||||
- connUnlock(conn) When supplied with a connection reference it restores that reference and it can be fired again. When no arguments are supplied it unlocks the entire connection.
|
|
||||||
|
|
||||||
**Note:** Lock and Unlock when supplied with arguments and not supplied with arguments operate on different objects. If you unlock an entire connection. Individual connection refs will not unlock. The same applies with locking. The entire connection and references are treated differently.
|
|
||||||
|
|
||||||
- multi.OnObjectCreated is only called when an object is created in a particular process. Proc.OnObjectCreated is needed to detect when an object is created within a process.
|
|
||||||
- multi.print shows "INFO" before it's message. Blue `INFO`
|
|
||||||
- Connections internals changed, not too much changed on the surface.
|
|
||||||
- newConnection(protect, func, kill)
|
|
||||||
- `protect` disables fastmode, but protects the connection
|
|
||||||
- `func` uses `..` and appends func to the connection so it calls it after all connections run. There is some internal overhead added when using this, but it isn't much.
|
|
||||||
- `kill` removes the connection when fired
|
|
||||||
|
|
||||||
**Note:** When using protect/kill connections are triggered in reverse order
|
|
||||||
|
|
||||||
Removed
|
|
||||||
---
|
|
||||||
- multi.CONNECTOR_LINK -- No longer used
|
|
||||||
- multi:newConnector() -- No longer used
|
|
||||||
- THREAD.getName() use THREAD_NAME instead
|
|
||||||
- THREAD.getID() use THREAD_ID instead
|
|
||||||
- conn:SetHelper(func) -- With the removal of old Connect this function is no longer needed
|
|
||||||
- connection events can no longer can be chained with connect. Connect only takes a function that you want to connect
|
|
||||||
|
|
||||||
Fixed
|
|
||||||
---
|
|
||||||
- Issue with luajit w/5.2 compat breaking with coroutine.running(), fixed the script to properly handle so thread.isThread() returns as expected!
|
|
||||||
- Issue with coroutine based threads where they weren't all being scheduled due to a bad for loop. Replaced with a while to ensure all threads are consumed properly. If a thread created a thread that created a thread that may or may not be on the same process, things got messed up due to the original function not being built with these abstractions in mind.
|
|
||||||
- Issue with thread:newFunction() where a threaded function will keep a record of their returns and pass them to future calls of the function.
|
|
||||||
- Issue with multi:newTask(func) not properly handling tasks to be removed. Now uses a thread internally to manage things.
|
|
||||||
- multi.isMainThread was not properly handled in each integration. This has been resolved.
|
|
||||||
- Issue with pseudo threading env's being messed up. Required removal of getName and getID!
|
|
||||||
- connections being multiplied together would block the entire connection object from pushing events! This is not the desired effect I wanted. Now only the connection reference involved in the multiplication is locked!
|
|
||||||
- multi:reallocate(processor, index) has been fixed to work with the current changes of the library.
|
|
||||||
- Issue with lanes not handling errors properly. This is now resolved
|
|
||||||
- Oversight with how pushStatus worked with nesting threaded functions, connections and forwarding events. Changes made and this works now!
|
|
||||||
```lua
|
|
||||||
func = thread:newFunction(function()
|
|
||||||
for i=1,10 do
|
|
||||||
thread.sleep(1)
|
|
||||||
thread.pushStatus(i)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
func2 = thread:newFunction(function()
|
|
||||||
local ref = func()
|
|
||||||
ref.OnStatus(function(num)
|
|
||||||
-- do stuff with this data
|
|
||||||
|
|
||||||
thread.pushStatus(num*2) -- Technically this is not ran within a thread. This is ran outside of a thread inside the thread handler.
|
|
||||||
end)
|
|
||||||
end)
|
|
||||||
|
|
||||||
local handler = func2()
|
|
||||||
handler.OnStatus(function(num)
|
|
||||||
print(num)
|
|
||||||
end)
|
|
||||||
```
|
|
||||||
|
|
||||||
ToDo
|
|
||||||
---
|
|
||||||
- Network Manager, I know I said it will be in this release, but I'm still planning it out.
|
|
||||||
|
|
||||||
# Update 15.3.1 - Bug fix
|
|
||||||
Fixed
|
|
||||||
---
|
|
||||||
- Issue where multiplying connections triggered events improperly
|
|
||||||
```lua
|
|
||||||
local multi, thread = require("multi"):init()
|
|
||||||
conn1 = multi:newConnection()
|
|
||||||
conn2 = multi:newConnection(); -- To remove function ambiguity
|
|
||||||
|
|
||||||
(conn1 * conn2)(function() print("Triggered!") end)
|
|
||||||
|
|
||||||
conn1:Fire()
|
|
||||||
conn2:Fire()
|
|
||||||
|
|
||||||
-- Looks like this is triggering a response. It shouldn't. We need to account for this
|
|
||||||
conn1:Fire()
|
|
||||||
conn1:Fire()
|
|
||||||
-- Triggering conn1 twice counted as a valid way to trigger the virtual connection (conn1 * conn2)
|
|
||||||
|
|
||||||
-- Now in 15.3.1, this works properly and the above doesn't do anything. Internally connections are locked until the conditions are met.
|
|
||||||
conn2:Fire()
|
|
||||||
```
|
|
||||||
|
|
||||||
# Update 15.3.0 - A world of Connections
|
# Update 15.3.0 - A world of Connections
|
||||||
|
|
||||||
@ -2309,7 +1681,7 @@ L: 2120906
|
|||||||
I: 2120506
|
I: 2120506
|
||||||
```
|
```
|
||||||
|
|
||||||
Auto Priority works by seeing what should be set high or low. Due to lua not having more persicion than milliseconds, I was unable to have a detailed manager that can set things to high, above normal, normal, ect. This has either high or low. If a process takes longer than .001 millisecond it will be set to low priority. You can change this by using the setting auto_lowest = multi.Priority_[PLevel] the defualt is low, not idle, since idle tends to get about 1 process each second though you can change it to idle using that setting. This is nolonger the case in version 16.0.0 multi has evolved ;)
|
Auto Priority works by seeing what should be set high or low. Due to lua not having more persicion than milliseconds, I was unable to have a detailed manager that can set things to high, above normal, normal, ect. This has either high or low. If a process takes longer than .001 millisecond it will be set to low priority. You can change this by using the setting auto_lowest = multi.Priority_[PLevel] the defualt is low, not idle, since idle tends to get about 1 process each second though you can change it to idle using that setting.
|
||||||
|
|
||||||
**Improved:**
|
**Improved:**
|
||||||
- Performance at the base level has been doubled! On my machine benchmark went from ~9mil to ~20 mil steps/s.
|
- Performance at the base level has been doubled! On my machine benchmark went from ~9mil to ~20 mil steps/s.
|
||||||
|
|||||||
@ -1,106 +0,0 @@
|
|||||||
local multi, thread = require("multi"):init()
|
|
||||||
|
|
||||||
multi.defaultSettings.debugging = true
|
|
||||||
|
|
||||||
local dbg = {}
|
|
||||||
dbg.__index = dbg
|
|
||||||
dbg.processors = {}
|
|
||||||
|
|
||||||
-- Hooks to all on object created events!
|
|
||||||
local c_cache = {}
|
|
||||||
local d_cache = {}
|
|
||||||
|
|
||||||
local proc = multi:newProcessor("Debug_Processor").Start()
|
|
||||||
|
|
||||||
dbg.OnObjectCreated = function(obj, process)
|
|
||||||
if c_cache[obj] then
|
|
||||||
return false
|
|
||||||
else
|
|
||||||
c_cache[obj] = true
|
|
||||||
proc:newTask(function()
|
|
||||||
c_cache[obj] = false
|
|
||||||
end)
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end .. multi:newConnection()
|
|
||||||
|
|
||||||
dbg.OnObjectDestroyed = function(obj, process)
|
|
||||||
if d_cache[obj] then
|
|
||||||
return false
|
|
||||||
else
|
|
||||||
d_cache[obj] = true
|
|
||||||
proc:newTask(function()
|
|
||||||
d_cache[obj] = false
|
|
||||||
end)
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end .. multi:newConnection()
|
|
||||||
|
|
||||||
local creation_hook, destruction_hook
|
|
||||||
local types
|
|
||||||
local objects = {}
|
|
||||||
|
|
||||||
creation_hook = function(obj, process)
|
|
||||||
types = multi:getTypes()
|
|
||||||
if obj.Type == multi.PROCESS and not dbg.processors[obj] then
|
|
||||||
obj.OnObjectCreated(creation_hook)
|
|
||||||
obj.OnObjectDestroyed(destruction_hook)
|
|
||||||
end
|
|
||||||
|
|
||||||
table.insert(objects, obj)
|
|
||||||
|
|
||||||
dbg.OnObjectCreated:Fire(obj, process)
|
|
||||||
end
|
|
||||||
|
|
||||||
destruction_hook = function(obj, process)
|
|
||||||
for i = 1, #objects do
|
|
||||||
if objects[i] == obj then
|
|
||||||
table.remove(objects, i)
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
dbg.OnObjectDestroyed:Fire(obj, process)
|
|
||||||
end
|
|
||||||
|
|
||||||
function dbg:getObjects(typ)
|
|
||||||
if type(typ) == "string" then
|
|
||||||
local objs = {}
|
|
||||||
for i = 1, #objects do
|
|
||||||
if objects[i].Type == typ then
|
|
||||||
objs[#objs+1] = objects[i]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return objs
|
|
||||||
elseif type(typ) == "table" then -- Process
|
|
||||||
local objs = {}
|
|
||||||
for i = 1, #objects do
|
|
||||||
if objects[i].Parent == typ then
|
|
||||||
objs[#objs+1] = objects[i]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return objs
|
|
||||||
elseif type(typ) == "function" then
|
|
||||||
local objs = {}
|
|
||||||
-- Keep objects local/private, return true to add to list, false to reject, "break" to break loop
|
|
||||||
for i = 1, #objects do
|
|
||||||
local ret = typ(objects[i])
|
|
||||||
if ret then
|
|
||||||
objs[#objs+1] = objects[i]
|
|
||||||
elseif ret == "break" then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return objs
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local debug_stats = {}
|
|
||||||
|
|
||||||
local tmulti = multi:getThreadManagerProcess()
|
|
||||||
multi.OnObjectCreated(creation_hook)
|
|
||||||
tmulti.OnObjectCreated(creation_hook)
|
|
||||||
multi.OnObjectDestroyed(destroction_hook)
|
|
||||||
tmulti.OnObjectDestroyed(destroction_hook)
|
|
||||||
|
|
||||||
-- We write to a debug interface in the multi namespace
|
|
||||||
multi.debugging = dbg
|
|
||||||
@ -1,46 +0,0 @@
|
|||||||
local multi, thread = require("multi"):init{error=true}
|
|
||||||
multi.error("Currntly not supported!")
|
|
||||||
os.exit(1)
|
|
||||||
local effil = require("effil")
|
|
||||||
|
|
||||||
-- I like some of the things that this library offers.
|
|
||||||
-- Current limitations prevent me from being able to use effil,
|
|
||||||
-- but I might fork and work on it myself.
|
|
||||||
|
|
||||||
-- Configs
|
|
||||||
effil.allow_table_upvalues(false)
|
|
||||||
|
|
||||||
local GLOBAL,THREAD = require("multi.integration.effilManager.threads").init()
|
|
||||||
local count = 1
|
|
||||||
local started = false
|
|
||||||
local livingThreads = {}
|
|
||||||
|
|
||||||
function multi:newSystemThread(name, func, ...)
|
|
||||||
local name = name or multi.randomString(16)
|
|
||||||
local rand = math.random(1, 10000000)
|
|
||||||
c = {}
|
|
||||||
c.name = name
|
|
||||||
c.Name = name
|
|
||||||
c.Id = count
|
|
||||||
end
|
|
||||||
|
|
||||||
function THREAD:newFunction(func, holdme)
|
|
||||||
return thread:newFunctionBase(function(...)
|
|
||||||
return multi:newSystemThread("TempSystemThread",func,...)
|
|
||||||
end, holdme, multi.SFUNCTION)()
|
|
||||||
end
|
|
||||||
|
|
||||||
THREAD.newSystemThread = function(...)
|
|
||||||
multi:newSystemThread(...)
|
|
||||||
end
|
|
||||||
|
|
||||||
multi.print("Integrated Effil Threading!")
|
|
||||||
multi.integration = {} -- for module creators
|
|
||||||
multi.integration.GLOBAL = GLOBAL
|
|
||||||
multi.integration.THREAD = THREAD
|
|
||||||
require("multi.integration.effilManager.extensions")
|
|
||||||
return {
|
|
||||||
init = function()
|
|
||||||
return GLOBAL, THREAD
|
|
||||||
end
|
|
||||||
}
|
|
||||||
@ -22,57 +22,29 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
]]
|
]]
|
||||||
local multi, thread = require("multi"):init()
|
local multi, thread = require("multi"):init()
|
||||||
|
|
||||||
if not (GLOBAL and THREAD) then
|
if not (GLOBAL and THREAD) then
|
||||||
GLOBAL, THREAD = multi.integration.GLOBAL, multi.integration.THREAD
|
local GLOBAL, THREAD = multi.integration.GLOBAL,multi.integration.THREAD
|
||||||
else
|
else
|
||||||
lanes = require("lanes")
|
lanes = require("lanes")
|
||||||
end
|
end
|
||||||
|
|
||||||
function multi:newSystemThreadedQueue(name)
|
function multi:newSystemThreadedQueue(name)
|
||||||
local name = name or multi.randomString(16)
|
local name = name or multi.randomString(16)
|
||||||
local c = {}
|
local c = {}
|
||||||
c.Name = name
|
c.Name = name
|
||||||
c.linda = lanes.linda()
|
c.linda = lanes.linda()
|
||||||
c.Type = multi.registerType("s_queue")
|
|
||||||
|
|
||||||
function c:push(v)
|
function c:push(v)
|
||||||
self.linda:send("Q", v)
|
self.linda:send("Q", v)
|
||||||
end
|
end
|
||||||
|
|
||||||
function c:pop()
|
function c:pop()
|
||||||
return ({self.linda:receive(0, "Q")})[2]
|
return ({self.linda:receive(0, "Q")})[2]
|
||||||
end
|
end
|
||||||
|
|
||||||
function c:peek()
|
function c:peek()
|
||||||
return self.linda:get("Q")
|
return self.linda:get("Q")
|
||||||
end
|
end
|
||||||
|
|
||||||
function c:init()
|
function c:init()
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
GLOBAL[name or "_"] = c
|
||||||
if multi.isMainThread then
|
|
||||||
multi.integration.GLOBAL[name] = c
|
|
||||||
else
|
|
||||||
GLOBAL[name] = c
|
|
||||||
end
|
|
||||||
|
|
||||||
function c:Hold(opt)
|
|
||||||
local multi, thread = require("multi"):init()
|
|
||||||
if opt.peek then
|
|
||||||
return thread.hold(function()
|
|
||||||
return self:peek()
|
|
||||||
end)
|
|
||||||
else
|
|
||||||
return thread.hold(function()
|
|
||||||
return self:pop()
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
self:create(c)
|
|
||||||
|
|
||||||
return c
|
return c
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -81,12 +53,6 @@ function multi:newSystemThreadedTable(name)
|
|||||||
local c = {}
|
local c = {}
|
||||||
c.link = lanes.linda()
|
c.link = lanes.linda()
|
||||||
c.Name = name
|
c.Name = name
|
||||||
c.Type = multi.registerType("s_table")
|
|
||||||
|
|
||||||
function c:init()
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
setmetatable(c,{
|
setmetatable(c,{
|
||||||
__index = function(t,k)
|
__index = function(t,k)
|
||||||
return c.link:get(k)
|
return c.link:get(k)
|
||||||
@ -95,46 +61,29 @@ function multi:newSystemThreadedTable(name)
|
|||||||
c.link:set(k,v)
|
c.link:set(k,v)
|
||||||
end
|
end
|
||||||
})
|
})
|
||||||
|
function c:init()
|
||||||
if multi.isMainThread then
|
return self
|
||||||
multi.integration.GLOBAL[name] = c
|
|
||||||
else
|
|
||||||
GLOBAL[name] = c
|
|
||||||
end
|
end
|
||||||
|
GLOBAL[name or "_"] = c
|
||||||
function c:Hold(opt)
|
|
||||||
local multi, thread = require("multi"):init()
|
|
||||||
if opt.key then
|
|
||||||
return thread.hold(function()
|
|
||||||
return self.tab[opt.key]
|
|
||||||
end)
|
|
||||||
else
|
|
||||||
multi.error("Must provide a key to check opt.key = 'key'")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
self:create(c)
|
|
||||||
|
|
||||||
return c
|
return c
|
||||||
end
|
end
|
||||||
|
|
||||||
function multi:newSystemThreadedJobQueue(n)
|
function multi:newSystemThreadedJobQueue(n)
|
||||||
local c = {}
|
local c = {}
|
||||||
c.cores = n or THREAD.getCores()*2
|
c.cores = n or THREAD.getCores()*2
|
||||||
c.Type = multi.registerType("s_jobqueue")
|
|
||||||
c.OnJobCompleted = multi:newConnection()
|
c.OnJobCompleted = multi:newConnection()
|
||||||
local funcs = multi:newSystemThreadedTable()
|
local funcs = multi:newSystemThreadedTable():init()
|
||||||
local queueJob = multi:newSystemThreadedQueue()
|
local queueJob = multi:newSystemThreadedQueue():init()
|
||||||
local queueReturn = multi:newSystemThreadedQueue()
|
local queueReturn = multi:newSystemThreadedQueue():init()
|
||||||
local doAll = multi:newSystemThreadedQueue()
|
local doAll = multi:newSystemThreadedQueue():init()
|
||||||
local ID=1
|
local ID=1
|
||||||
local jid = 1
|
local jid = 1
|
||||||
function c:isEmpty()
|
function c:isEmpty()
|
||||||
return queueJob:peek()==nil
|
return queueJob:peek()==nil
|
||||||
end
|
end
|
||||||
function c:doToAll(func,...)
|
function c:doToAll(func)
|
||||||
for i=1,c.cores do
|
for i=1,c.cores do
|
||||||
doAll:push{ID,func,...}
|
doAll:push{ID,func}
|
||||||
end
|
end
|
||||||
ID = ID + 1
|
ID = ID + 1
|
||||||
return self
|
return self
|
||||||
@ -144,7 +93,7 @@ function multi:newSystemThreadedJobQueue(n)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
function c:pushJob(name,...)
|
function c:pushJob(name,...)
|
||||||
queueJob:push{name,jid,multi.pack(...)}
|
queueJob:push{name,jid,{...}}
|
||||||
jid = jid + 1
|
jid = jid + 1
|
||||||
return jid-1
|
return jid-1
|
||||||
end
|
end
|
||||||
@ -163,16 +112,13 @@ function multi:newSystemThreadedJobQueue(n)
|
|||||||
local rets
|
local rets
|
||||||
link = c.OnJobCompleted(function(jid,...)
|
link = c.OnJobCompleted(function(jid,...)
|
||||||
if id==jid then
|
if id==jid then
|
||||||
rets = multi.pack(...)
|
rets = {...}
|
||||||
|
link:Destroy()
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
return thread.hold(function()
|
return thread.hold(function()
|
||||||
if rets then
|
if rets then
|
||||||
if #rets == 0 then
|
return unpack(rets) or multi.NIL
|
||||||
return multi.NIL
|
|
||||||
else
|
|
||||||
return multi.unpack(rets)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end,holup),name
|
end,holup),name
|
||||||
@ -182,19 +128,16 @@ function multi:newSystemThreadedJobQueue(n)
|
|||||||
local job = thread.hold(function()
|
local job = thread.hold(function()
|
||||||
return queueReturn:pop()
|
return queueReturn:pop()
|
||||||
end)
|
end)
|
||||||
if job then
|
|
||||||
local id = table.remove(job,1)
|
local id = table.remove(job,1)
|
||||||
c.OnJobCompleted:Fire(id,multi.unpack(job))
|
c.OnJobCompleted:Fire(id,unpack(job))
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
for i=1,c.cores do
|
for i=1,c.cores do
|
||||||
multi:newSystemThread("STJQ_"..multi.randomString(8),function(queue)
|
multi:newSystemThread("SystemThreadedJobQueue",function(queue)
|
||||||
local multi,thread = require("multi"):init()
|
local multi,thread = require("multi"):init()
|
||||||
local idle = os.clock()
|
local idle = os.clock()
|
||||||
local clock = os.clock
|
local clock = os.clock
|
||||||
local ref = 0
|
local ref = 0
|
||||||
_G["__QR"] = queueReturn
|
|
||||||
setmetatable(_G,{__index = funcs})
|
setmetatable(_G,{__index = funcs})
|
||||||
thread:newThread("JobHandler",function()
|
thread:newThread("JobHandler",function()
|
||||||
while true do
|
while true do
|
||||||
@ -202,12 +145,10 @@ function multi:newSystemThreadedJobQueue(n)
|
|||||||
return queueJob:pop()
|
return queueJob:pop()
|
||||||
end)
|
end)
|
||||||
idle = clock()
|
idle = clock()
|
||||||
thread:newThread("JobQueue-Spawn",function()
|
|
||||||
local name = table.remove(dat,1)
|
local name = table.remove(dat,1)
|
||||||
local jid = table.remove(dat,1)
|
local jid = table.remove(dat,1)
|
||||||
local args = table.remove(dat,1)
|
local args = table.remove(dat,1)
|
||||||
queueReturn:push{jid, funcs[name](args[1],args[2],args[3],args[4],args[5],args[6],args[7],args[8]), queue}
|
queueReturn:push{jid, funcs[name](unpack(args)),queue}
|
||||||
end)
|
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
thread:newThread("DoAllHandler",function()
|
thread:newThread("DoAllHandler",function()
|
||||||
@ -217,10 +158,9 @@ function multi:newSystemThreadedJobQueue(n)
|
|||||||
end)
|
end)
|
||||||
if dat then
|
if dat then
|
||||||
if dat[1]>ref then
|
if dat[1]>ref then
|
||||||
ref = table.remove(dat, 1)
|
|
||||||
func = table.remove(dat, 1)
|
|
||||||
idle = clock()
|
idle = clock()
|
||||||
func(unpack(dat))
|
ref = dat[1]
|
||||||
|
dat[2]()
|
||||||
doAll:pop()
|
doAll:pop()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -235,22 +175,14 @@ function multi:newSystemThreadedJobQueue(n)
|
|||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
multi:mainloop()
|
multi:mainloop()
|
||||||
end,i)
|
end,i).priority = thread.Priority_Core
|
||||||
end
|
end
|
||||||
|
|
||||||
function c:Hold(opt)
|
|
||||||
return thread.hold(self.OnJobCompleted)
|
|
||||||
end
|
|
||||||
|
|
||||||
self:create(c)
|
|
||||||
|
|
||||||
return c
|
return c
|
||||||
end
|
end
|
||||||
|
|
||||||
function multi:newSystemThreadedConnection(name)
|
function multi:newSystemThreadedConnection(name)
|
||||||
local name = name or multi.randomString(16)
|
local name = name or multi.randomString(16)
|
||||||
local c = {}
|
local c = {}
|
||||||
c.Type = multi.registerType("s_connection")
|
|
||||||
c.CONN = 0x00
|
c.CONN = 0x00
|
||||||
c.TRIG = 0x01
|
c.TRIG = 0x01
|
||||||
c.PING = 0x02
|
c.PING = 0x02
|
||||||
@ -264,7 +196,7 @@ function multi:newSystemThreadedConnection(name)
|
|||||||
end
|
end
|
||||||
return r
|
return r
|
||||||
end
|
end
|
||||||
c.CID = THREAD_ID
|
c.CID = THREAD.getID()
|
||||||
c.subscribe = multi:newSystemThreadedQueue("SUB_STC_"..self.Name):init()
|
c.subscribe = multi:newSystemThreadedQueue("SUB_STC_"..self.Name):init()
|
||||||
c.Name = name
|
c.Name = name
|
||||||
c.links = {} -- All triggers sent from main connection. When a connection is triggered on another thread, they speak to the main then send stuff out.
|
c.links = {} -- All triggers sent from main connection. When a connection is triggered on another thread, they speak to the main then send stuff out.
|
||||||
@ -301,7 +233,7 @@ function multi:newSystemThreadedConnection(name)
|
|||||||
|
|
||||||
local function fire(...)
|
local function fire(...)
|
||||||
for _, link in pairs(c.links) do
|
for _, link in pairs(c.links) do
|
||||||
link:push {c.TRIG, multi.pack(...)}
|
link:push {c.TRIG, {...}}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -321,16 +253,16 @@ function multi:newSystemThreadedConnection(name)
|
|||||||
end)
|
end)
|
||||||
c.links[#c.links+1] = item[2]
|
c.links[#c.links+1] = item[2]
|
||||||
elseif item[1] == c.TRIG then
|
elseif item[1] == c.TRIG then
|
||||||
fire(multi.unpack(item[2]))
|
fire(unpack(item[2]))
|
||||||
c.proxy_conn:Fire(multi.unpack(item[2]))
|
c.proxy_conn:Fire(unpack(item[2]))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
--- ^^^ This will only exist in the init thread
|
--- ^^^ This will only exist in the init thread
|
||||||
|
|
||||||
function c:Fire(...)
|
function c:Fire(...)
|
||||||
local args = multi.pack(...)
|
local args = {...}
|
||||||
if self.CID == THREAD_ID then -- Host Call
|
if self.CID == THREAD.getID() then -- Host Call
|
||||||
for _, link in pairs(self.links) do
|
for _, link in pairs(self.links) do
|
||||||
link:push {self.TRIG, args}
|
link:push {self.TRIG, args}
|
||||||
end
|
end
|
||||||
@ -345,14 +277,8 @@ function multi:newSystemThreadedConnection(name)
|
|||||||
self.links = {}
|
self.links = {}
|
||||||
self.proxy_conn = multi:newConnection()
|
self.proxy_conn = multi:newConnection()
|
||||||
local mt = getmetatable(self.proxy_conn)
|
local mt = getmetatable(self.proxy_conn)
|
||||||
local tempMT = {}
|
setmetatable(self, {__index = self.proxy_conn, __call = function(t,func) self.proxy_conn(func) end, __add = mt.__add})
|
||||||
for i,v in pairs(mt) do
|
if self.CID == THREAD.getID() then return self end
|
||||||
tempMT[i] = v
|
|
||||||
end
|
|
||||||
tempMT.__index = self.proxy_conn
|
|
||||||
tempMT.__call = function(t,func) self.proxy_conn(func) end
|
|
||||||
setmetatable(self, tempMT)
|
|
||||||
if self.CID == THREAD_ID then return self end
|
|
||||||
thread:newThread("STC_CONN_MAN"..name,function()
|
thread:newThread("STC_CONN_MAN"..name,function()
|
||||||
local item
|
local item
|
||||||
local link_self_ref = multi:newSystemThreadedQueue()
|
local link_self_ref = multi:newSystemThreadedQueue()
|
||||||
@ -370,7 +296,7 @@ function multi:newSystemThreadedConnection(name)
|
|||||||
end
|
end
|
||||||
link_self_ref:pop()
|
link_self_ref:pop()
|
||||||
elseif item[1] == self.TRIG then
|
elseif item[1] == self.TRIG then
|
||||||
self.proxy_conn:Fire(multi.unpack(item[2]))
|
self.proxy_conn:Fire(unpack(item[2]))
|
||||||
link_self_ref:pop()
|
link_self_ref:pop()
|
||||||
else
|
else
|
||||||
-- This shouldn't be the case
|
-- This shouldn't be the case
|
||||||
@ -380,14 +306,7 @@ function multi:newSystemThreadedConnection(name)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
if multi.isMainThread then
|
|
||||||
multi.integration.GLOBAL[name] = c
|
|
||||||
else
|
|
||||||
GLOBAL[name] = c
|
GLOBAL[name] = c
|
||||||
end
|
|
||||||
|
|
||||||
self:create(c)
|
|
||||||
|
|
||||||
return c
|
return c
|
||||||
end
|
end
|
||||||
require("multi.integration.sharedExtensions")
|
|
||||||
@ -36,9 +36,6 @@ lanes = require("lanes").configure()
|
|||||||
multi.SystemThreads = {}
|
multi.SystemThreads = {}
|
||||||
multi.isMainThread = true
|
multi.isMainThread = true
|
||||||
|
|
||||||
_G.THREAD_NAME = "MAIN_THREAD"
|
|
||||||
_G.THREAD_ID = 0
|
|
||||||
|
|
||||||
function multi:canSystemThread()
|
function multi:canSystemThread()
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
@ -61,7 +58,7 @@ local livingThreads = {}
|
|||||||
function THREAD:newFunction(func,holdme)
|
function THREAD:newFunction(func,holdme)
|
||||||
return thread:newFunctionBase(function(...)
|
return thread:newFunctionBase(function(...)
|
||||||
return multi:newSystemThread("TempSystemThread",func,...)
|
return multi:newSystemThread("TempSystemThread",func,...)
|
||||||
end, holdme, multi.registerType("s_function"))()
|
end,holdme)()
|
||||||
end
|
end
|
||||||
|
|
||||||
function multi:newSystemThread(name, func, ...)
|
function multi:newSystemThread(name, func, ...)
|
||||||
@ -70,43 +67,35 @@ function multi:newSystemThread(name, func, ...)
|
|||||||
local rand = math.random(1, 10000000)
|
local rand = math.random(1, 10000000)
|
||||||
local return_linda = lanes.linda()
|
local return_linda = lanes.linda()
|
||||||
c = {}
|
c = {}
|
||||||
|
c.name = name
|
||||||
c.Name = name
|
c.Name = name
|
||||||
c.ID = count
|
c.Id = count
|
||||||
c.loadString = {"base","package","os","io","math","table","string","coroutine"}
|
c.loadString = {"base","package","os","io","math","table","string","coroutine"}
|
||||||
livingThreads[count] = {true, name}
|
livingThreads[count] = {true, name}
|
||||||
c.returns = return_linda
|
c.returns = return_linda
|
||||||
c.Type = multi.registerType("s_thread")
|
c.Type = "sthread"
|
||||||
c.creationTime = os.clock()
|
c.creationTime = os.clock()
|
||||||
c.alive = true
|
c.alive = true
|
||||||
c.priority = THREAD.Priority_Normal
|
c.priority = THREAD.Priority_Normal
|
||||||
local multi_settings = multi.defaultSettings
|
local multi_settings = multi.defaultSettings
|
||||||
local globe = {
|
for i,v in pairs(multi_settings) do
|
||||||
|
print(i,v)
|
||||||
|
end
|
||||||
|
c.thread = lanes.gen("*",
|
||||||
|
{
|
||||||
|
globals={ -- Set up some globals
|
||||||
THREAD_NAME = name,
|
THREAD_NAME = name,
|
||||||
THREAD_ID = count,
|
THREAD_ID = count,
|
||||||
THREAD = THREAD,
|
THREAD = THREAD,
|
||||||
GLOBAL = GLOBAL,
|
GLOBAL = GLOBAL,
|
||||||
_Console = __ConsoleLinda,
|
_Console = __ConsoleLinda
|
||||||
_DEFER = {}
|
},
|
||||||
}
|
|
||||||
if GLOBAL["__env"] then
|
|
||||||
for i,v in pairs(GLOBAL["__env"]) do
|
|
||||||
globe[i] = v
|
|
||||||
end
|
|
||||||
end
|
|
||||||
c.thread = lanes.gen("*",
|
|
||||||
{
|
|
||||||
globals = globe,
|
|
||||||
priority=c.priority
|
priority=c.priority
|
||||||
},function(...)
|
},function(...)
|
||||||
multi, thread = require("multi"):init(multi_settings)
|
require("multi"):init(multi_settings)
|
||||||
require("multi.integration.lanesManager.extensions")
|
require("multi.integration.lanesManager.extensions")
|
||||||
require("multi.integration.sharedExtensions")
|
|
||||||
local has_error = true
|
local has_error = true
|
||||||
returns = {pcall(func, ...)}
|
return_linda:set("returns",{func(...)})
|
||||||
return_linda:set("returns", returns)
|
|
||||||
for i,v in pairs(_DEFER) do
|
|
||||||
pcall(v)
|
|
||||||
end
|
|
||||||
has_error = false
|
has_error = false
|
||||||
end)(...)
|
end)(...)
|
||||||
count = count + 1
|
count = count + 1
|
||||||
@ -121,20 +110,10 @@ function multi:newSystemThread(name, func, ...)
|
|||||||
c.OnDeath = multi:newConnection()
|
c.OnDeath = multi:newConnection()
|
||||||
c.OnError = multi:newConnection()
|
c.OnError = multi:newConnection()
|
||||||
GLOBAL["__THREADS__"] = livingThreads
|
GLOBAL["__THREADS__"] = livingThreads
|
||||||
c.OnError(multi.error)
|
|
||||||
|
|
||||||
if self.isActor then
|
|
||||||
self:create(c)
|
|
||||||
else
|
|
||||||
multi.create(multi, c)
|
|
||||||
end
|
|
||||||
|
|
||||||
return c
|
return c
|
||||||
end
|
end
|
||||||
|
|
||||||
THREAD.newSystemThread = function(...)
|
THREAD.newSystemThread = multi.newSystemThread
|
||||||
multi:newSystemThread(...)
|
|
||||||
end
|
|
||||||
|
|
||||||
function multi.InitSystemThreadErrorHandler()
|
function multi.InitSystemThreadErrorHandler()
|
||||||
if started == true then
|
if started == true then
|
||||||
@ -147,26 +126,18 @@ function multi.InitSystemThreadErrorHandler()
|
|||||||
while true do
|
while true do
|
||||||
thread.yield()
|
thread.yield()
|
||||||
_,data = __ConsoleLinda:receive(0, "Q")
|
_,data = __ConsoleLinda:receive(0, "Q")
|
||||||
if data then
|
if data then print(unpack(data)) end
|
||||||
--print(data[1])
|
|
||||||
end
|
|
||||||
for i = #threads, 1, -1 do
|
for i = #threads, 1, -1 do
|
||||||
temp = threads[i]
|
temp = threads[i]
|
||||||
status = temp.thread.status
|
status = temp.thread.status
|
||||||
push = __StatusLinda:get(temp.ID)
|
push = __StatusLinda:get(temp.Id)
|
||||||
if push then
|
if push then
|
||||||
temp.statusconnector:Fire(multi.unpack(({__StatusLinda:receive(nil, temp.ID)})[2]))
|
temp.statusconnector:Fire(unpack(({__StatusLinda:receive(nil, temp.Id)})[2]))
|
||||||
end
|
end
|
||||||
if status == "done" or temp.returns:get("returns") then
|
if status == "done" or temp.returns:get("returns") then
|
||||||
returns = ({temp.returns:receive(0, "returns")})[2]
|
livingThreads[temp.Id] = {false, temp.Name}
|
||||||
livingThreads[temp.ID] = {false, temp.Name}
|
|
||||||
temp.alive = false
|
temp.alive = false
|
||||||
if returns[1] == false then
|
temp.OnDeath:Fire(unpack(({temp.returns:receive(0, "returns")})[2]))
|
||||||
temp.OnError:Fire(temp, returns[2])
|
|
||||||
else
|
|
||||||
table.remove(returns,1)
|
|
||||||
temp.OnDeath:Fire(multi.unpack(returns))
|
|
||||||
end
|
|
||||||
GLOBAL["__THREADS__"] = livingThreads
|
GLOBAL["__THREADS__"] = livingThreads
|
||||||
table.remove(threads, i)
|
table.remove(threads, i)
|
||||||
elseif status == "running" then
|
elseif status == "running" then
|
||||||
@ -174,15 +145,19 @@ function multi.InitSystemThreadErrorHandler()
|
|||||||
elseif status == "waiting" then
|
elseif status == "waiting" then
|
||||||
--
|
--
|
||||||
elseif status == "error" then
|
elseif status == "error" then
|
||||||
-- The thread never really errors, we handle this through our linda object
|
livingThreads[temp.Id] = {false, temp.Name}
|
||||||
|
temp.alive = false
|
||||||
|
temp.OnError:Fire(temp,unpack(temp.returns:receive(0,"returns") or {"Thread Killed!"}))
|
||||||
|
GLOBAL["__THREADS__"] = livingThreads
|
||||||
|
table.remove(threads, i)
|
||||||
elseif status == "cancelled" then
|
elseif status == "cancelled" then
|
||||||
livingThreads[temp.ID] = {false, temp.Name}
|
livingThreads[temp.Id] = {false, temp.Name}
|
||||||
temp.alive = false
|
temp.alive = false
|
||||||
temp.OnError:Fire(temp,"thread_cancelled")
|
temp.OnError:Fire(temp,"thread_cancelled")
|
||||||
GLOBAL["__THREADS__"] = livingThreads
|
GLOBAL["__THREADS__"] = livingThreads
|
||||||
table.remove(threads, i)
|
table.remove(threads, i)
|
||||||
elseif status == "killed" then
|
elseif status == "killed" then
|
||||||
livingThreads[temp.ID] = {false, temp.Name}
|
livingThreads[temp.Id] = {false, temp.Name}
|
||||||
temp.alive = false
|
temp.alive = false
|
||||||
temp.OnError:Fire(temp,"thread_killed")
|
temp.OnError:Fire(temp,"thread_killed")
|
||||||
GLOBAL["__THREADS__"] = livingThreads
|
GLOBAL["__THREADS__"] = livingThreads
|
||||||
@ -190,10 +165,10 @@ function multi.InitSystemThreadErrorHandler()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end).OnError(multi.error)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
multi.print("Integrated Lanes Threading!")
|
multi.print("Integrated Lanes!")
|
||||||
multi.integration = {} -- for module creators
|
multi.integration = {} -- for module creators
|
||||||
multi.integration.GLOBAL = GLOBAL
|
multi.integration.GLOBAL = GLOBAL
|
||||||
multi.integration.THREAD = THREAD
|
multi.integration.THREAD = THREAD
|
||||||
|
|||||||
@ -48,12 +48,20 @@ local function INIT(__GlobalLinda, __SleepingLinda, __StatusLinda, __Console)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function THREAD.waitFor(name)
|
function THREAD.waitFor(name)
|
||||||
local multi, thread = require("multi"):init()
|
local function wait()
|
||||||
return multi.hold(function()
|
|
||||||
math.randomseed(os.time())
|
math.randomseed(os.time())
|
||||||
__SleepingLinda:receive(.001, "__non_existing_variable")
|
__SleepingLinda:receive(.001, "__non_existing_variable")
|
||||||
|
end
|
||||||
|
repeat
|
||||||
|
wait()
|
||||||
|
until __GlobalLinda:get(name)
|
||||||
return __GlobalLinda:get(name)
|
return __GlobalLinda:get(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
|
end
|
||||||
|
|
||||||
function THREAD.getCores()
|
function THREAD.getCores()
|
||||||
@ -64,11 +72,11 @@ local function INIT(__GlobalLinda, __SleepingLinda, __StatusLinda, __Console)
|
|||||||
local c = {}
|
local c = {}
|
||||||
c.queue = __Console
|
c.queue = __Console
|
||||||
function c.print(...)
|
function c.print(...)
|
||||||
c.queue:push("Q", table.concat(multi.pack(...), "\t"))
|
c.queue:send("Q", {...})
|
||||||
end
|
end
|
||||||
function c.error(err)
|
function c.error(err)
|
||||||
c.queue:push("Q", "Error in <"..THREAD_NAME..":" .. THREAD_ID .. ">: ".. err)
|
c.queue:push("Q",{"ERROR in <"..__THREADNAME__..">: "..err,__THREADID__})
|
||||||
multi.error(err)
|
error(err)
|
||||||
end
|
end
|
||||||
return c
|
return c
|
||||||
end
|
end
|
||||||
@ -87,12 +95,16 @@ local function INIT(__GlobalLinda, __SleepingLinda, __StatusLinda, __Console)
|
|||||||
error("Thread was killed!\1")
|
error("Thread was killed!\1")
|
||||||
end
|
end
|
||||||
|
|
||||||
function THREAD.sync()
|
function THREAD.getName()
|
||||||
-- Maybe do something...
|
return THREAD_NAME
|
||||||
|
end
|
||||||
|
|
||||||
|
function THREAD.getID()
|
||||||
|
return THREAD_ID
|
||||||
end
|
end
|
||||||
|
|
||||||
function THREAD.pushStatus(...)
|
function THREAD.pushStatus(...)
|
||||||
local args = multi.pack(...)
|
local args = {...}
|
||||||
__StatusLinda:send(nil,THREAD_ID, args)
|
__StatusLinda:send(nil,THREAD_ID, args)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -122,30 +134,9 @@ local function INIT(__GlobalLinda, __SleepingLinda, __StatusLinda, __Console)
|
|||||||
__GlobalLinda:set(k, v)
|
__GlobalLinda:set(k, v)
|
||||||
end
|
end
|
||||||
})
|
})
|
||||||
|
|
||||||
function THREAD.setENV(env, name)
|
|
||||||
GLOBAL[name or "__env"] = env
|
|
||||||
end
|
|
||||||
|
|
||||||
function THREAD.getENV(name)
|
|
||||||
return GLOBAL[name or "__env"]
|
|
||||||
end
|
|
||||||
|
|
||||||
function THREAD.exposeENV(name)
|
|
||||||
name = name or "__env"
|
|
||||||
local env = THREAD.getENV(name)
|
|
||||||
for i,v in pairs(env) do
|
|
||||||
_G[i] = v
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function THREAD.defer(func)
|
|
||||||
table.insert(_DEFER, func)
|
|
||||||
end
|
|
||||||
|
|
||||||
return GLOBAL, THREAD
|
return GLOBAL, THREAD
|
||||||
end
|
end
|
||||||
|
|
||||||
return {init = function(g,s,st,c,onexit)
|
return {init = function(g,s,st,c)
|
||||||
return INIT(g,s,st,c,onexit)
|
return INIT(g,s,st,c)
|
||||||
end}
|
end}
|
||||||
@ -1,131 +1,106 @@
|
|||||||
|
--[[
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
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
|
||||||
|
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 not ISTHREAD then
|
if not ISTHREAD then
|
||||||
multi, thread = require("multi").init()
|
multi, thread = require("multi").init()
|
||||||
GLOBAL = multi.integration.GLOBAL
|
GLOBAL = multi.integration.GLOBAL
|
||||||
THREAD = multi.integration.THREAD
|
THREAD = multi.integration.THREAD
|
||||||
|
else
|
||||||
|
GLOBAL = multi.integration.GLOBAL
|
||||||
|
THREAD = multi.integration.THREAD
|
||||||
end
|
end
|
||||||
|
|
||||||
function multi:newSystemThreadedQueue(name)
|
function multi:newSystemThreadedQueue(name)
|
||||||
local name = name or multi.randomString(16)
|
local name = name or multi.randomString(16)
|
||||||
|
|
||||||
local c = {}
|
local c = {}
|
||||||
|
|
||||||
c.Name = name
|
c.Name = name
|
||||||
c.Type = multi.registerType("s_queue")
|
local fRef = {"func",nil}
|
||||||
c.chan = love.thread.getChannel(name)
|
|
||||||
|
|
||||||
function c:push(dat)
|
|
||||||
self.chan:push(THREAD.packValue(dat))
|
|
||||||
end
|
|
||||||
|
|
||||||
function c:pop()
|
|
||||||
return THREAD.unpackValue(self.chan:pop())
|
|
||||||
end
|
|
||||||
|
|
||||||
function c:peek()
|
|
||||||
return THREAD.unpackValue(self.chan:peek())
|
|
||||||
end
|
|
||||||
|
|
||||||
function c:init()
|
function c:init()
|
||||||
self.chan = love.thread.getChannel(self.Name)
|
local q = {}
|
||||||
return self
|
q.chan = love.thread.getChannel(self.Name)
|
||||||
end
|
function q:push(dat)
|
||||||
|
if type(dat) == "function" then
|
||||||
function c:Hold(opt)
|
fRef[2] = THREAD.dump(dat)
|
||||||
local multi, thread = require("multi"):init()
|
self.chan:push(fRef)
|
||||||
if opt.peek then
|
return
|
||||||
return thread.hold(function()
|
|
||||||
return self:peek()
|
|
||||||
end)
|
|
||||||
else
|
else
|
||||||
return thread.hold(function()
|
self.chan:push(dat)
|
||||||
return self:pop()
|
|
||||||
end)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
function q:pop()
|
||||||
GLOBAL[name] = c
|
local dat = self.chan:pop()
|
||||||
|
if type(dat)=="table" and dat[1]=="func" then
|
||||||
self:create(c)
|
return THREAD.loadDump(dat[2])
|
||||||
|
else
|
||||||
|
return dat
|
||||||
|
end
|
||||||
|
end
|
||||||
|
function q:peek()
|
||||||
|
local dat = self.chan:peek()
|
||||||
|
if type(dat)=="table" and dat[1]=="func" then
|
||||||
|
return THREAD.loadDump(dat[2])
|
||||||
|
else
|
||||||
|
return dat
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return q
|
||||||
|
end
|
||||||
|
THREAD.package(name,c)
|
||||||
return c
|
return c
|
||||||
end
|
end
|
||||||
|
|
||||||
function multi:newSystemThreadedTable(name)
|
function multi:newSystemThreadedTable(name)
|
||||||
local name = name or multi.randomString(16)
|
local name = name or multi.randomString(16)
|
||||||
|
|
||||||
local c = {}
|
local c = {}
|
||||||
|
|
||||||
c.Name = name
|
c.Name = name
|
||||||
c.Type = multi.registerType("s_table")
|
|
||||||
c.tab = THREAD.createTable(name)
|
|
||||||
|
|
||||||
function c:init()
|
function c:init()
|
||||||
self.tab = THREAD.createTable(self.Name)
|
return THREAD.createTable(self.Name)
|
||||||
setmetatable(self,{
|
|
||||||
__index = function(t, k)
|
|
||||||
return self.tab[k]
|
|
||||||
end,
|
|
||||||
__newindex = function(t,k,v)
|
|
||||||
self.tab[k] = v
|
|
||||||
end
|
end
|
||||||
})
|
THREAD.package(name,c)
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
c.__init = c.init
|
|
||||||
|
|
||||||
function c:Hold(opt)
|
|
||||||
local multi, thread = require("multi"):init()
|
|
||||||
if opt.key then
|
|
||||||
return thread.hold(function()
|
|
||||||
return self.tab[opt.key]
|
|
||||||
end)
|
|
||||||
else
|
|
||||||
multi.error("Must provide a key to check opt.key = 'key'")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
setmetatable(c,{
|
|
||||||
__index = function(t, k)
|
|
||||||
return c.tab[k]
|
|
||||||
end,
|
|
||||||
__newindex = function(t,k,v)
|
|
||||||
c.tab[k] = v
|
|
||||||
end
|
|
||||||
})
|
|
||||||
|
|
||||||
GLOBAL[name] = c
|
|
||||||
|
|
||||||
self:create(c)
|
|
||||||
|
|
||||||
return c
|
return c
|
||||||
end
|
end
|
||||||
|
|
||||||
local jqc = 1
|
local jqc = 1
|
||||||
function multi:newSystemThreadedJobQueue(n)
|
function multi:newSystemThreadedJobQueue(n)
|
||||||
local c = {}
|
local c = {}
|
||||||
|
|
||||||
c.cores = n or THREAD.getCores()
|
c.cores = n or THREAD.getCores()
|
||||||
c.registerQueue = {}
|
c.registerQueue = {}
|
||||||
c.Type = multi.registerType("s_jobqueue")
|
c.funcs = THREAD.createStaticTable("__JobQueue_"..jqc.."_table")
|
||||||
c.funcs = THREAD.createTable("__JobQueue_"..jqc.."_table")
|
c.queue = love.thread.getChannel("__JobQueue_"..jqc.."_queue")
|
||||||
c.queue = multi:newSystemThreadedQueue("__JobQueue_"..jqc.."_queue")
|
c.queueReturn = love.thread.getChannel("__JobQueue_"..jqc.."_queueReturn")
|
||||||
c.queueReturn = multi:newSystemThreadedQueue("__JobQueue_"..jqc.."_queueReturn")
|
c.queueAll = love.thread.getChannel("__JobQueue_"..jqc.."_queueAll")
|
||||||
c.queueAll = multi:newSystemThreadedQueue("__JobQueue_"..jqc.."_queueAll")
|
|
||||||
c.id = 0
|
c.id = 0
|
||||||
c.OnJobCompleted = multi:newConnection()
|
c.OnJobCompleted = multi:newConnection()
|
||||||
|
|
||||||
local allfunc = 0
|
local allfunc = 0
|
||||||
|
|
||||||
function c:doToAll(func)
|
function c:doToAll(func)
|
||||||
|
local f = THREAD.dump(func)
|
||||||
for i = 1, self.cores do
|
for i = 1, self.cores do
|
||||||
self.queueAll:push({allfunc, func})
|
self.queueAll:push({allfunc,f})
|
||||||
end
|
end
|
||||||
allfunc = allfunc + 1
|
allfunc = allfunc + 1
|
||||||
end
|
end
|
||||||
function c:registerFunction(name,func)
|
function c:registerFunction(name,func)
|
||||||
if self.funcs[name] then
|
if self.funcs[name] then
|
||||||
multi.error("A function by the name "..name.." has already been registered!")
|
error("A function by the name "..name.." has already been registered!")
|
||||||
end
|
end
|
||||||
self.funcs[name] = func
|
self.funcs[name] = func
|
||||||
end
|
end
|
||||||
@ -152,12 +127,13 @@ function multi:newSystemThreadedJobQueue(n)
|
|||||||
local rets
|
local rets
|
||||||
link = c.OnJobCompleted(function(jid,...)
|
link = c.OnJobCompleted(function(jid,...)
|
||||||
if id==jid then
|
if id==jid then
|
||||||
rets = multi.pack(...)
|
rets = {...}
|
||||||
|
link:Destroy()
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
return thread.hold(function()
|
return thread.hold(function()
|
||||||
if rets then
|
if rets then
|
||||||
return multi.unpack(rets) or multi.NIL
|
return unpack(rets) or multi.NIL
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end,holup),name
|
end,holup),name
|
||||||
@ -167,7 +143,7 @@ function multi:newSystemThreadedJobQueue(n)
|
|||||||
thread.yield()
|
thread.yield()
|
||||||
local dat = c.queueReturn:pop()
|
local dat = c.queueReturn:pop()
|
||||||
if dat then
|
if dat then
|
||||||
c.OnJobCompleted:Fire(multi.unpack(dat))
|
c.OnJobCompleted:Fire(unpack(dat))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
@ -175,15 +151,16 @@ function multi:newSystemThreadedJobQueue(n)
|
|||||||
multi:newSystemThread("JobQueue_"..jqc.."_worker_"..i,function(jqc)
|
multi:newSystemThread("JobQueue_"..jqc.."_worker_"..i,function(jqc)
|
||||||
local multi, thread = require("multi"):init()
|
local multi, thread = require("multi"):init()
|
||||||
require("love.timer")
|
require("love.timer")
|
||||||
love.timer.sleep(1)
|
local function atomic(channel)
|
||||||
|
return channel:pop()
|
||||||
|
end
|
||||||
local clock = os.clock
|
local clock = os.clock
|
||||||
local funcs = THREAD.createTable("__JobQueue_"..jqc.."_table")
|
local funcs = THREAD.createStaticTable("__JobQueue_"..jqc.."_table")
|
||||||
local queue = THREAD.waitFor("__JobQueue_"..jqc.."_queue")
|
local queue = love.thread.getChannel("__JobQueue_"..jqc.."_queue")
|
||||||
local queueReturn = THREAD.waitFor("__JobQueue_"..jqc.."_queueReturn")
|
local queueReturn = love.thread.getChannel("__JobQueue_"..jqc.."_queueReturn")
|
||||||
local lastProc = clock()
|
local lastProc = clock()
|
||||||
local queueAll = THREAD.waitFor("__JobQueue_"..jqc.."_queueAll")
|
local queueAll = love.thread.getChannel("__JobQueue_"..jqc.."_queueAll")
|
||||||
local registry = {}
|
local registry = {}
|
||||||
_G["__QR"] = queueReturn
|
|
||||||
setmetatable(_G,{__index = funcs})
|
setmetatable(_G,{__index = funcs})
|
||||||
thread:newThread("startUp",function()
|
thread:newThread("startUp",function()
|
||||||
while true do
|
while true do
|
||||||
@ -191,7 +168,7 @@ function multi:newSystemThreadedJobQueue(n)
|
|||||||
local all = queueAll:peek()
|
local all = queueAll:peek()
|
||||||
if all and not registry[all[1]] then
|
if all and not registry[all[1]] then
|
||||||
lastProc = os.clock()
|
lastProc = os.clock()
|
||||||
queueAll:pop()[2]()
|
THREAD.loadDump(queueAll:pop()[2])()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
@ -202,21 +179,20 @@ function multi:newSystemThreadedJobQueue(n)
|
|||||||
local all = queueAll:peek()
|
local all = queueAll:peek()
|
||||||
if all and not registry[all[1]] then
|
if all and not registry[all[1]] then
|
||||||
lastProc = os.clock()
|
lastProc = os.clock()
|
||||||
queueAll:pop()[2]()
|
THREAD.loadDump(queueAll:pop()[2])()
|
||||||
end
|
end
|
||||||
local dat = thread.hold(queue)
|
local dat = queue:performAtomic(atomic)
|
||||||
if dat then
|
if dat then
|
||||||
multi:newThread("Test",function()
|
|
||||||
lastProc = os.clock()
|
lastProc = os.clock()
|
||||||
local name = table.remove(dat,1)
|
local name = table.remove(dat,1)
|
||||||
local id = table.remove(dat,1)
|
local id = table.remove(dat,1)
|
||||||
local tab = {funcs[name](multi.unpack(dat))}
|
local tab = {funcs[name](unpack(dat))}
|
||||||
table.insert(tab,1,id)
|
table.insert(tab,1,id)
|
||||||
--local test = queueReturn.push
|
|
||||||
queueReturn:push(tab)
|
queueReturn:push(tab)
|
||||||
end)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end):OnError(function(...)
|
||||||
|
error(...)
|
||||||
end)
|
end)
|
||||||
thread:newThread("Idler",function()
|
thread:newThread("Idler",function()
|
||||||
while true do
|
while true do
|
||||||
@ -231,14 +207,145 @@ function multi:newSystemThreadedJobQueue(n)
|
|||||||
multi:mainloop()
|
multi:mainloop()
|
||||||
end,jqc)
|
end,jqc)
|
||||||
end
|
end
|
||||||
|
jqc = jqc + 1
|
||||||
function c:Hold(opt)
|
return c
|
||||||
return thread.hold(self.OnJobCompleted)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
jqc = jqc + 1
|
function multi:newSystemThreadedConnection(name)
|
||||||
|
local name = name or multi.randomString(16)
|
||||||
|
local c = {}
|
||||||
|
c.CONN = 0x00
|
||||||
|
c.TRIG = 0x01
|
||||||
|
c.PING = 0x02
|
||||||
|
c.PONG = 0x03
|
||||||
|
|
||||||
self:create(c)
|
local subscribe = love.thread.getChannel("SUB_STC_" .. name)
|
||||||
|
|
||||||
|
function c:init()
|
||||||
|
|
||||||
|
self.subscribe = love.thread.getChannel("SUB_STC_" .. self.Name)
|
||||||
|
|
||||||
|
function self:Fire(...)
|
||||||
|
local args = {...}
|
||||||
|
if self.CID == THREAD.getID() then -- Host Call
|
||||||
|
for _, link in pairs(self.links) do
|
||||||
|
love.thread.getChannel(link):push{self.TRIG, args}
|
||||||
|
end
|
||||||
|
self.proxy_conn:Fire(...)
|
||||||
|
else
|
||||||
|
self.subscribe:push{self.TRIG, args}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local multi, thread = require("multi"):init()
|
||||||
|
self.links = {}
|
||||||
|
self.proxy_conn = multi:newConnection()
|
||||||
|
local mt = getmetatable(self.proxy_conn)
|
||||||
|
setmetatable(self, {__index = self.proxy_conn, __call = function(t,func) self.proxy_conn(func) end, __add = mt.__add})
|
||||||
|
if self.CID == THREAD.getID() then return self end
|
||||||
|
thread:newThread("STC_CONN_MAN" .. self.Name,function()
|
||||||
|
local item
|
||||||
|
local string_self_ref = "LSF_" .. multi.randomString(16)
|
||||||
|
local link_self_ref = love.thread.getChannel(string_self_ref)
|
||||||
|
self.subscribe:push{self.CONN, string_self_ref}
|
||||||
|
while true do
|
||||||
|
item = thread.hold(function()
|
||||||
|
return link_self_ref:peek()
|
||||||
|
end)
|
||||||
|
if item[1] == self.PING then
|
||||||
|
link_self_ref:push{self.PONG}
|
||||||
|
link_self_ref:pop()
|
||||||
|
elseif item[1] == self.CONN then
|
||||||
|
if string_self_ref ~= item[2] then
|
||||||
|
table.insert(self.links, love.thread.getChannel(item[2]))
|
||||||
|
end
|
||||||
|
link_self_ref:pop()
|
||||||
|
elseif item[1] == self.TRIG then
|
||||||
|
self.proxy_conn:Fire(unpack(item[2]))
|
||||||
|
link_self_ref:pop()
|
||||||
|
else
|
||||||
|
-- This shouldn't be the case
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end).OnError(print)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
local function remove(a, b)
|
||||||
|
local ai = {}
|
||||||
|
local r = {}
|
||||||
|
for k,v in pairs(a) do ai[v]=true end
|
||||||
|
for k,v in pairs(b) do
|
||||||
|
if ai[v]==nil then table.insert(r,a[k]) end
|
||||||
|
end
|
||||||
|
return r
|
||||||
|
end
|
||||||
|
c.CID = THREAD.getID()
|
||||||
|
c.Name = name
|
||||||
|
c.links = {} -- All triggers sent from main connection. When a connection is triggered on another thread, they speak to the main then send stuff out.
|
||||||
|
-- Locals will only live in the thread that creates the original object
|
||||||
|
local ping
|
||||||
|
local pong = function(link, links)
|
||||||
|
local res = thread.hold(function()
|
||||||
|
return love.thread.getChannel(link):peek()[1] == c.PONG
|
||||||
|
end,{sleep=3})
|
||||||
|
|
||||||
|
if not res then
|
||||||
|
for i=1,#links do
|
||||||
|
if links[i] == link then
|
||||||
|
table.remove(links,i,link)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
love.thread.getChannel(link):pop()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ping = thread:newFunction(function(self)
|
||||||
|
ping:Pause()
|
||||||
|
|
||||||
|
multi.ForEach(self.links, function(link) -- Sync new connections
|
||||||
|
love.thread.getChannel(link):push{self.PING}
|
||||||
|
multi:newThread("pong Thread", pong, link, self.links)
|
||||||
|
end)
|
||||||
|
|
||||||
|
thread.sleep(3)
|
||||||
|
|
||||||
|
ping:Resume()
|
||||||
|
end,false)
|
||||||
|
|
||||||
|
local function fire(...)
|
||||||
|
for _, link in pairs(c.links) do
|
||||||
|
love.thread.getChannel(link):push {c.TRIG, {...}}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
thread:newThread("STC_SUB_MAN"..name,function()
|
||||||
|
local item
|
||||||
|
while true do
|
||||||
|
thread.yield()
|
||||||
|
-- We need to check on broken connections
|
||||||
|
ping(c) -- Should return instantlly and process this in another thread
|
||||||
|
item = thread.hold(function() -- This will keep things held up until there is something to process
|
||||||
|
return c.subscribe:pop()
|
||||||
|
end)
|
||||||
|
if item[1] == c.CONN then
|
||||||
|
|
||||||
|
multi.ForEach(c.links, function(link) -- Sync new connections
|
||||||
|
love.thread.getChannel(item[2]):push{c.CONN, link}
|
||||||
|
end)
|
||||||
|
c.links[#c.links+1] = item[2]
|
||||||
|
|
||||||
|
elseif item[1] == c.TRIG then
|
||||||
|
fire(unpack(item[2]))
|
||||||
|
c.proxy_conn:Fire(unpack(item[2]))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end).OnError(print)
|
||||||
|
--- ^^^ This will only exist in the init thread
|
||||||
|
|
||||||
|
THREAD.package(name,c)
|
||||||
|
|
||||||
return c
|
return c
|
||||||
end
|
end
|
||||||
@ -1,137 +1,122 @@
|
|||||||
|
--[[
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
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
|
||||||
|
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 ISTHREAD then
|
if ISTHREAD then
|
||||||
error("You cannot require the loveManager from within a thread!")
|
error("You cannot require the loveManager from within a thread!")
|
||||||
end
|
end
|
||||||
|
|
||||||
local ThreadFileData = [[
|
local ThreadFileData = [[
|
||||||
ISTHREAD = true
|
ISTHREAD = true
|
||||||
args = {...}
|
THREAD = require("multi.integration.loveManager.threads")
|
||||||
THREAD_ID = args[1]
|
sThread = THREAD
|
||||||
THREAD_NAME = args[2]
|
__IMPORTS = {...}
|
||||||
GLOBAL, THREAD, DEFER = require("multi.integration.loveManager.threads"):init()
|
__FUNC__=table.remove(__IMPORTS,1)
|
||||||
__FUNC = THREAD.unpackValue(args[3])
|
__THREADID__=table.remove(__IMPORTS,1)
|
||||||
ARGS = THREAD.unpackValue(args[4])
|
__THREADNAME__=table.remove(__IMPORTS,1)
|
||||||
settings = args[5]
|
math.randomseed(__THREADID__)
|
||||||
if ARGS == nil then ARGS = {} end
|
|
||||||
math.randomseed(THREAD_ID)
|
|
||||||
math.random()
|
math.random()
|
||||||
math.random()
|
math.random()
|
||||||
math.random()
|
math.random()
|
||||||
stab = THREAD.createTable(THREAD_NAME .. THREAD_ID)
|
stab = THREAD.createStaticTable(__THREADNAME__)
|
||||||
if GLOBAL["__env"] then
|
GLOBAL = THREAD.getGlobal()
|
||||||
local env = THREAD.getENV()
|
multi, thread = require("multi").init()
|
||||||
for i,v in pairs(env) do
|
|
||||||
_G[i] = v
|
|
||||||
end
|
|
||||||
end
|
|
||||||
multi, thread = require("multi"):init{error=true, warning=true, print=true, priority=true}
|
|
||||||
multi.defaultSettings.print = true
|
|
||||||
require("multi.integration.loveManager.extensions")
|
|
||||||
require("multi.integration.sharedExtensions")
|
|
||||||
local returns = {pcall(__FUNC, multi.unpack(ARGS))}
|
|
||||||
table.remove(returns,1)
|
|
||||||
stab["returns"] = returns
|
|
||||||
for i,v in pairs(DEFER) do
|
|
||||||
pcall(v)
|
|
||||||
end
|
|
||||||
]]
|
|
||||||
|
|
||||||
_G.THREAD_NAME = "MAIN_THREAD"
|
|
||||||
_G.THREAD_ID = 0
|
|
||||||
|
|
||||||
local multi, thread = require("multi"):init()
|
|
||||||
local GLOBAL, THREAD = require("multi.integration.loveManager.threads"):init()
|
|
||||||
|
|
||||||
multi.registerType("s_function")
|
|
||||||
multi.registerType("s_thread")
|
|
||||||
|
|
||||||
multi.integration={}
|
multi.integration={}
|
||||||
multi.isMainThread = true
|
multi.integration.GLOBAL = GLOBAL
|
||||||
local threads = {}
|
multi.integration.THREAD = THREAD
|
||||||
local tid = 0
|
pcall(require,"multi.integration.loveManager.extensions")
|
||||||
|
stab["returns"] = {THREAD.loadDump(__FUNC__)(unpack(__IMPORTS))}
|
||||||
|
]]
|
||||||
|
local multi, thread = require("multi"):init()
|
||||||
|
local THREAD = {}
|
||||||
|
__THREADID__ = 0
|
||||||
|
__THREADNAME__ = "MainThread"
|
||||||
|
multi.integration={}
|
||||||
|
local THREAD = require("multi.integration.loveManager.threads")
|
||||||
|
local GLOBAL = THREAD.getGlobal()
|
||||||
|
local THREAD_ID = 1
|
||||||
|
local OBJECT_ID = 0
|
||||||
|
local stf = 0
|
||||||
|
|
||||||
function multi:newSystemThread(name,func,...)
|
function multi:newSystemThread(name,func,...)
|
||||||
multi.InitSystemThreadErrorHandler()
|
|
||||||
local name = name or multi.randomString(16)
|
|
||||||
tid = tid + 1
|
|
||||||
local c = {}
|
local c = {}
|
||||||
c.Type = multi.STHREAD
|
c.name = name
|
||||||
c.Name = name
|
c.ID=THREAD_ID
|
||||||
c.ID = tid
|
|
||||||
c.thread=love.thread.newThread(ThreadFileData)
|
c.thread=love.thread.newThread(ThreadFileData)
|
||||||
c.thread:start(c.ID, c.Name, THREAD.packValue(func), THREAD.packValue({...}), multi.defaultSettings)
|
c.thread:start(THREAD.dump(func),c.ID,c.name,...)
|
||||||
c.stab = THREAD.createTable(name .. c.ID)
|
c.stab = THREAD.createStaticTable(name)
|
||||||
c.creationTime = os.clock()
|
|
||||||
c.OnDeath = multi:newConnection()
|
c.OnDeath = multi:newConnection()
|
||||||
c.OnError = multi:newConnection()
|
c.OnError = multi:newConnection()
|
||||||
c.status_channel = love.thread.getChannel("__status_channel__" .. c.ID)
|
GLOBAL["__THREAD_"..c.ID] = {ID=c.ID, Name=c.name, Thread=c.thread}
|
||||||
|
GLOBAL["__THREAD_COUNT"] = THREAD_ID
|
||||||
function c:getName() return c.name end
|
THREAD_ID=THREAD_ID + 1
|
||||||
|
function c:getName()
|
||||||
table.insert(threads, c)
|
return c.name
|
||||||
|
|
||||||
c.OnError(multi.error)
|
|
||||||
|
|
||||||
if self.isActor then
|
|
||||||
self:create(c)
|
|
||||||
else
|
|
||||||
multi.create(multi, c)
|
|
||||||
end
|
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
|
||||||
|
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
|
return c
|
||||||
end
|
end
|
||||||
|
|
||||||
local started = false
|
function THREAD:newFunction(func)
|
||||||
local console_channel = love.thread.getChannel("__console_channel__")
|
|
||||||
|
|
||||||
function THREAD:newFunction(func, holdme)
|
|
||||||
return thread:newFunctionBase(function(...)
|
return thread:newFunctionBase(function(...)
|
||||||
return multi:newSystemThread("SystemThreaded Function Handler", func, ...)
|
return multi:newSystemThread("TempSystemThread"..THREAD_ID,func,...)
|
||||||
end, holdme, multi.SFUNCTION)()
|
end)()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
THREAD.newSystemThread = multi.newSystemThread
|
||||||
|
|
||||||
function love.threaderror(thread, errorstr)
|
function love.threaderror(thread, errorstr)
|
||||||
multi.error("Thread error! " .. errorstr)
|
multi.print("Thread error!\n"..errorstr)
|
||||||
end
|
|
||||||
|
|
||||||
function multi.InitSystemThreadErrorHandler()
|
|
||||||
if started == true then return end
|
|
||||||
started = true
|
|
||||||
thread:newThread("Love System Thread Handler", function()
|
|
||||||
while true do
|
|
||||||
thread.yield()
|
|
||||||
for i = #threads, 1, -1 do
|
|
||||||
local th = threads[i]
|
|
||||||
if th.status_channel:peek() ~= nil then
|
|
||||||
th.statusconnector:Fire(multi.unpack(th.status_channel:pop()))
|
|
||||||
end
|
|
||||||
local th_err = th.thread:getError()
|
|
||||||
if th_err == "Thread Killed!\1" then
|
|
||||||
th.OnDeath:Fire("Thread Killed!")
|
|
||||||
table.remove(threads, i)
|
|
||||||
elseif th_err then
|
|
||||||
th.OnError:Fire(th, th_err)
|
|
||||||
table.remove(threads, i)
|
|
||||||
elseif th.stab.returns then
|
|
||||||
th.OnDeath:Fire(multi.unpack(th.stab.returns))
|
|
||||||
th.stab.returns = nil
|
|
||||||
table.remove(threads, i)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
THREAD.newSystemThread = function(...)
|
|
||||||
multi:newSystemThread(...)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
multi.integration.GLOBAL = GLOBAL
|
multi.integration.GLOBAL = GLOBAL
|
||||||
multi.integration.THREAD = THREAD
|
multi.integration.THREAD = THREAD
|
||||||
require("multi.integration.loveManager.extensions")
|
require("multi.integration.loveManager.extensions")
|
||||||
require("multi.integration.sharedExtensions")
|
|
||||||
multi.print("Integrated Love Threading!")
|
multi.print("Integrated Love Threading!")
|
||||||
|
return {init=function()
|
||||||
return {
|
|
||||||
init = function()
|
|
||||||
return GLOBAL,THREAD
|
return GLOBAL,THREAD
|
||||||
end
|
end}
|
||||||
}
|
|
||||||
@ -25,96 +25,144 @@ require("love.timer")
|
|||||||
require("love.system")
|
require("love.system")
|
||||||
require("love.data")
|
require("love.data")
|
||||||
require("love.thread")
|
require("love.thread")
|
||||||
local multi, thread = require("multi"):init()
|
local socket = require("socket")
|
||||||
|
local multi, thread = require("multi").init()
|
||||||
|
local threads = {}
|
||||||
|
|
||||||
-- Checks if the given value is a LOVE2D object (i.e. has metatable with __index field) and if that __index field contains functions typical of LOVE2D objects
|
function threads.loadDump(d)
|
||||||
function isLoveObject(value)
|
return loadstring(d:getString())
|
||||||
-- Check if the value has metatable
|
|
||||||
if type(value) == "userdata" and getmetatable(value) then
|
|
||||||
-- Check if the metatable has the __index field
|
|
||||||
local index = getmetatable(value).__index
|
|
||||||
if type(index) == "table" then
|
|
||||||
-- Check if the metatable's __index table contains functions typical of LOVE2D objects
|
|
||||||
if index.draw or index.update or index.getWidth or index.getHeight or index.getString or index.getPointer then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Converts any function values in a table to a string with the value "\1\2:func:<function_string>" where <function_string> is the Lua stringified version of the function
|
function threads.dump(func)
|
||||||
function tableToFunctionString(t)
|
return love.data.newByteData(string.dump(func))
|
||||||
if type(t) == "nil" then return "\1\2:nil:" end
|
|
||||||
if type(t) == "function" then return "\1\2:func:"..string.dump(t) end
|
|
||||||
if type(t) ~= "table" then return t end
|
|
||||||
local newtable = {}
|
|
||||||
for k, v in pairs(t) do
|
|
||||||
if type(v) == "function" then
|
|
||||||
newtable[k] = "\1\2:func:"..string.dump(v)
|
|
||||||
elseif type(v) == "table" then
|
|
||||||
newtable[k] = tableToFunctionString(v)
|
|
||||||
elseif isLoveObject(v) then
|
|
||||||
newtable[k] = v
|
|
||||||
elseif type(v) == "userdata" then
|
|
||||||
newtable[k] = tostring(v)
|
|
||||||
else
|
|
||||||
newtable[k] = v
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return newtable
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Converts strings with the value "\1\2:func:<function_string>" back to functions
|
local fRef = {"func",nil}
|
||||||
function functionStringToTable(t)
|
local function manage(channel, value)
|
||||||
if type(t) == "string" and t:sub(1, 8) == "\1\2:func:" then return loadstring(t:sub(9, -1)) end
|
channel:clear()
|
||||||
if type(t) == "string" and t:sub(1, 7) == "\1\2:nil:" then return nil end
|
if type(value) == "function" then
|
||||||
if type(t) ~= "table" then return t end
|
fRef[2] = THREAD.dump(value)
|
||||||
for k, v in pairs(t) do
|
channel:push(fRef)
|
||||||
if type(v) == "string" then
|
return
|
||||||
if v:sub(1, 8) == "\1\2:func:" then
|
|
||||||
t[k] = loadstring(v:sub(9, -1))
|
|
||||||
else
|
else
|
||||||
t[k] = v
|
channel:push(value)
|
||||||
end
|
end
|
||||||
elseif type(v) == "table" then
|
end
|
||||||
t[k] = functionStringToTable(v)
|
|
||||||
|
local function RandomVariable(length)
|
||||||
|
local res = {}
|
||||||
|
math.randomseed(socket.gettime()*10000)
|
||||||
|
for i = 1, length do
|
||||||
|
res[#res+1] = string.char(math.random(97, 122))
|
||||||
|
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()
|
||||||
|
if type(dat)=="table" and dat[1]=="func" then
|
||||||
|
return THREAD.loadDump(dat[2])
|
||||||
else
|
else
|
||||||
t[k] = v
|
return dat
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if t.init then
|
|
||||||
t:init()
|
function threads.waitFor(name)
|
||||||
|
if thread.isThread() then
|
||||||
|
return thread.hold(function()
|
||||||
|
return threads.get(name)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
while threads.get(name)==nil do
|
||||||
|
love.timer.sleep(.001)
|
||||||
|
end
|
||||||
|
local dat = threads.get(name)
|
||||||
|
if type(dat) == "table" and dat.init then
|
||||||
|
dat.init = threads.loadDump(dat.init)
|
||||||
|
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 threads.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
|
||||||
|
t[#t+1]=GLOBAL["__THREAD_"..i]
|
||||||
end
|
end
|
||||||
return t
|
return t
|
||||||
end
|
end
|
||||||
|
|
||||||
local function packValue(t)
|
function threads.getThread(n)
|
||||||
return tableToFunctionString(t)
|
return GLOBAL["__THREAD_"..n]
|
||||||
end
|
end
|
||||||
|
|
||||||
local function unpackValue(t)
|
function threads.getName()
|
||||||
return functionStringToTable(t)
|
return __THREADNAME__
|
||||||
end
|
end
|
||||||
|
|
||||||
local function createTable(n)
|
function threads.getID()
|
||||||
if not n then
|
return __THREADID__
|
||||||
n = "STAB"..multi.randomString(8)
|
|
||||||
end
|
end
|
||||||
local __proxy = {}
|
|
||||||
|
function threads.sleep(n)
|
||||||
|
love.timer.sleep(n)
|
||||||
|
end
|
||||||
|
|
||||||
|
function threads.getGlobal()
|
||||||
|
return setmetatable({},
|
||||||
|
{
|
||||||
|
__index = function(t, k)
|
||||||
|
return THREAD.get(k)
|
||||||
|
end,
|
||||||
|
__newindex = function(t, k, v)
|
||||||
|
THREAD.set(k,v)
|
||||||
|
end
|
||||||
|
}
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
function threads.createTable(n)
|
||||||
|
local _proxy = {}
|
||||||
local function set(name,val)
|
local function set(name,val)
|
||||||
local chan = love.thread.getChannel(n .. name)
|
if not _proxy[name] then _proxy[name] = love.thread.getChannel(n..name) end
|
||||||
if chan:getCount() == 1 then chan:pop() end
|
_proxy[name]:performAtomic(manage, val)
|
||||||
__proxy[name] = true
|
|
||||||
chan:push(packValue(val))
|
|
||||||
end
|
end
|
||||||
local function get(name)
|
local function get(name)
|
||||||
return unpackValue(love.thread.getChannel(n .. name):peek())
|
if not _proxy[name] then _proxy[name] = love.thread.getChannel(n..name) end
|
||||||
-- if type(data) == "table" and data.init then
|
local dat = _proxy[name]:peek()
|
||||||
-- return data:init()
|
if type(dat)=="table" and dat[1]=="func" then
|
||||||
-- else
|
return THREAD.loadDump(dat[2])
|
||||||
-- return data
|
else
|
||||||
-- end
|
return dat
|
||||||
|
end
|
||||||
end
|
end
|
||||||
return setmetatable({},
|
return setmetatable({},
|
||||||
{
|
{
|
||||||
@ -128,101 +176,75 @@ local function createTable(n)
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
function INIT()
|
function threads.getConsole()
|
||||||
local GLOBAL, THREAD, DEFER = createTable("__GLOBAL__"), {}, {}
|
|
||||||
local status_channel, console_channel = love.thread.getChannel("__status_channel__" .. THREAD_ID),
|
|
||||||
love.thread.getChannel("__console_channel__")
|
|
||||||
|
|
||||||
-- Non portable methods, shouldn't be used unless you know what you are doing
|
|
||||||
THREAD.packValue = packValue
|
|
||||||
THREAD.unpackValue = unpackValue
|
|
||||||
THREAD.createTable = createTable
|
|
||||||
|
|
||||||
function THREAD.set(name, val)
|
|
||||||
GLOBAL[name] = val
|
|
||||||
end
|
|
||||||
|
|
||||||
function THREAD.get(name, val)
|
|
||||||
return GLOBAL[name]
|
|
||||||
end
|
|
||||||
|
|
||||||
THREAD.waitFor = thread:newFunction(function(name)
|
|
||||||
local function wait()
|
|
||||||
math.randomseed(os.time())
|
|
||||||
thread.yield()
|
|
||||||
end
|
|
||||||
repeat
|
|
||||||
wait()
|
|
||||||
until GLOBAL[name] ~= nil
|
|
||||||
return GLOBAL[name]
|
|
||||||
end, true)
|
|
||||||
|
|
||||||
function THREAD.getCores()
|
|
||||||
return love.system.getProcessorCount()
|
|
||||||
end
|
|
||||||
|
|
||||||
function THREAD.getConsole()
|
|
||||||
local c = {}
|
local c = {}
|
||||||
c.queue = console_channel
|
c.queue = love.thread.getChannel("__CONSOLE__")
|
||||||
function c.print(...)
|
function c.print(...)
|
||||||
c.queue:push(table.concat(multi.pack(...), "\t"))
|
c.queue:push{...}
|
||||||
end
|
end
|
||||||
function c.error(err)
|
function c.error(err)
|
||||||
c.queue:push("Error in <"..THREAD_NAME..":" .. THREAD_ID .. ">: ".. err)
|
c.queue:push{"ERROR in <"..__THREADNAME__..">: "..err,__THREADID__}
|
||||||
multi.error(err)
|
error(err)
|
||||||
end
|
end
|
||||||
return c
|
return c
|
||||||
end
|
end
|
||||||
|
|
||||||
function THREAD.getThreads()
|
if not ISTHREAD then
|
||||||
--
|
local clock = os.clock
|
||||||
|
local lastproc = clock()
|
||||||
|
local queue = love.thread.getChannel("__CONSOLE__")
|
||||||
|
thread:newThread("consoleManager",function()
|
||||||
|
while true do
|
||||||
|
thread.yield()
|
||||||
|
dat = queue:pop()
|
||||||
|
if dat then
|
||||||
|
lastproc = clock()
|
||||||
|
print(unpack(dat))
|
||||||
end
|
end
|
||||||
|
if clock()-lastproc>2 then
|
||||||
function THREAD.kill() -- trigger the lane destruction
|
thread.sleep(.1)
|
||||||
error("Thread was killed!\1")
|
|
||||||
end
|
|
||||||
|
|
||||||
function THREAD.pushStatus(...)
|
|
||||||
status_channel:push(multi.pack(...))
|
|
||||||
end
|
|
||||||
|
|
||||||
function THREAD.sleep(n)
|
|
||||||
love.timer.sleep(n)
|
|
||||||
end
|
|
||||||
|
|
||||||
THREAD.hold = thread:newFunction(function(n)
|
|
||||||
thread.hold(n)
|
|
||||||
end, true)
|
|
||||||
|
|
||||||
function THREAD.setENV(env, name)
|
|
||||||
GLOBAL[name or "__env"] = env
|
|
||||||
end
|
|
||||||
|
|
||||||
function THREAD.getENV(name)
|
|
||||||
return GLOBAL[name or "__env"]
|
|
||||||
end
|
|
||||||
|
|
||||||
function THREAD.exposeENV(name)
|
|
||||||
name = name or "__env"
|
|
||||||
local env = THREAD.getENV(name)
|
|
||||||
for i,v in pairs(env) do
|
|
||||||
_G[i] = v
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end)
|
||||||
function THREAD.defer(func)
|
|
||||||
table.insert(DEFER, func)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function THREAD.sync()
|
function threads.createStaticTable(n)
|
||||||
-- Maybe do something...
|
local __proxy = {}
|
||||||
|
local function set(name,val)
|
||||||
|
if __proxy[name] then return end
|
||||||
|
local chan = love.thread.getChannel(n..name)
|
||||||
|
if chan:getCount()>0 then return end
|
||||||
|
chan:performAtomic(manage, val)
|
||||||
|
__proxy[name] = val
|
||||||
end
|
end
|
||||||
|
local function get(name)
|
||||||
return GLOBAL, THREAD, DEFER
|
if __proxy[name] then return __proxy[name] end
|
||||||
|
local dat = love.thread.getChannel(n..name):peek()
|
||||||
|
if type(dat)=="table" and dat[1]=="func" then
|
||||||
|
__proxy[name] = THREAD.loadDump(dat[2])
|
||||||
|
return __proxy[name]
|
||||||
|
else
|
||||||
|
__proxy[name] = dat
|
||||||
|
return __proxy[name]
|
||||||
end
|
end
|
||||||
|
end
|
||||||
return {
|
return setmetatable({},
|
||||||
init = function()
|
{
|
||||||
return INIT()
|
__index = function(t, k)
|
||||||
|
return get(k)
|
||||||
|
end,
|
||||||
|
__newindex = function(t, k, v)
|
||||||
|
set(k,v)
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
function threads.hold(n)
|
||||||
|
local dat
|
||||||
|
while not(dat) do
|
||||||
|
dat = n()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return threads
|
||||||
@ -118,13 +118,13 @@ function multi:newSystemThreadedJobQueue(n)
|
|||||||
local rets
|
local rets
|
||||||
link = c.OnJobCompleted(function(jid,...)
|
link = c.OnJobCompleted(function(jid,...)
|
||||||
if id==jid then
|
if id==jid then
|
||||||
rets = multi.pack(...)
|
rets = {...}
|
||||||
link:Destroy()
|
link:Destroy()
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
return thread.hold(function()
|
return thread.hold(function()
|
||||||
if rets then
|
if rets then
|
||||||
return multi.unpack(rets) or multi.NIL
|
return unpack(rets) or multi.NIL
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end,holup),name
|
end,holup),name
|
||||||
@ -134,7 +134,7 @@ function multi:newSystemThreadedJobQueue(n)
|
|||||||
thread.yield()
|
thread.yield()
|
||||||
local dat = c.queueReturn:pop()
|
local dat = c.queueReturn:pop()
|
||||||
if dat then
|
if dat then
|
||||||
c.OnJobCompleted:Fire(multi.unpack(dat))
|
c.OnJobCompleted:Fire(unpack(dat))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
@ -152,7 +152,6 @@ function multi:newSystemThreadedJobQueue(n)
|
|||||||
local lastProc = clock()
|
local lastProc = clock()
|
||||||
local queueAll = lovr.thread.getChannel("__JobQueue_"..jqc.."_queueAll")
|
local queueAll = lovr.thread.getChannel("__JobQueue_"..jqc.."_queueAll")
|
||||||
local registry = {}
|
local registry = {}
|
||||||
_G["__QR"] = queueReturn
|
|
||||||
setmetatable(_G,{__index = funcs})
|
setmetatable(_G,{__index = funcs})
|
||||||
thread:newThread("startUp",function()
|
thread:newThread("startUp",function()
|
||||||
while true do
|
while true do
|
||||||
@ -178,7 +177,7 @@ function multi:newSystemThreadedJobQueue(n)
|
|||||||
lastProc = os.clock()
|
lastProc = os.clock()
|
||||||
local name = table.remove(dat,1)
|
local name = table.remove(dat,1)
|
||||||
local id = table.remove(dat,1)
|
local id = table.remove(dat,1)
|
||||||
local tab = {funcs[name](multi.unpack(dat))}
|
local tab = {funcs[name](unpack(dat))}
|
||||||
table.insert(tab,1,id)
|
table.insert(tab,1,id)
|
||||||
queueReturn:push(tab)
|
queueReturn:push(tab)
|
||||||
end
|
end
|
||||||
|
|||||||
@ -36,14 +36,12 @@ __THREADNAME__=table.remove(__IMPORTS,1)
|
|||||||
stab = THREAD.createStaticTable(__THREADNAME__)
|
stab = THREAD.createStaticTable(__THREADNAME__)
|
||||||
GLOBAL = THREAD.getGlobal()
|
GLOBAL = THREAD.getGlobal()
|
||||||
multi, thread = require("multi").init()
|
multi, thread = require("multi").init()
|
||||||
stab["returns"] = {THREAD.loadDump(__FUNC__)(multi.unpack(__IMPORTS))}
|
stab["returns"] = {THREAD.loadDump(__FUNC__)(unpack(__IMPORTS))}
|
||||||
]]
|
]]
|
||||||
local multi, thread = require("multi.compat.lovr2d"):init()
|
local multi, thread = require("multi.compat.lovr2d"):init()
|
||||||
local THREAD = {}
|
local THREAD = {}
|
||||||
__THREADID__ = 0
|
__THREADID__ = 0
|
||||||
__THREADNAME__ = "MainThread"
|
__THREADNAME__ = "MainThread"
|
||||||
_G.THREAD_NAME = "MAIN_THREAD"
|
|
||||||
_G.THREAD_ID = 0
|
|
||||||
multi.integration={}
|
multi.integration={}
|
||||||
multi.integration.lovr2d={}
|
multi.integration.lovr2d={}
|
||||||
local THREAD = require("multi.integration.lovrManager.threads")
|
local THREAD = require("multi.integration.lovrManager.threads")
|
||||||
@ -60,7 +58,7 @@ function THREAD:newFunction(func,holup)
|
|||||||
if t.stab["returns"] then
|
if t.stab["returns"] then
|
||||||
local dat = t.stab.returns
|
local dat = t.stab.returns
|
||||||
t.stab.returns = nil
|
t.stab.returns = nil
|
||||||
return multi.unpack(dat)
|
return unpack(dat)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end,holup)()
|
end,holup)()
|
||||||
@ -76,23 +74,16 @@ function multi:newSystemThread(name,func,...)
|
|||||||
GLOBAL["__THREAD_"..c.ID] = {ID=c.ID,Name=c.name,Thread=c.thread}
|
GLOBAL["__THREAD_"..c.ID] = {ID=c.ID,Name=c.name,Thread=c.thread}
|
||||||
GLOBAL["__THREAD_COUNT"] = THREAD_ID
|
GLOBAL["__THREAD_COUNT"] = THREAD_ID
|
||||||
THREAD_ID=THREAD_ID+1
|
THREAD_ID=THREAD_ID+1
|
||||||
|
|
||||||
if self.isActor then
|
|
||||||
self:create(c)
|
|
||||||
else
|
|
||||||
multi.create(multi, c)
|
|
||||||
end
|
|
||||||
|
|
||||||
return c
|
return c
|
||||||
end
|
end
|
||||||
THREAD.newSystemThread = multi.newSystemThread
|
THREAD.newSystemThread = multi.newSystemThread
|
||||||
function lovr.threaderror(thread, errorstr)
|
function lovr.threaderror(thread, errorstr)
|
||||||
multi.print("Thread error!\n"..errorstr)
|
print("Thread error!\n"..errorstr)
|
||||||
end
|
end
|
||||||
multi.integration.GLOBAL = GLOBAL
|
multi.integration.GLOBAL = GLOBAL
|
||||||
multi.integration.THREAD = THREAD
|
multi.integration.THREAD = THREAD
|
||||||
require("multi.integration.lovrManager.extensions")
|
require("multi.integration.lovrManager.extensions")
|
||||||
multi.print("Integrated lovr Threading!")
|
print("Integrated lovr Threading!")
|
||||||
return {init=function()
|
return {init=function()
|
||||||
return GLOBAL,THREAD
|
return GLOBAL,THREAD
|
||||||
end}
|
end}
|
||||||
@ -156,7 +156,7 @@ function threads.getConsole()
|
|||||||
local c = {}
|
local c = {}
|
||||||
c.queue = lovr.thread.getChannel("__CONSOLE__")
|
c.queue = lovr.thread.getChannel("__CONSOLE__")
|
||||||
function c.print(...)
|
function c.print(...)
|
||||||
c.queue:push(multi.pack(...))
|
c.queue:push{...}
|
||||||
end
|
end
|
||||||
function c.error(err)
|
function c.error(err)
|
||||||
c.queue:push{"ERROR in <"..__THREADNAME__..">: "..err,__THREADID__}
|
c.queue:push{"ERROR in <"..__THREADNAME__..">: "..err,__THREADID__}
|
||||||
@ -174,7 +174,7 @@ if not ISTHREAD then
|
|||||||
dat = queue:pop()
|
dat = queue:pop()
|
||||||
if dat then
|
if dat then
|
||||||
lastproc = clock()
|
lastproc = clock()
|
||||||
print(multi.unpack(dat))
|
print(unpack(dat))
|
||||||
end
|
end
|
||||||
if clock()-lastproc>2 then
|
if clock()-lastproc>2 then
|
||||||
thread.sleep(.1)
|
thread.sleep(.1)
|
||||||
|
|||||||
@ -35,7 +35,7 @@ local function _INIT(luvitThread, timer)
|
|||||||
end
|
end
|
||||||
-- Step 1 get setup threads on luvit... Sigh how do i even...
|
-- Step 1 get setup threads on luvit... Sigh how do i even...
|
||||||
local multi, thread = require("multi").init()
|
local multi, thread = require("multi").init()
|
||||||
multi.isMainThread = true
|
isMainThread = true
|
||||||
function multi:canSystemThread()
|
function multi:canSystemThread()
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
@ -107,7 +107,7 @@ local function _INIT(luvitThread, timer)
|
|||||||
local c = {}
|
local c = {}
|
||||||
local __self = c
|
local __self = c
|
||||||
c.name = name
|
c.name = name
|
||||||
c.Type = multi.STHREAD
|
c.Type = "sthread"
|
||||||
c.thread = {}
|
c.thread = {}
|
||||||
c.func = string.dump(func)
|
c.func = string.dump(func)
|
||||||
function c:kill()
|
function c:kill()
|
||||||
|
|||||||
141
integration/pesudoManager/extensions.lua
Normal file
141
integration/pesudoManager/extensions.lua
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
--[[
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
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
|
||||||
|
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.
|
||||||
|
]]
|
||||||
|
local multi, thread = require("multi"):init()
|
||||||
|
local GLOBAL, THREAD = multi.integration.GLOBAL,multi.integration.THREAD
|
||||||
|
|
||||||
|
local function stripUpValues(func)
|
||||||
|
local dmp = string.dump(func)
|
||||||
|
if setfenv then
|
||||||
|
return loadstring(dmp,"IsolatedThread_PesudoThreading")
|
||||||
|
else
|
||||||
|
return load(dmp,"IsolatedThread_PesudoThreading","bt")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function multi:newSystemThreadedQueue(name)
|
||||||
|
local c = {}
|
||||||
|
function c:push(v)
|
||||||
|
table.insert(self,v)
|
||||||
|
end
|
||||||
|
function c:pop()
|
||||||
|
return table.remove(self,1)
|
||||||
|
end
|
||||||
|
function c:peek()
|
||||||
|
return self[1]
|
||||||
|
end
|
||||||
|
function c:init()
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
GLOBAL[name or "_"] = c
|
||||||
|
return c
|
||||||
|
end
|
||||||
|
function multi:newSystemThreadedTable(name)
|
||||||
|
local c = {}
|
||||||
|
function c:init()
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
GLOBAL[name or "_"] = c
|
||||||
|
return c
|
||||||
|
end
|
||||||
|
local setfenv = setfenv
|
||||||
|
if not setfenv then
|
||||||
|
if not debug then
|
||||||
|
multi.print("Unable to implement setfenv in lua 5.2+ the debug module is not available!")
|
||||||
|
else
|
||||||
|
setfenv = function(f, env)
|
||||||
|
return load(string.dump(f), nil, nil, env)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
function multi:newSystemThreadedJobQueue(n)
|
||||||
|
local c = {}
|
||||||
|
c.cores = n or THREAD.getCores()*2
|
||||||
|
c.OnJobCompleted = multi:newConnection()
|
||||||
|
local jobs = {}
|
||||||
|
local ID=1
|
||||||
|
local jid = 1
|
||||||
|
local env = {}
|
||||||
|
setmetatable(env,{
|
||||||
|
__index = _G
|
||||||
|
})
|
||||||
|
local funcs = {}
|
||||||
|
function c:doToAll(func)
|
||||||
|
setfenv(func,env)()
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
function c:registerFunction(name,func)
|
||||||
|
funcs[name] = setfenv(func,env)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
function c:pushJob(name,...)
|
||||||
|
table.insert(jobs,{name,jid,{...}})
|
||||||
|
jid = jid + 1
|
||||||
|
return jid-1
|
||||||
|
end
|
||||||
|
function c:isEmpty()
|
||||||
|
print(#jobs)
|
||||||
|
return #jobs == 0
|
||||||
|
end
|
||||||
|
local nFunc = 0
|
||||||
|
function c:newFunction(name,func,holup) -- This registers with the queue
|
||||||
|
local func = stripUpValues(func)
|
||||||
|
if type(name)=="function" then
|
||||||
|
holup = func
|
||||||
|
func = name
|
||||||
|
name = "JQ_Function_"..nFunc
|
||||||
|
end
|
||||||
|
nFunc = nFunc + 1
|
||||||
|
c:registerFunction(name,func)
|
||||||
|
return thread:newFunction(function(...)
|
||||||
|
local id = c:pushJob(name,...)
|
||||||
|
local link
|
||||||
|
local rets
|
||||||
|
link = c.OnJobCompleted(function(jid,...)
|
||||||
|
if id==jid then
|
||||||
|
rets = {...}
|
||||||
|
link:Destroy()
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
return thread.hold(function()
|
||||||
|
if rets then
|
||||||
|
return unpack(rets) or multi.NIL
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end,holup),name
|
||||||
|
end
|
||||||
|
for i=1,c.cores do
|
||||||
|
thread:newthread("PesudoThreadedJobQueue_"..i,function()
|
||||||
|
while true do
|
||||||
|
thread.yield()
|
||||||
|
if #jobs>0 then
|
||||||
|
local j = table.remove(jobs,1)
|
||||||
|
c.OnJobCompleted:Fire(j[2],funcs[j[1]](unpack(j[3])))
|
||||||
|
else
|
||||||
|
thread.sleep(.05)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
return c
|
||||||
|
end
|
||||||
@ -24,8 +24,6 @@ SOFTWARE.
|
|||||||
package.path = "?/init.lua;?.lua;" .. package.path
|
package.path = "?/init.lua;?.lua;" .. package.path
|
||||||
local multi, thread = require("multi"):init()
|
local multi, thread = require("multi"):init()
|
||||||
|
|
||||||
local pseudoProcessor = multi:newProcessor()
|
|
||||||
|
|
||||||
if multi.integration then
|
if multi.integration then
|
||||||
return {
|
return {
|
||||||
init = function()
|
init = function()
|
||||||
@ -33,12 +31,8 @@ if multi.integration then
|
|||||||
end
|
end
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
multi.isMainThread = true
|
|
||||||
local activator = require("multi.integration.pseudoManager.threads")
|
|
||||||
local GLOBAL, THREAD = activator.init(thread)
|
|
||||||
|
|
||||||
_G.THREAD_NAME = "MAIN_THREAD"
|
local GLOBAL, THREAD = require("multi.integration.pesudoManager.threads").init(thread)
|
||||||
_G.THREAD_ID = 0
|
|
||||||
|
|
||||||
function multi:canSystemThread() -- We are emulating system threading
|
function multi:canSystemThread() -- We are emulating system threading
|
||||||
return true
|
return true
|
||||||
@ -56,44 +50,29 @@ local function split(str)
|
|||||||
return tab
|
return tab
|
||||||
end
|
end
|
||||||
|
|
||||||
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,xpcall,math,coroutine,string,table]]
|
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)
|
tab = split(tab)
|
||||||
|
|
||||||
local id = 0
|
local id = 0
|
||||||
|
|
||||||
function multi:newSystemThread(name,func,...)
|
function multi:newSystemThread(name,func,...)
|
||||||
local env
|
GLOBAL["$THREAD_NAME"] = name
|
||||||
env = {
|
GLOBAL["$__THREADNAME__"] = name
|
||||||
|
GLOBAL["$THREAD_ID"] = id
|
||||||
|
GLOBAL["$thread"] = thread
|
||||||
|
local env = {
|
||||||
GLOBAL = GLOBAL,
|
GLOBAL = GLOBAL,
|
||||||
THREAD = THREAD,
|
THREAD = THREAD,
|
||||||
THREAD_NAME = tostring(name),
|
THREAD_NAME = name,
|
||||||
__THREADNAME__ = tostring(name),
|
__THREADNAME__ = name,
|
||||||
THREAD_ID = id,
|
THREAD_ID = id,
|
||||||
thread = thread,
|
thread = thread
|
||||||
multi = multi,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, v in pairs(_G) do
|
for i = 1,#tab do
|
||||||
if not(env[i]) and not(i == "_G") and not(i == "local_global") then
|
env[tab[i]] = _G[tab[i]]
|
||||||
env[i] = v
|
|
||||||
else
|
|
||||||
multi.warn("skipping:",i)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if GLOBAL["__env"] then
|
local th = thread:newISOThread(name,func,env,...)
|
||||||
for i,v in pairs(GLOBAL["__env"]) do
|
|
||||||
env[i] = v
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
env._G = env
|
|
||||||
|
|
||||||
local GLOBAL, THREAD = activator.init(thread, env)
|
|
||||||
|
|
||||||
local th = pseudoProcessor:newISOThread(name, func, env, ...)
|
|
||||||
th.Type = multi.registerType("s_thread", "pseudoThreads")
|
|
||||||
th.OnError(multi.error)
|
|
||||||
|
|
||||||
id = id + 1
|
id = id + 1
|
||||||
|
|
||||||
@ -107,15 +86,14 @@ THREAD.newSystemThread = multi.newSystemThread
|
|||||||
function THREAD:newFunction(func,holdme)
|
function THREAD:newFunction(func,holdme)
|
||||||
return thread:newFunctionBase(function(...)
|
return thread:newFunctionBase(function(...)
|
||||||
return multi:newSystemThread("TempSystemThread",func,...)
|
return multi:newSystemThread("TempSystemThread",func,...)
|
||||||
end, holdme, multi.registerType("s_function", "pseudoFunctions"))()
|
end,holdme)()
|
||||||
end
|
end
|
||||||
|
|
||||||
multi.print("Integrated Pesudo Threading!")
|
multi.print("Integrated Pesudo Threading!")
|
||||||
multi.integration = {} -- for module creators
|
multi.integration = {} -- for module creators
|
||||||
multi.integration.GLOBAL = GLOBAL
|
multi.integration.GLOBAL = GLOBAL
|
||||||
multi.integration.THREAD = THREAD
|
multi.integration.THREAD = THREAD
|
||||||
require("multi.integration.pseudoManager.extensions")
|
require("multi.integration.pesudoManager.extensions")
|
||||||
require("multi.integration.sharedExtensions")
|
|
||||||
return {
|
return {
|
||||||
init = function()
|
init = function()
|
||||||
return GLOBAL, THREAD
|
return GLOBAL, THREAD
|
||||||
@ -33,7 +33,6 @@ end
|
|||||||
local function INIT(thread)
|
local function INIT(thread)
|
||||||
local THREAD = {}
|
local THREAD = {}
|
||||||
local GLOBAL = {}
|
local GLOBAL = {}
|
||||||
|
|
||||||
THREAD.Priority_Core = 3
|
THREAD.Priority_Core = 3
|
||||||
THREAD.Priority_High = 2
|
THREAD.Priority_High = 2
|
||||||
THREAD.Priority_Above_Normal = 1
|
THREAD.Priority_Above_Normal = 1
|
||||||
@ -81,42 +80,31 @@ local function INIT(thread)
|
|||||||
|
|
||||||
THREAD.pushStatus = thread.pushStatus
|
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()
|
function THREAD.kill()
|
||||||
error("Thread was killed!")
|
error("Thread was killed!")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function THREAD.getName()
|
||||||
|
return GLOBAL["$THREAD_NAME"]
|
||||||
|
end
|
||||||
|
|
||||||
|
function THREAD.getID()
|
||||||
|
return GLOBAL["$THREAD_ID"]
|
||||||
|
end
|
||||||
|
|
||||||
THREAD.sleep = thread.sleep
|
THREAD.sleep = thread.sleep
|
||||||
|
|
||||||
THREAD.hold = thread.hold
|
THREAD.hold = thread.hold
|
||||||
|
|
||||||
THREAD.defer = thread.defer
|
|
||||||
|
|
||||||
function THREAD.setENV(env, name)
|
|
||||||
name = name or "__env"
|
|
||||||
GLOBAL[name] = env
|
|
||||||
end
|
|
||||||
|
|
||||||
function THREAD.getENV(name)
|
|
||||||
name = name or "__env"
|
|
||||||
return GLOBAL[name]
|
|
||||||
end
|
|
||||||
|
|
||||||
function THREAD.exposeENV(name)
|
|
||||||
name = name or "__env"
|
|
||||||
local env = THREAD.getENV(name)
|
|
||||||
for i,v in pairs(env) do
|
|
||||||
-- This may need to be reworked!
|
|
||||||
local_global[i] = v
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function THREAD.sync()
|
|
||||||
thread.sleep(.5)
|
|
||||||
end
|
|
||||||
|
|
||||||
return GLOBAL, THREAD
|
return GLOBAL, THREAD
|
||||||
end
|
end
|
||||||
|
|
||||||
return {init = function(thread, global)
|
return {init = function(thread)
|
||||||
return INIT(thread, global)
|
return INIT(thread)
|
||||||
end}
|
end}
|
||||||
@ -1,213 +0,0 @@
|
|||||||
-- Advanced process management. Mutates the multi namespace
|
|
||||||
local multi, thread = require("multi"):init()
|
|
||||||
local ok, chronos = pcall(require, "chronos") -- hpc
|
|
||||||
|
|
||||||
if not ok then chronos = nil end
|
|
||||||
|
|
||||||
-- This is an integration, we cannot directly access locals that are in the main file.
|
|
||||||
|
|
||||||
local PList = {
|
|
||||||
multi.Priority_Core,
|
|
||||||
multi.Priority_Very_High,
|
|
||||||
multi.Priority_High,
|
|
||||||
multi.Priority_Above_Normal,
|
|
||||||
multi.Priority_Normal,
|
|
||||||
multi.Priority_Below_Normal,
|
|
||||||
multi.Priority_Low,
|
|
||||||
multi.Priority_Very_Low,
|
|
||||||
multi.Priority_Idle
|
|
||||||
}
|
|
||||||
|
|
||||||
-- Restructered these functions since they rely on local variables from the core library
|
|
||||||
|
|
||||||
local mainloop = multi.mainloopRef
|
|
||||||
local mainloop_p = multi.mainloop_p
|
|
||||||
local uManagerRef = multi.uManagerRef
|
|
||||||
local uManagerRefP = multi.uManagerRefP1
|
|
||||||
|
|
||||||
local PROFILE_COUNT = 5
|
|
||||||
|
|
||||||
-- self:setCurrentProcess() a bit slower than using the local var, but there isn't another option
|
|
||||||
|
|
||||||
local priorityManager = multi:newProcessor("Priority Manager", true)
|
|
||||||
priorityManager.newThread = function() multi.warn("You cannot spawn threads on the priority manager!") end
|
|
||||||
|
|
||||||
priorityManager.setPriorityScheme = function() multi.warn("You cannot set priority on the priorityManager!") end
|
|
||||||
|
|
||||||
local function average(t)
|
|
||||||
local sum = 0
|
|
||||||
for _,v in pairs(t) do
|
|
||||||
sum = sum + v
|
|
||||||
end
|
|
||||||
return sum / #t
|
|
||||||
end
|
|
||||||
|
|
||||||
local function getPriority(obj)
|
|
||||||
local avg = average(obj.__profiling)
|
|
||||||
if avg < 0.0002 then
|
|
||||||
return PList[1]
|
|
||||||
elseif avg < 0.0004 then
|
|
||||||
return PList[2]
|
|
||||||
elseif avg < 0.0008 then
|
|
||||||
return PList[3]
|
|
||||||
elseif avg < 0.001 then
|
|
||||||
return PList[4]
|
|
||||||
elseif avg < 0.0025 then
|
|
||||||
return PList[5]
|
|
||||||
elseif avg < 0.005 then
|
|
||||||
return PList[6]
|
|
||||||
elseif avg < 0.008 then
|
|
||||||
return PList[7]
|
|
||||||
elseif avg < 0.01 then
|
|
||||||
return PList[8]
|
|
||||||
else
|
|
||||||
return PList[9]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local start, stop
|
|
||||||
|
|
||||||
priorityManager.uManager = function(self)
|
|
||||||
-- proc.run already checks if the processor is active
|
|
||||||
self:setCurrentProcess()
|
|
||||||
local Loop=self.Mainloop
|
|
||||||
local ctask
|
|
||||||
for _D=#Loop,1,-1 do
|
|
||||||
ctask = Loop[_D]
|
|
||||||
ctask:setCurrentTask()
|
|
||||||
start = chronos.nanotime()
|
|
||||||
if ctask:Act() then
|
|
||||||
stop = chronos.nanotime()
|
|
||||||
if ctask.__profiling then
|
|
||||||
table.insert(ctask.__profiling, stop - start)
|
|
||||||
end
|
|
||||||
if ctask.__profiling and #ctask.__profiling == PROFILE_COUNT then
|
|
||||||
ctask:setPriority(getPriority(ctask))
|
|
||||||
ctask:reallocate(ctask.__restoreProc)
|
|
||||||
ctask.__restoreProc = nil
|
|
||||||
ctask.__profiling = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
self:setCurrentProcess()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function processHook(obj, proc)
|
|
||||||
if obj.Type == multi.registerType("process", "processes") or not(obj.IsAnActor) then return end
|
|
||||||
obj.__restoreProc = proc
|
|
||||||
obj.__profiling = {}
|
|
||||||
obj:reallocate(priorityManager)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function init()
|
|
||||||
local registry = {}
|
|
||||||
|
|
||||||
multi.priorityScheme = {
|
|
||||||
RoundRobin = "RoundRobin",
|
|
||||||
PriorityBased = "PriorityBased",
|
|
||||||
TimeBased = "TimeBased"
|
|
||||||
}
|
|
||||||
|
|
||||||
function multi:setProfilerCount(count)
|
|
||||||
PROFILE_COUNT = count
|
|
||||||
end
|
|
||||||
|
|
||||||
function multi:recalibrate()
|
|
||||||
if self.__processConn then
|
|
||||||
local items = self.Mainloop
|
|
||||||
for i,v in pairs(items) do
|
|
||||||
processHook(v, self)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
multi.error("Cannot recalibrate the priority if not using Time based mangement!")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function multi:isRegistredScheme(scheme)
|
|
||||||
return registry[name] ~= nil
|
|
||||||
end
|
|
||||||
|
|
||||||
function multi:getRegisteredScheme(scheme)
|
|
||||||
return registry[name].mainloop, registry[name].umanager, registry[name].condition
|
|
||||||
end
|
|
||||||
|
|
||||||
local empty_func = function() return true end
|
|
||||||
function multi:registerScheme(name,options)
|
|
||||||
local mainloop = options.mainloop or multi.error("You must provide a mainloop option when registring a scheme!")
|
|
||||||
local umanager = options.umanager or multi.error("You must provide a umanager option when registring a scheme!")
|
|
||||||
|
|
||||||
if not options.condition then
|
|
||||||
multi.warn("You might want to use condition when registring a scheme! A function that returns true has been auto generated for you!")
|
|
||||||
end
|
|
||||||
|
|
||||||
local condition = options.condition or empty_func
|
|
||||||
|
|
||||||
if registry[name] and not registry[name].static then
|
|
||||||
multi.warn("A scheme named: \"" .. name .. "\" has already been registred, overriting!")
|
|
||||||
else
|
|
||||||
multi.error("A scheme named: \"" .. name .. "\" has already been registred!")
|
|
||||||
end
|
|
||||||
|
|
||||||
registry[name] = {
|
|
||||||
mainloop = mainloop,
|
|
||||||
umanager = umanger,
|
|
||||||
condition = condition,
|
|
||||||
static = options.static or false
|
|
||||||
}
|
|
||||||
|
|
||||||
multi.priorityScheme[name] = name
|
|
||||||
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
function multi:setPriorityScheme(scheme)
|
|
||||||
|
|
||||||
if not self.Type == multi.registerType("process", "processes") or not self.Type == multi.registerType("rootprocess") then
|
|
||||||
multi.warn("You should only invoke setPriorityScheme on a processor object!")
|
|
||||||
end
|
|
||||||
|
|
||||||
if scheme == multi.priorityScheme.RoundRobin then
|
|
||||||
if self.__processConn then self.OnObjectCreated:Unconnect(self.__processConn) self.__processConn = nil end
|
|
||||||
self.mainloop = mainloop
|
|
||||||
self.uManager = uManagerRef
|
|
||||||
elseif scheme == multi.priorityScheme.PriorityBased then
|
|
||||||
if self.__processConn then self.OnObjectCreated:Unconnect(self.__processConn) self.__processConn = nil end
|
|
||||||
self.mainloop = mainloop_p
|
|
||||||
self.uManager = uManagerRefP
|
|
||||||
elseif scheme == multi.priorityScheme.TimeBased then
|
|
||||||
if not chronos then return multi.warn("Unable to use TimeBased Priority without the chronos library!") end
|
|
||||||
if self.__processConn then multi.warn("Already enabled TimeBased Priority!") end
|
|
||||||
self.__processConn = self.OnObjectCreated(processHook)
|
|
||||||
self.mainloop = mainloop_p
|
|
||||||
self.uManager = uManagerRefP
|
|
||||||
elseif self:isRegistredScheme(scheme) then
|
|
||||||
local mainloop, umanager, condition = self:getRegisteredScheme(scheme)
|
|
||||||
if condition() then
|
|
||||||
self.mainloop = mainloop
|
|
||||||
self.uManager = umanager
|
|
||||||
end
|
|
||||||
else
|
|
||||||
self.error("Invalid priority scheme selected!")
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function init_chronos()
|
|
||||||
-- Let's implement a higher precision clock
|
|
||||||
multi.setClock(chronos.nanotime) -- This is also in .000 format. So a plug and play works.
|
|
||||||
thread:newThread("System Priority Manager", function()
|
|
||||||
while true do
|
|
||||||
thread.yield()
|
|
||||||
priorityManager.run()
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
if chronos then
|
|
||||||
init_chronos()
|
|
||||||
else
|
|
||||||
multi.warn("In order to have time based priority management, you need to install the chronos library!")
|
|
||||||
end
|
|
||||||
|
|
||||||
init()
|
|
||||||
@ -1,221 +0,0 @@
|
|||||||
--[[
|
|
||||||
MIT License
|
|
||||||
|
|
||||||
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
|
|
||||||
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.
|
|
||||||
]]
|
|
||||||
local multi, thread = require("multi"):init()
|
|
||||||
local GLOBAL, THREAD = multi.integration.GLOBAL, multi.integration.THREAD
|
|
||||||
|
|
||||||
local function stripUpValues(func)
|
|
||||||
local dmp = string.dump(func)
|
|
||||||
if setfenv then
|
|
||||||
return loadstring(dmp,"IsolatedThread_PesudoThreading")
|
|
||||||
else
|
|
||||||
return load(dmp,"IsolatedThread_PesudoThreading","bt")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function multi:newSystemThreadedQueue(name)
|
|
||||||
local c = {}
|
|
||||||
c.data = {}
|
|
||||||
c.Type = multi.registerType("s_queue")
|
|
||||||
function c:push(v)
|
|
||||||
table.insert(self.data,v)
|
|
||||||
end
|
|
||||||
function c:pop()
|
|
||||||
return table.remove(self.data,1)
|
|
||||||
end
|
|
||||||
function c:peek()
|
|
||||||
return self.data[1]
|
|
||||||
end
|
|
||||||
function c:init()
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
function c:Hold(opt)
|
|
||||||
if opt.peek then
|
|
||||||
return thread.hold(function()
|
|
||||||
return self:peek()
|
|
||||||
end)
|
|
||||||
else
|
|
||||||
return thread.hold(function()
|
|
||||||
return self:pop()
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
GLOBAL[name or "_"] = c
|
|
||||||
return c
|
|
||||||
end
|
|
||||||
|
|
||||||
function multi:newSystemThreadedTable(name)
|
|
||||||
local c = {}
|
|
||||||
c.Type = multi.registerType("s_table")
|
|
||||||
function c:init()
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
function c:Hold(opt)
|
|
||||||
if opt.key then
|
|
||||||
return thread.hold(function()
|
|
||||||
return self.tab[opt.key]
|
|
||||||
end)
|
|
||||||
else
|
|
||||||
multi.error("Must provide a key to check opt.key = 'key'")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
GLOBAL[name or "_"] = c
|
|
||||||
return c
|
|
||||||
end
|
|
||||||
|
|
||||||
local setfenv = multi.isolateFunction
|
|
||||||
|
|
||||||
local jqc = 1
|
|
||||||
function multi:newSystemThreadedJobQueue(n)
|
|
||||||
local c = {}
|
|
||||||
|
|
||||||
c.cores = n or THREAD.getCores()
|
|
||||||
c.registerQueue = {}
|
|
||||||
c.Type = multi.registerType("s_jobqueue")
|
|
||||||
c.funcs = multi:newSystemThreadedTable("__JobQueue_"..jqc.."_table")
|
|
||||||
c.queue = multi:newSystemThreadedQueue("__JobQueue_"..jqc.."_queue")
|
|
||||||
c.queueReturn = multi:newSystemThreadedQueue("__JobQueue_"..jqc.."_queueReturn")
|
|
||||||
c.queueAll = multi:newSystemThreadedQueue("__JobQueue_"..jqc.."_queueAll")
|
|
||||||
c.id = 0
|
|
||||||
c.OnJobCompleted = multi:newConnection()
|
|
||||||
|
|
||||||
local allfunc = 0
|
|
||||||
|
|
||||||
function c:doToAll(func)
|
|
||||||
for i = 1, self.cores do
|
|
||||||
self.queueAll:push({allfunc, func})
|
|
||||||
end
|
|
||||||
allfunc = allfunc + 1
|
|
||||||
end
|
|
||||||
function c:registerFunction(name, func)
|
|
||||||
if self.funcs[name] then
|
|
||||||
multi.error("A function by the name "..name.." has already been registered!")
|
|
||||||
end
|
|
||||||
self.funcs[name] = func
|
|
||||||
end
|
|
||||||
function c:pushJob(name,...)
|
|
||||||
self.id = self.id + 1
|
|
||||||
self.queue:push{name,self.id,...}
|
|
||||||
return self.id
|
|
||||||
end
|
|
||||||
function c:isEmpty()
|
|
||||||
return queueJob:peek()==nil
|
|
||||||
end
|
|
||||||
local nFunc = 0
|
|
||||||
function c:newFunction(name,func,holup) -- This registers with the queue
|
|
||||||
if type(name)=="function" then
|
|
||||||
holup = func
|
|
||||||
func = name
|
|
||||||
name = "JQ_Function_"..nFunc
|
|
||||||
end
|
|
||||||
nFunc = nFunc + 1
|
|
||||||
c:registerFunction(name,func)
|
|
||||||
return thread:newFunction(function(...)
|
|
||||||
local id = c:pushJob(name,...)
|
|
||||||
local link
|
|
||||||
local rets
|
|
||||||
link = c.OnJobCompleted(function(jid,...)
|
|
||||||
if id==jid then
|
|
||||||
rets = multi.pack(...)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
return thread.hold(function()
|
|
||||||
if rets then
|
|
||||||
return multi.unpack(rets) or multi.NIL
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
end,holup),name
|
|
||||||
end
|
|
||||||
thread:newThread("jobManager",function()
|
|
||||||
while true do
|
|
||||||
thread.yield()
|
|
||||||
local dat = c.queueReturn:pop()
|
|
||||||
if dat then
|
|
||||||
c.OnJobCompleted:Fire(multi.unpack(dat))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
for i=1,c.cores do
|
|
||||||
multi:newSystemThread("STJQ_"..multi.randomString(8),function(jqc)
|
|
||||||
local GLOBAL, THREAD = require("multi.integration.pseudoManager"):init()
|
|
||||||
local multi, thread = require("multi"):init()
|
|
||||||
local clock = os.clock
|
|
||||||
local funcs = THREAD.waitFor("__JobQueue_"..jqc.."_table")
|
|
||||||
local queue = THREAD.waitFor("__JobQueue_"..jqc.."_queue")
|
|
||||||
local queueReturn = THREAD.waitFor("__JobQueue_"..jqc.."_queueReturn")
|
|
||||||
local queueAll = THREAD.waitFor("__JobQueue_"..jqc.."_queueAll")
|
|
||||||
local registry = {}
|
|
||||||
_G["__QR"] = queueReturn
|
|
||||||
setmetatable(_G,{__index = funcs})
|
|
||||||
thread:newThread("startUp",function()
|
|
||||||
while true do
|
|
||||||
thread.yield()
|
|
||||||
local all = queueAll:peek()
|
|
||||||
if all and not registry[all[1]] then
|
|
||||||
queueAll:pop()[2]()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
thread:newThread("runner",function()
|
|
||||||
thread.sleep(.1)
|
|
||||||
while true do
|
|
||||||
thread.yield()
|
|
||||||
local all = queueAll:peek()
|
|
||||||
if all and not registry[all[1]] then
|
|
||||||
queueAll:pop()[2]()
|
|
||||||
end
|
|
||||||
local dat = thread.hold(queue)
|
|
||||||
if dat then
|
|
||||||
multi:newThread("JobSubRunner",function()
|
|
||||||
local name = table.remove(dat,1)
|
|
||||||
local id = table.remove(dat,1)
|
|
||||||
local tab = {multi.isolateFunction(funcs[name],_G)(multi.unpack(dat))}
|
|
||||||
table.insert(tab,1,id)
|
|
||||||
queueReturn:push(tab)
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
multi:mainloop()
|
|
||||||
end, jqc)
|
|
||||||
end
|
|
||||||
|
|
||||||
function c:Hold(opt)
|
|
||||||
return thread.hold(self.OnJobCompleted)
|
|
||||||
end
|
|
||||||
|
|
||||||
jqc = jqc + 1
|
|
||||||
|
|
||||||
self:create(c)
|
|
||||||
|
|
||||||
return c
|
|
||||||
end
|
|
||||||
|
|
||||||
function multi:newSystemThreadedConnection(name)
|
|
||||||
local conn = multi:newConnection()
|
|
||||||
conn.init = function(self) return self end
|
|
||||||
GLOBAL[name or "_"] = conn
|
|
||||||
return conn
|
|
||||||
end
|
|
||||||
|
|
||||||
require("multi.integration.sharedExtensions")
|
|
||||||
@ -1,336 +0,0 @@
|
|||||||
--[[ todo finish the targeted job!
|
|
||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2023 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.
|
|
||||||
]]
|
|
||||||
|
|
||||||
local multi, thread = require("multi"):init()
|
|
||||||
|
|
||||||
-- Returns a handler that allows a user to interact with an object on another thread!
|
|
||||||
-- Create on the thread that you want to interact with, send over the handle
|
|
||||||
|
|
||||||
function multi:chop(obj)
|
|
||||||
if not _G["UIDS"] then
|
|
||||||
_G["UIDS"] = {}
|
|
||||||
end
|
|
||||||
local multi, thread = require("multi"):init()
|
|
||||||
local list = {[0] = multi.randomString(12)}
|
|
||||||
_G[list[0]] = obj
|
|
||||||
for i,v in pairs(obj) do
|
|
||||||
if type(v) == "function" or type(v) == "table" and v.Type == multi.registerType("s_function") then
|
|
||||||
table.insert(list, i)
|
|
||||||
elseif type(v) == "table" and v.Type == multi.registerType("connector", "connections") then
|
|
||||||
table.insert(list, {i, multi:newProxy(multi:chop(v)):init()})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return list
|
|
||||||
end
|
|
||||||
|
|
||||||
function multi:newProxy(list)
|
|
||||||
|
|
||||||
local c = {}
|
|
||||||
|
|
||||||
c.name = multi.randomString(12)
|
|
||||||
c.is_init = false
|
|
||||||
local multi, thread = nil, nil
|
|
||||||
function c:init()
|
|
||||||
local multi, thread = nil, nil
|
|
||||||
local function copy(obj)
|
|
||||||
if type(obj) ~= 'table' then return obj end
|
|
||||||
local res = {}
|
|
||||||
for k, v in pairs(obj) do res[copy(k)] = copy(v) end
|
|
||||||
return res
|
|
||||||
end
|
|
||||||
if not(self.is_init) then
|
|
||||||
THREAD.sync()
|
|
||||||
self.is_init = true
|
|
||||||
local multi, thread = require("multi"):init()
|
|
||||||
self.proxy_link = "PL" .. multi.randomString(12)
|
|
||||||
|
|
||||||
if multi.integration then
|
|
||||||
GLOBAL = multi.integration.GLOBAL
|
|
||||||
THREAD = multi.integration.THREAD
|
|
||||||
end
|
|
||||||
|
|
||||||
GLOBAL[self.proxy_link] = self
|
|
||||||
|
|
||||||
local function check()
|
|
||||||
return self.send:pop()
|
|
||||||
end
|
|
||||||
|
|
||||||
self.send = multi:newSystemThreadedQueue(self.name.."_S"):init()
|
|
||||||
self.recv = multi:newSystemThreadedQueue(self.name.."_R"):init()
|
|
||||||
self.funcs = list
|
|
||||||
self._funcs = copy(list)
|
|
||||||
self.Type = multi.registerType("proxy", "proxies")
|
|
||||||
self.TID = THREAD_ID
|
|
||||||
|
|
||||||
thread:newThread("Proxy_Handler_" .. multi.randomString(4), function()
|
|
||||||
while true do
|
|
||||||
local data = thread.hold(check)
|
|
||||||
if data then
|
|
||||||
-- Let's not hold the main threadloop
|
|
||||||
thread:newThread("Temp_Thread", function()
|
|
||||||
local func = table.remove(data, 1)
|
|
||||||
local sref = table.remove(data, 1)
|
|
||||||
local ret
|
|
||||||
if sref then
|
|
||||||
ret = {_G[list[0]][func](_G[list[0]], multi.unpack(data))}
|
|
||||||
else
|
|
||||||
ret = {_G[list[0]][func](multi.unpack(data))}
|
|
||||||
end
|
|
||||||
|
|
||||||
for i = 1,#ret do
|
|
||||||
if type(ret[i]) == "table" and ret[i].Type ~= nil and ret[i].Type ~= multi.registerType("proxy", "proxies") then
|
|
||||||
ret[i] = "\1PARENT_REF"
|
|
||||||
end
|
|
||||||
if type(ret[i]) == "table" and getmetatable(ret[i]) then
|
|
||||||
setmetatable(ret[i],nil) -- remove that metatable, we do not need it on the other side!
|
|
||||||
end
|
|
||||||
if ret[i] == _G[list[0]] then
|
|
||||||
-- We cannot return itself, that return can contain bad values.
|
|
||||||
ret[i] = "\1SELF_REF"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
table.insert(ret, 1, func)
|
|
||||||
self.recv:push(ret)
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
return self
|
|
||||||
else
|
|
||||||
THREAD.sync()
|
|
||||||
if not self.funcs then return self end
|
|
||||||
local function copy(obj)
|
|
||||||
if type(obj) ~= 'table' then return obj end
|
|
||||||
local res = {}
|
|
||||||
for k, v in pairs(obj) do res[copy(k)] = copy(v) end
|
|
||||||
return res
|
|
||||||
end
|
|
||||||
local multi, thread = require("multi"):init()
|
|
||||||
local me = self
|
|
||||||
local funcs = copy(self.funcs)
|
|
||||||
if multi.integration then
|
|
||||||
GLOBAL = multi.integration.GLOBAL
|
|
||||||
THREAD = multi.integration.THREAD
|
|
||||||
end
|
|
||||||
self.send = THREAD.waitFor(self.name.."_S"):init()
|
|
||||||
self.recv = THREAD.waitFor(self.name.."_R"):init()
|
|
||||||
self.Type = multi.registerType("proxy", "proxies")
|
|
||||||
for _,v in pairs(funcs) do
|
|
||||||
if type(v) == "table" then
|
|
||||||
-- We have a connection
|
|
||||||
v[2]:init(proc_name)
|
|
||||||
self[v[1]] = v[2]
|
|
||||||
v[2].Parent = self
|
|
||||||
setmetatable(v[2],getmetatable(multi:newConnection()))
|
|
||||||
else
|
|
||||||
self[v] = thread:newFunction(function(self,...)
|
|
||||||
if self == me then
|
|
||||||
me.send:push({v, true, ...})
|
|
||||||
else
|
|
||||||
me.send:push({v, false, ...})
|
|
||||||
end
|
|
||||||
return thread.hold(function()
|
|
||||||
local data = me.recv:peek()
|
|
||||||
if data and data[1] == v then
|
|
||||||
me.recv:pop()
|
|
||||||
table.remove(data, 1)
|
|
||||||
for i=1,#data do
|
|
||||||
if data[i] == "\1SELF_REF" then
|
|
||||||
data[i] = me
|
|
||||||
elseif data[i] == "\1PARENT_REF" then
|
|
||||||
data[i] = me.Parent
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return multi.unpack(data)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
end, true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
end
|
|
||||||
function c:getTransferable()
|
|
||||||
local cp = {}
|
|
||||||
local multi, thread = require("multi"):init()
|
|
||||||
local function copy(obj)
|
|
||||||
if type(obj) ~= 'table' then return obj end
|
|
||||||
local res = {}
|
|
||||||
for k, v in pairs(obj) do res[copy(k)] = copy(v) end
|
|
||||||
return res
|
|
||||||
end
|
|
||||||
cp.is_init = true
|
|
||||||
cp.proxy_link = self.proxy_link
|
|
||||||
cp.name = self.name
|
|
||||||
cp.funcs = copy(self._funcs)
|
|
||||||
cp.init = function(self)
|
|
||||||
local multi, thread = require("multi"):init()
|
|
||||||
if multi.integration then
|
|
||||||
GLOBAL = multi.integration.GLOBAL
|
|
||||||
THREAD = multi.integration.THREAD
|
|
||||||
end
|
|
||||||
local proxy = THREAD.waitFor(self.proxy_link)
|
|
||||||
proxy.funcs = self.funcs
|
|
||||||
return proxy:init()
|
|
||||||
end
|
|
||||||
return cp
|
|
||||||
end
|
|
||||||
self:create(c)
|
|
||||||
return c
|
|
||||||
end
|
|
||||||
|
|
||||||
local targets = {}
|
|
||||||
local references = {}
|
|
||||||
|
|
||||||
local jid = -1
|
|
||||||
function multi:newSystemThreadedProcessor(cores)
|
|
||||||
|
|
||||||
local name = "STP_"..multi.randomString(4) -- set a random name if none was given.
|
|
||||||
|
|
||||||
local autoscale = autoscale or false -- Will scale up the number of cores that the process uses.
|
|
||||||
local c = {}
|
|
||||||
|
|
||||||
setmetatable(c,{__index = multi})
|
|
||||||
|
|
||||||
c.Type = multi.registerType("s_process", "s_processes")
|
|
||||||
c.threads = {}
|
|
||||||
c.cores = cores or 8
|
|
||||||
c.Name = name
|
|
||||||
c.Mainloop = {}
|
|
||||||
c.__count = 0
|
|
||||||
c.processors = {}
|
|
||||||
c.proc_list = {}
|
|
||||||
c.OnObjectCreated = multi:newConnection()
|
|
||||||
c.parent = self
|
|
||||||
c.jobqueue = multi:newSystemThreadedJobQueue(c.cores)
|
|
||||||
|
|
||||||
function c:pushJob(ID, name, ...)
|
|
||||||
local tq = THREAD.waitFor(self.Name .. "_target_tq_" .. ID):init()
|
|
||||||
tq:push{name, jid, multi.pack(...)}
|
|
||||||
jid = jid - 1
|
|
||||||
return jid + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
c.jobqueue:registerFunction("packObj",function(obj)
|
|
||||||
local multi, thread = require("multi"):init()
|
|
||||||
|
|
||||||
local list = multi:chop(obj)
|
|
||||||
obj.__link_name = list[0]
|
|
||||||
|
|
||||||
local proxy = multi:newProxy(list):init()
|
|
||||||
|
|
||||||
return proxy
|
|
||||||
end)
|
|
||||||
|
|
||||||
c.spawnThread = c.jobqueue:newFunction("__spawnThread__", function(name, func, ...)
|
|
||||||
local multi, thread = require("multi"):init()
|
|
||||||
local obj = thread:newThread(name, func, ...)
|
|
||||||
return packObj(obj)
|
|
||||||
end, true)
|
|
||||||
|
|
||||||
c.spawnTask = c.jobqueue:newFunction("__spawnTask__", function(obj, func, ...)
|
|
||||||
local multi, thread = require("multi"):init()
|
|
||||||
local obj = multi[obj](multi, func, ...)
|
|
||||||
return packObj(obj)
|
|
||||||
end, true)
|
|
||||||
|
|
||||||
local implement = {
|
|
||||||
"newLoop",
|
|
||||||
"newTLoop",
|
|
||||||
"newUpdater",
|
|
||||||
"newEvent",
|
|
||||||
"newAlarm",
|
|
||||||
"newStep",
|
|
||||||
"newTStep",
|
|
||||||
"newService"
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, method in pairs(implement) do
|
|
||||||
c[method] = thread:newFunction(function(self, ...)
|
|
||||||
proxy = self.spawnTask(method, ...)
|
|
||||||
proxy:init()
|
|
||||||
references[proxy] = self
|
|
||||||
return proxy
|
|
||||||
end, true)
|
|
||||||
end
|
|
||||||
|
|
||||||
function c:newThread(name, func, ...)
|
|
||||||
proxy = self.spawnThread(name, func, ...):init(self.Name)
|
|
||||||
references[proxy] = self
|
|
||||||
table.insert(self.threads, proxy)
|
|
||||||
return proxy
|
|
||||||
end
|
|
||||||
|
|
||||||
function c:newFunction(func, holdme)
|
|
||||||
return self.jobqueue:newFunction(func, holdme)
|
|
||||||
end
|
|
||||||
|
|
||||||
function c:newSharedTable(name)
|
|
||||||
if not name then multi.error("You must provide a name when creating a table!") end
|
|
||||||
local tbl_name = "TABLE_"..multi.randomString(8)
|
|
||||||
self.jobqueue:doToAll(function(tbl_name, interaction)
|
|
||||||
_G[interaction] = THREAD.waitFor(tbl_name):init()
|
|
||||||
end, tbl_name, name)
|
|
||||||
return multi:newSystemThreadedTable(tbl_name):init()
|
|
||||||
end
|
|
||||||
|
|
||||||
function c:getHandler()
|
|
||||||
return function() end -- return empty function
|
|
||||||
end
|
|
||||||
|
|
||||||
function c:getThreads()
|
|
||||||
return self.threads
|
|
||||||
end
|
|
||||||
|
|
||||||
function c:getFullName()
|
|
||||||
return self.parent:getFullName() .. "." .. c.Name
|
|
||||||
end
|
|
||||||
|
|
||||||
function c:getName()
|
|
||||||
return self.Name
|
|
||||||
end
|
|
||||||
|
|
||||||
function c.run()
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
function c.isActive()
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
function c.Start()
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
function c.Stop()
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
function c:Destroy()
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
return c
|
|
||||||
end
|
|
||||||
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
function love.conf(t)
|
|
||||||
t.identity = nil -- The name of the save directory (string)
|
|
||||||
t.version = "12.0" -- The LOVE version this game was made for (string)
|
|
||||||
t.console = true -- Attach a console (boolean, Windows only)
|
|
||||||
|
|
||||||
-- t.modules.audio = false -- Enable the audio module (boolean)
|
|
||||||
-- t.modules.event = false -- Enable the event module (boolean)
|
|
||||||
-- t.modules.graphics = false -- Enable the graphics module (boolean)
|
|
||||||
-- t.modules.image = false -- Enable the image module (boolean)
|
|
||||||
-- t.modules.joystick = false -- Enable the joystick module (boolean)
|
|
||||||
-- t.modules.keyboard = false -- Enable the keyboard module (boolean)
|
|
||||||
-- t.modules.math = false -- Enable the math module (boolean)
|
|
||||||
-- t.modules.mouse = false -- Enable the mouse module (boolean)
|
|
||||||
-- t.modules.physics = false -- Enable the physics module (boolean)
|
|
||||||
-- t.modules.sound = false -- Enable the sound module (boolean)
|
|
||||||
-- t.modules.system = true -- Enable the system module (boolean)
|
|
||||||
-- t.modules.timer = true -- Enable the timer module (boolean)
|
|
||||||
-- t.modules.window = false -- Enable the window module (boolean)
|
|
||||||
-- t.modules.thread = true -- Enable the thread module (boolean)
|
|
||||||
end
|
|
||||||
@ -1,75 +0,0 @@
|
|||||||
package.path = "../?/init.lua;../?.lua;"..package.path
|
|
||||||
local multi, thread = require("multi"):init{print=true, warning = true, error=true}
|
|
||||||
local utils = require("multi.integration.loveManager.utils")
|
|
||||||
|
|
||||||
local people = {1,2,3}
|
|
||||||
|
|
||||||
function dump(o)
|
|
||||||
if type(o) == 'table' then
|
|
||||||
local s = '{ '
|
|
||||||
for k,v in pairs(o) do
|
|
||||||
if type(k) ~= 'number' then k = '"'..k..'"' end
|
|
||||||
s = s .. '['..k..'] = ' .. dump(v) .. '('..type(v):sub(1,1)..'),'
|
|
||||||
end
|
|
||||||
return s .. '} '
|
|
||||||
else
|
|
||||||
return tostring(o)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local fpeople = utils.pack(people)
|
|
||||||
|
|
||||||
print("Pack:", dump(fpeople))
|
|
||||||
|
|
||||||
local people = utils.unpack(fpeople)
|
|
||||||
|
|
||||||
print("Unpack:", dump(people))
|
|
||||||
print(type(people[3]))
|
|
||||||
|
|
||||||
-- GLOBAL, THREAD = require("multi.integration.loveManager"):init()
|
|
||||||
|
|
||||||
-- local queue = multi:newSystemThreadedQueue("TestQueue")
|
|
||||||
-- local tab = multi:newSystemThreadedTable("TestTable")
|
|
||||||
|
|
||||||
-- local test = multi:newSystemThread("Test",function()
|
|
||||||
-- local queue = THREAD.waitFor("TestQueue")
|
|
||||||
-- local tab = THREAD.waitFor("TestTable")
|
|
||||||
-- print("THREAD_ID:",THREAD_ID)
|
|
||||||
-- queue:push("Did it work?")
|
|
||||||
-- tab["Test"] = true
|
|
||||||
-- return 1,2,3
|
|
||||||
-- end)
|
|
||||||
|
|
||||||
-- multi:newThread("QueueTest", function()
|
|
||||||
-- print(thread.hold(queue))
|
|
||||||
-- print(thread.hold(tab, {key="Test"}))
|
|
||||||
-- print("Done!")
|
|
||||||
-- end)
|
|
||||||
|
|
||||||
-- local jq = multi:newSystemThreadedJobQueue(n)
|
|
||||||
|
|
||||||
-- jq:registerFunction("test2",function()
|
|
||||||
-- print("This works!")
|
|
||||||
-- end)
|
|
||||||
|
|
||||||
-- jq:registerFunction("test",function(a, b, c)
|
|
||||||
-- print(a, b+c)
|
|
||||||
-- test2()
|
|
||||||
-- return a+b+c
|
|
||||||
-- end)
|
|
||||||
|
|
||||||
-- print("Job:",jq:pushJob("test",1,2,3))
|
|
||||||
-- print("Job:",jq:pushJob("test",2,3,4))
|
|
||||||
-- print("Job:",jq:pushJob("test",5,6,7))
|
|
||||||
|
|
||||||
-- jq.OnJobCompleted(function(...)
|
|
||||||
-- print("Job Completed!", ...)
|
|
||||||
-- end)
|
|
||||||
|
|
||||||
-- function love.draw()
|
|
||||||
-- --
|
|
||||||
-- end
|
|
||||||
|
|
||||||
-- function love.update()
|
|
||||||
-- multi:uManager()
|
|
||||||
-- end
|
|
||||||
@ -1 +0,0 @@
|
|||||||
../
|
|
||||||
@ -1,39 +0,0 @@
|
|||||||
package = "multi"
|
|
||||||
version = "15.3-1"
|
|
||||||
source = {
|
|
||||||
url = "git://github.com/rayaman/multi.git",
|
|
||||||
tag = "15.3.1",
|
|
||||||
}
|
|
||||||
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"] = "init.lua",
|
|
||||||
["multi.integration.lanesManager"] = "integration/lanesManager/init.lua",
|
|
||||||
["multi.integration.lanesManager.extensions"] = "integration/lanesManager/extensions.lua",
|
|
||||||
["multi.integration.lanesManager.threads"] = "integration/lanesManager/threads.lua",
|
|
||||||
["multi.integration.loveManager"] = "integration/loveManager/init.lua",
|
|
||||||
["multi.integration.loveManager.extensions"] = "integration/loveManager/extensions.lua",
|
|
||||||
["multi.integration.loveManager.threads"] = "integration/loveManager/threads.lua",
|
|
||||||
--["multi.integration.lovrManager"] = "integration/lovrManager/init.lua",
|
|
||||||
--["multi.integration.lovrManager.extensions"] = "integration/lovrManager/extensions.lua",
|
|
||||||
--["multi.integration.lovrManager.threads"] = "integration/lovrManager/threads.lua",
|
|
||||||
["multi.integration.pesudoManager"] = "integration/pesudoManager/init.lua",
|
|
||||||
["multi.integration.pesudoManager.extensions"] = "integration/pesudoManager/extensions.lua",
|
|
||||||
["multi.integration.pesudoManager.threads"] = "integration/pesudoManager/threads.lua",
|
|
||||||
["multi.integration.luvitManager"] = "integration/luvitManager.lua",
|
|
||||||
["multi.integration.threading"] = "integration/threading.lua",
|
|
||||||
--["multi.integration.networkManager"] = "integration/networkManager.lua",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,42 +0,0 @@
|
|||||||
package = "multi"
|
|
||||||
version = "16.0-0"
|
|
||||||
source = {
|
|
||||||
url = "git://github.com/rayaman/multi.git",
|
|
||||||
tag = "v16.0.0",
|
|
||||||
}
|
|
||||||
description = {
|
|
||||||
summary = "Lua Multi tasking library",
|
|
||||||
detailed = [[
|
|
||||||
This library contains many methods for multi tasking. Features non coroutine based multi-tasking, coroutine based multi-tasking, and system threading (Requires use of an integration).
|
|
||||||
Check github for documentation.
|
|
||||||
]],
|
|
||||||
homepage = "https://github.com/rayaman/multi",
|
|
||||||
license = "MIT"
|
|
||||||
}
|
|
||||||
dependencies = {
|
|
||||||
"lua >= 5.1"
|
|
||||||
}
|
|
||||||
build = {
|
|
||||||
type = "builtin",
|
|
||||||
modules = {
|
|
||||||
["multi"] = "init.lua",
|
|
||||||
["multi.integration.lanesManager"] = "integration/lanesManager/init.lua",
|
|
||||||
["multi.integration.lanesManager.extensions"] = "integration/lanesManager/extensions.lua",
|
|
||||||
["multi.integration.lanesManager.threads"] = "integration/lanesManager/threads.lua",
|
|
||||||
["multi.integration.loveManager"] = "integration/loveManager/init.lua",
|
|
||||||
["multi.integration.loveManager.extensions"] = "integration/loveManager/extensions.lua",
|
|
||||||
["multi.integration.loveManager.threads"] = "integration/loveManager/threads.lua",
|
|
||||||
["multi.integration.loveManager.utils"] = "integration/loveManager/threads.lua",
|
|
||||||
--["multi.integration.lovrManager"] = "integration/lovrManager/init.lua",
|
|
||||||
--["multi.integration.lovrManager.extensions"] = "integration/lovrManager/extensions.lua",
|
|
||||||
--["multi.integration.lovrManager.threads"] = "integration/lovrManager/threads.lua",
|
|
||||||
["multi.integration.pseudoManager"] = "integration/pseudoManager/init.lua",
|
|
||||||
["multi.integration.pseudoManager.extensions"] = "integration/pseudoManager/extensions.lua",
|
|
||||||
["multi.integration.pseudoManager.threads"] = "integration/pseudoManager/threads.lua",
|
|
||||||
["multi.integration.luvitManager"] = "integration/luvitManager.lua",
|
|
||||||
["multi.integration.threading"] = "integration/threading.lua",
|
|
||||||
["multi.integration.sharedExtensions"] = "integration/sharedExtensions/init.lua",
|
|
||||||
["multi.integration.priorityManager"] = "integration/priorityManager/init.lua",
|
|
||||||
--["multi.integration.networkManager"] = "integration/networkManager.lua",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,42 +0,0 @@
|
|||||||
package = "multi"
|
|
||||||
version = "16.0-1"
|
|
||||||
source = {
|
|
||||||
url = "git://github.com/rayaman/multi.git",
|
|
||||||
tag = "v16.0.1",
|
|
||||||
}
|
|
||||||
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"] = "init.lua",
|
|
||||||
["multi.integration.lanesManager"] = "integration/lanesManager/init.lua",
|
|
||||||
["multi.integration.lanesManager.extensions"] = "integration/lanesManager/extensions.lua",
|
|
||||||
["multi.integration.lanesManager.threads"] = "integration/lanesManager/threads.lua",
|
|
||||||
["multi.integration.loveManager"] = "integration/loveManager/init.lua",
|
|
||||||
["multi.integration.loveManager.extensions"] = "integration/loveManager/extensions.lua",
|
|
||||||
["multi.integration.loveManager.threads"] = "integration/loveManager/threads.lua",
|
|
||||||
["multi.integration.loveManager.utils"] = "integration/loveManager/threads.lua",
|
|
||||||
--["multi.integration.lovrManager"] = "integration/lovrManager/init.lua",
|
|
||||||
--["multi.integration.lovrManager.extensions"] = "integration/lovrManager/extensions.lua",
|
|
||||||
--["multi.integration.lovrManager.threads"] = "integration/lovrManager/threads.lua",
|
|
||||||
["multi.integration.pseudoManager"] = "integration/pseudoManager/init.lua",
|
|
||||||
["multi.integration.pseudoManager.extensions"] = "integration/pseudoManager/extensions.lua",
|
|
||||||
["multi.integration.pseudoManager.threads"] = "integration/pseudoManager/threads.lua",
|
|
||||||
["multi.integration.luvitManager"] = "integration/luvitManager.lua",
|
|
||||||
["multi.integration.threading"] = "integration/threading.lua",
|
|
||||||
["multi.integration.sharedExtensions"] = "integration/sharedExtensions/init.lua",
|
|
||||||
["multi.integration.priorityManager"] = "integration/priorityManager/init.lua",
|
|
||||||
--["multi.integration.networkManager"] = "integration/networkManager.lua",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,39 +0,0 @@
|
|||||||
function love.conf(t)
|
|
||||||
t.identity = nil -- The name of the save directory (string)
|
|
||||||
t.version = "12.0" -- The LOVE version this game was made for (string)
|
|
||||||
t.console = true -- Attach a console (boolean, Windows only)
|
|
||||||
|
|
||||||
t.window.title = "MultiThreadTest" -- The window title (string)
|
|
||||||
t.window.icon = nil -- Filepath to an image to use as the window's icon (string)
|
|
||||||
t.window.width = 1280 -- The window width (number)
|
|
||||||
t.window.height = 720 -- The window height (number)
|
|
||||||
|
|
||||||
t.window.borderless = false -- Remove all border visuals from the window (boolean)
|
|
||||||
t.window.resizable = true -- Let the window be user-resizable (boolean)
|
|
||||||
t.window.minwidth = 1 -- Minimum window width if the window is resizable (number)
|
|
||||||
t.window.minheight = 1 -- Minimum window height if the window is resizable (number)
|
|
||||||
t.window.fullscreen = false -- Enable fullscreen (boolean)
|
|
||||||
t.window.fullscreentype = "desktop" -- Standard fullscreen or desktop fullscreen mode (string)
|
|
||||||
t.window.vsync = false -- Enable vertical sync (boolean)
|
|
||||||
t.window.fsaa = 2 -- The number of samples to use with multi-sampled antialiasing (number)
|
|
||||||
t.window.display = 1 -- Index of the monitor to show the window in (number)
|
|
||||||
t.window.highdpi = false -- Enable high-dpi mode for the window on a Retina display (boolean)
|
|
||||||
t.window.srgb = false -- Enable sRGB gamma correction when drawing to the screen (boolean)
|
|
||||||
t.window.x = nil -- The x-coordinate of the window's position in the specified display (number)
|
|
||||||
t.window.y = nil -- The y-coordinate of the window's position in the specified display (number)
|
|
||||||
|
|
||||||
t.modules.audio = false -- Enable the audio module (boolean)
|
|
||||||
t.modules.event = false -- Enable the event module (boolean)
|
|
||||||
t.modules.graphics = false -- Enable the graphics module (boolean)
|
|
||||||
t.modules.image = false -- Enable the image module (boolean)
|
|
||||||
t.modules.joystick = false -- Enable the joystick module (boolean)
|
|
||||||
t.modules.keyboard = false -- Enable the keyboard module (boolean)
|
|
||||||
t.modules.math = false -- Enable the math module (boolean)
|
|
||||||
t.modules.mouse = false -- Enable the mouse module (boolean)
|
|
||||||
t.modules.physics = false -- Enable the physics module (boolean)
|
|
||||||
t.modules.sound = false -- Enable the sound module (boolean)
|
|
||||||
t.modules.system = false -- Enable the system module (boolean)
|
|
||||||
t.modules.timer = false -- Enable the timer module (boolean)
|
|
||||||
t.modules.window = false -- Enable the window module (boolean)
|
|
||||||
t.modules.thread = true -- Enable the thread module (boolean)
|
|
||||||
end
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
package.path = "../?/init.lua;../?.lua;"..package.path
|
|
||||||
|
|
||||||
if os.getenv("LOCAL_LUA_DEBUGGER_VSCODE") == "1" then
|
|
||||||
require("lldebugger").start()
|
|
||||||
end
|
|
||||||
|
|
||||||
GLOBAL, THREAD = require("multi.integration.loveManager"):init()
|
|
||||||
|
|
||||||
require("runtests")
|
|
||||||
require("threadtests")
|
|
||||||
@ -1 +0,0 @@
|
|||||||
../
|
|
||||||
@ -1,18 +1,35 @@
|
|||||||
package.path = "../?/init.lua;../?.lua;./init.lua;./?.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 = "../?/init.lua;../?.lua;"..package.path
|
||||||
|
end
|
||||||
|
--[[
|
||||||
|
This file runs all tests.
|
||||||
|
Format:
|
||||||
|
Expected:
|
||||||
|
...
|
||||||
|
...
|
||||||
|
...
|
||||||
|
Actual:
|
||||||
|
...
|
||||||
|
...
|
||||||
|
...
|
||||||
|
|
||||||
local multi, thread = require("multi"):init{print=true,warn=true,error=true}--{priority=true}
|
Each test that is ran should have a 5 second pause after the test is complete
|
||||||
|
The expected and actual should "match" (Might be impossible when playing with threads)
|
||||||
|
This will be pushed directly to the master as tests start existing.
|
||||||
|
]]
|
||||||
|
local multi, thread = require("multi"):init{print=true}--{priority=true}
|
||||||
local good = false
|
local good = false
|
||||||
local proc = multi:newProcessor("Test")
|
local proc = multi:newProcessor("Test",true)
|
||||||
|
|
||||||
proc.Start()
|
|
||||||
|
|
||||||
proc:newAlarm(3):OnRing(function()
|
proc:newAlarm(3):OnRing(function()
|
||||||
good = true
|
good = true
|
||||||
end)
|
end)
|
||||||
|
|
||||||
runTest = thread:newFunction(function()
|
runTest = thread:newFunction(function()
|
||||||
local alarms,tsteps,steps,loops,tloops,updaters,events=false,0,0,0,0,0,false
|
local alarms,tsteps,steps,loops,tloops,updaters,events=false,0,0,0,0,0,false
|
||||||
multi.print("Testing Basic Features. If this fails most other features will probably not work!")
|
print("Testing Basic Features. If this fails most other features will probably not work!")
|
||||||
proc:newAlarm(2):OnRing(function(a)
|
proc:newAlarm(2):OnRing(function(a)
|
||||||
alarms = true
|
alarms = true
|
||||||
a:Destroy()
|
a:Destroy()
|
||||||
@ -42,18 +59,18 @@ runTest = thread:newFunction(function()
|
|||||||
event.OnEvent(function(evnt)
|
event.OnEvent(function(evnt)
|
||||||
evnt:Destroy()
|
evnt:Destroy()
|
||||||
events = true
|
events = true
|
||||||
multi.success("Alarms: Ok")
|
print("Alarms: Ok")
|
||||||
multi.success("Events: Ok")
|
print("Events: Ok")
|
||||||
if tsteps == 10 then multi.success("TSteps: Ok") else multi.error("TSteps: Bad!") end
|
if tsteps == 10 then print("TSteps: Ok") else print("TSteps: Bad!") end
|
||||||
if steps == 10 then multi.success("Steps: Ok") else multi.error("Steps: Bad!") end
|
if steps == 10 then print("Steps: Ok") else print("Steps: Bad!") end
|
||||||
if loops > 100 then multi.success("Loops: Ok") else multi.error("Loops: Bad!") end
|
if loops > 100 then print("Loops: Ok") else print("Loops: Bad!") end
|
||||||
if tloops > 10 then multi.success("TLoops: Ok") else multi.error("TLoops: Bad!") end
|
if tloops > 10 then print("TLoops: Ok") else print("TLoops: Bad!") end
|
||||||
if updaters > 100 then multi.success("Updaters: Ok") else multi.error("Updaters: Bad!") end
|
if updaters > 100 then print("Updaters: Ok") else print("Updaters: Bad!") end
|
||||||
end)
|
end)
|
||||||
thread.hold(event.OnEvent)
|
thread.hold(event.OnEvent)
|
||||||
multi.print("Starting Connection and Thread tests!")
|
print("Starting Connection and Thread tests!")
|
||||||
func = thread:newFunction(function(count)
|
func = thread:newFunction(function(count)
|
||||||
multi.print("Starting Status test: ",count)
|
print("Starting Status test: ",count)
|
||||||
local a = 0
|
local a = 0
|
||||||
while true do
|
while true do
|
||||||
a = a + 1
|
a = a + 1
|
||||||
@ -68,13 +85,13 @@ runTest = thread:newFunction(function()
|
|||||||
local ret3 = func(20)
|
local ret3 = func(20)
|
||||||
local s1,s2,s3 = 0,0,0
|
local s1,s2,s3 = 0,0,0
|
||||||
ret.OnError(function(...)
|
ret.OnError(function(...)
|
||||||
multi.error("Func 1:",...)
|
print("Func 1:",...)
|
||||||
end)
|
end)
|
||||||
ret2.OnError(function(...)
|
ret2.OnError(function(...)
|
||||||
multi.error("Func 2:",...)
|
print("Func 2:",...)
|
||||||
end)
|
end)
|
||||||
ret3.OnError(function(...)
|
ret3.OnError(function(...)
|
||||||
multi.error("Func 3:",...)
|
print("Func 3:",...)
|
||||||
end)
|
end)
|
||||||
ret.OnStatus(function(part,whole)
|
ret.OnStatus(function(part,whole)
|
||||||
s1 = math.ceil((part/whole)*1000)/10
|
s1 = math.ceil((part/whole)*1000)/10
|
||||||
@ -87,31 +104,31 @@ runTest = thread:newFunction(function()
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
ret.OnReturn(function(...)
|
ret.OnReturn(function(...)
|
||||||
multi.success("Done 1",...)
|
print("Done 1",...)
|
||||||
end)
|
end)
|
||||||
ret2.OnReturn(function(...)
|
ret2.OnReturn(function(...)
|
||||||
multi.success("Done 2",...)
|
print("Done 2",...)
|
||||||
end)
|
end)
|
||||||
ret3.OnReturn(function(...)
|
ret3.OnReturn(function(...)
|
||||||
multi.success("Done 3",...)
|
print("Done 3",...)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
local err, timeout = thread.hold(ret.OnReturn * ret2.OnReturn * ret3.OnReturn)
|
local err, timeout = thread.hold(ret.OnReturn * ret2.OnReturn * ret3.OnReturn)
|
||||||
|
|
||||||
if s1 == 100 and s2 == 100 and s3 == 100 then
|
if s1 == 100 and s2 == 100 and s3 == 100 then
|
||||||
multi.success("Threads: All tests Ok")
|
print("Threads: All tests Ok")
|
||||||
else
|
else
|
||||||
if s1>0 and s2>0 and s3 > 0 then
|
if s1>0 and s2>0 and s3 > 0 then
|
||||||
multi.success("Thread OnStatus: Ok")
|
print("Thread OnStatus: Ok")
|
||||||
else
|
else
|
||||||
multi.error("Threads OnStatus or thread.hold(conn) Error!")
|
print("Threads OnStatus or thread.hold(conn) Error!")
|
||||||
end
|
end
|
||||||
if timeout then
|
if timeout then
|
||||||
multi.error("Connection Error!")
|
print("Connection Error!")
|
||||||
else
|
else
|
||||||
multi.success("Connection Test 1: Ok")
|
print("Connection Test 1: Ok")
|
||||||
end
|
end
|
||||||
multi.error("Connection holding Error!")
|
print("Connection holding Error!")
|
||||||
end
|
end
|
||||||
|
|
||||||
conn1 = proc:newConnection()
|
conn1 = proc:newConnection()
|
||||||
@ -140,54 +157,29 @@ runTest = thread:newFunction(function()
|
|||||||
conn3:Fire()
|
conn3:Fire()
|
||||||
|
|
||||||
if c1 and c2 and c3 and c4 then
|
if c1 and c2 and c3 and c4 then
|
||||||
multi.success("Connection Test 2: Ok")
|
print("Connection Test 2: Ok")
|
||||||
else
|
else
|
||||||
multi.error("Connection Test 2: Error")
|
print("Connection Test 2: Error")
|
||||||
end
|
end
|
||||||
c3 = false
|
c3 = false
|
||||||
c4 = false
|
c4 = false
|
||||||
conn3:Unconnect(d)
|
d:Destroy()
|
||||||
conn3:Fire()
|
conn3:Fire()
|
||||||
if c3 and not(c4) then
|
if c3 and not(c4) then
|
||||||
multi.success("Connection Test 3: Ok")
|
print("Connection Test 3: Ok")
|
||||||
else
|
else
|
||||||
multi.error("Connection Test 3: Error removing connection")
|
print("Connection Test 3: Error removing connection")
|
||||||
end
|
|
||||||
if not love then
|
|
||||||
local ec = 0
|
|
||||||
multi.print("Testing pseudo threading")
|
|
||||||
capture = io.popen("lua tests/threadtests.lua p"):read("*a")
|
|
||||||
if capture:lower():match("error") then
|
|
||||||
ec = ec + 1
|
|
||||||
os.exit(1)
|
|
||||||
else
|
|
||||||
io.write(capture)
|
|
||||||
end
|
|
||||||
multi.print("Testing lanes threading")
|
|
||||||
capture = io.popen("lua tests/threadtests.lua l"):read("*a")
|
|
||||||
if capture:lower():match("error") then
|
|
||||||
ec = ec + 1
|
|
||||||
os.exit(1)
|
|
||||||
else
|
|
||||||
io.write(capture)
|
|
||||||
end
|
|
||||||
os.exit(0)
|
|
||||||
end
|
end
|
||||||
|
os.exit() -- End of tests
|
||||||
end)
|
end)
|
||||||
|
|
||||||
local handle = runTest()
|
print(runTest().OnError(function(...)
|
||||||
|
print("Error: Something went wrong with the test!")
|
||||||
handle.OnError(function(...)
|
|
||||||
multi.error("Something went wrong with the test!")
|
|
||||||
print(...)
|
print(...)
|
||||||
end)
|
os.exit(1)
|
||||||
|
end))
|
||||||
|
|
||||||
if not love then
|
print("Pumping proc")
|
||||||
multi:mainloop()
|
while true do
|
||||||
else
|
proc.run()
|
||||||
local hold = thread:newFunction(function()
|
|
||||||
thread.hold(handle.OnError + handle.OnReturn)
|
|
||||||
end, true)
|
|
||||||
hold()
|
|
||||||
multi.print("Starting Threading tests!")
|
|
||||||
end
|
end
|
||||||
305
tests/test.lua
305
tests/test.lua
@ -1,141 +1,11 @@
|
|||||||
package.path = "../?/init.lua;../?.lua;"..package.path
|
package.path = "../?/init.lua;../?.lua;"..package.path
|
||||||
multi, thread = require("multi"):init{print=true,warn=true,debugging=true}
|
multi, thread = require("multi"):init{print=true,findopt=true}
|
||||||
-- for i,v in pairs(thread) do
|
GLOBAL, THREAD = require("multi.integration.lanesManager"):init()
|
||||||
-- print(i,v)
|
multi:getOptimizationConnection()(function(msg)
|
||||||
-- end
|
print(msg)
|
||||||
|
end)
|
||||||
|
|
||||||
-- require("multi.integration.priorityManager")
|
-- local conn1, conn2, conn3 = multi:newConnection(), multi:newConnection():fastMode(), multi:newConnection()
|
||||||
|
|
||||||
-- multi.debugging.OnObjectCreated(function(obj, process)
|
|
||||||
-- multi.print("Created:", obj.Type, "in", process.Type, process:getFullName())
|
|
||||||
-- end)
|
|
||||||
|
|
||||||
-- multi.debugging.OnObjectDestroyed(function(obj, process)
|
|
||||||
-- multi.print("Destroyed:", obj.Type, "in", process.Type, process:getFullName())
|
|
||||||
-- end)
|
|
||||||
|
|
||||||
|
|
||||||
-- test = multi:newProcessor("Test")
|
|
||||||
-- test:setPriorityScheme(multi.priorityScheme.TimeBased)
|
|
||||||
|
|
||||||
-- test:newUpdater(10000000):OnUpdate(function()
|
|
||||||
-- print("Print is slowish")
|
|
||||||
-- end)
|
|
||||||
|
|
||||||
-- print("Running...")
|
|
||||||
|
|
||||||
-- local conn1, conn2 = multi:newConnection(), multi:newConnection()
|
|
||||||
-- conn3 = conn1 + conn2
|
|
||||||
|
|
||||||
-- conn1(function()
|
|
||||||
-- print("Hi 1")
|
|
||||||
-- end)
|
|
||||||
|
|
||||||
-- conn2(function()
|
|
||||||
-- print("Hi 2")
|
|
||||||
-- end)
|
|
||||||
|
|
||||||
-- conn3(function()
|
|
||||||
-- print("Hi 3")
|
|
||||||
-- end)
|
|
||||||
|
|
||||||
-- function test(a,b,c)
|
|
||||||
-- print("I run before all and control if execution should continue!")
|
|
||||||
-- return a>b
|
|
||||||
-- end
|
|
||||||
|
|
||||||
-- conn4 = test .. conn1
|
|
||||||
|
|
||||||
-- conn5 = conn2 .. function() print("I run after it all!") end
|
|
||||||
|
|
||||||
-- conn4:Fire(3,2,3)
|
|
||||||
-- -- This second one won't trigger the Hi's
|
|
||||||
-- conn4:Fire(1,2,3)
|
|
||||||
|
|
||||||
-- conn5(function()
|
|
||||||
-- print("Test 1")
|
|
||||||
-- end)
|
|
||||||
|
|
||||||
-- conn5(function()
|
|
||||||
-- print("Test 2")
|
|
||||||
-- end)
|
|
||||||
|
|
||||||
-- conn5(function()
|
|
||||||
-- print("Test 3")
|
|
||||||
-- end)
|
|
||||||
|
|
||||||
-- conn5:Fire()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- multi.print("Testing thread:newProcessor()")
|
|
||||||
|
|
||||||
-- proc = thread:newProcessor("Test")
|
|
||||||
|
|
||||||
-- proc:newLoop(function()
|
|
||||||
-- multi.print("Running...")
|
|
||||||
-- thread.sleep(1)
|
|
||||||
-- end)
|
|
||||||
|
|
||||||
-- proc:newThread(function()
|
|
||||||
-- while true do
|
|
||||||
-- multi.warn("Everything is a thread in this proc!")
|
|
||||||
-- thread.sleep(1)
|
|
||||||
-- end
|
|
||||||
-- end)
|
|
||||||
|
|
||||||
-- proc:newAlarm(5):OnRing(function(a)
|
|
||||||
-- multi.print(";) Goodbye")
|
|
||||||
-- a:Destroy()
|
|
||||||
-- end)
|
|
||||||
|
|
||||||
-- local func = thread:newFunction(function()
|
|
||||||
-- thread.sleep(4)
|
|
||||||
-- print("Hello!")
|
|
||||||
-- end)
|
|
||||||
|
|
||||||
-- multi:newTLoop(func, 1)
|
|
||||||
|
|
||||||
-- multi:mainloop()
|
|
||||||
|
|
||||||
-- multi:setTaskDelay(.05)
|
|
||||||
-- multi:newTask(function()
|
|
||||||
-- for i = 1, 10 do
|
|
||||||
-- multi:newTask(function()
|
|
||||||
-- print("Task "..i)
|
|
||||||
-- end)
|
|
||||||
-- end
|
|
||||||
-- end)
|
|
||||||
|
|
||||||
-- local conn = multi:newConnection()
|
|
||||||
-- conn(function() print("Test 1") end)
|
|
||||||
-- conn(function() print("Test 2") end)
|
|
||||||
-- conn(function() print("Test 3") end)
|
|
||||||
-- conn(function() print("Test 4") end)
|
|
||||||
|
|
||||||
-- print("Fire 1")
|
|
||||||
-- conn:Fire()
|
|
||||||
-- conn = -conn
|
|
||||||
-- print("Fire 2")
|
|
||||||
-- conn:Fire()
|
|
||||||
|
|
||||||
-- print(#conn)
|
|
||||||
|
|
||||||
-- thread:newThread("Test thread", function()
|
|
||||||
-- print("Starting thread!")
|
|
||||||
-- thread.defer(function() -- Runs when the thread finishes execution
|
|
||||||
-- print("Clean up time!")
|
|
||||||
-- end)
|
|
||||||
-- --[[
|
|
||||||
-- Do lot's of stuff
|
|
||||||
-- ]]
|
|
||||||
-- thread.sleep(3)
|
|
||||||
-- end)
|
|
||||||
|
|
||||||
multi:mainloop()
|
|
||||||
|
|
||||||
-- local conn1, conn2, conn3 = multi:newConnection(nil,nil,true), multi:newConnection(), multi:newConnection()
|
|
||||||
|
|
||||||
-- local link = conn1(function()
|
-- local link = conn1(function()
|
||||||
-- print("Conn1, first")
|
-- print("Conn1, first")
|
||||||
@ -161,14 +31,10 @@ multi:mainloop()
|
|||||||
-- print("Conn2, third")
|
-- print("Conn2, third")
|
||||||
-- end)
|
-- end)
|
||||||
|
|
||||||
-- print("Links 1-6",link,link2,link3,link4,link5,link6)
|
|
||||||
-- conn1:Lock(link)
|
|
||||||
-- print("All conns\n-------------")
|
-- print("All conns\n-------------")
|
||||||
-- conn1:Fire()
|
-- conn1:Fire()
|
||||||
-- conn2:Fire()
|
-- conn2:Fire()
|
||||||
|
|
||||||
-- conn1:Unlock(link)
|
|
||||||
|
|
||||||
-- conn1:Unconnect(link3)
|
-- conn1:Unconnect(link3)
|
||||||
-- conn2:Unconnect(link6)
|
-- conn2:Unconnect(link6)
|
||||||
-- print("All conns Edit\n---------------------")
|
-- print("All conns Edit\n---------------------")
|
||||||
@ -192,107 +58,76 @@ multi:mainloop()
|
|||||||
-- multi:newAlarm(3):OnRing(function()
|
-- multi:newAlarm(3):OnRing(function()
|
||||||
-- print("Conn3")
|
-- print("Conn3")
|
||||||
-- conn3:Fire()
|
-- conn3:Fire()
|
||||||
-- os.exit()
|
|
||||||
-- end)
|
-- end)
|
||||||
|
|
||||||
|
local conn = multi:newSystemThreadedConnection("conn"):init()
|
||||||
|
|
||||||
-- local conn = multi:newSystemThreadedConnection("conn"):init()
|
multi:newSystemThread("Thread_Test_1", function()
|
||||||
|
local multi, thread = require("multi"):init()
|
||||||
|
local conn = GLOBAL["conn"]:init()
|
||||||
|
local console = THREAD.getConsole()
|
||||||
|
conn(function(a,b,c)
|
||||||
|
console.print(THREAD:getName().." was triggered!",a,b,c)
|
||||||
|
end)
|
||||||
|
multi:mainloop()
|
||||||
|
end)
|
||||||
|
|
||||||
-- multi:newSystemThread("Thread_Test_1", function()
|
multi:newSystemThread("Thread_Test_2", function()
|
||||||
-- local multi, thread = require("multi"):init()
|
local multi, thread = require("multi"):init()
|
||||||
-- local conn = GLOBAL["conn"]:init()
|
local conn = GLOBAL["conn"]:init()
|
||||||
-- local console = THREAD.getConsole()
|
local console = THREAD.getConsole()
|
||||||
-- conn(function(a,b,c)
|
conn(function(a,b,c)
|
||||||
-- console.print(THREAD:getName().." was triggered!",a,b,c)
|
console.print(THREAD:getName().." was triggered!",a,b,c)
|
||||||
-- end)
|
end)
|
||||||
-- multi:mainloop()
|
multi:newAlarm(2):OnRing(function()
|
||||||
-- end)
|
console.print("Fire 2!!!")
|
||||||
|
conn:Fire(4,5,6)
|
||||||
|
THREAD.kill()
|
||||||
|
end)
|
||||||
|
|
||||||
-- multi:newSystemThread("Thread_Test_2", function()
|
multi:mainloop()
|
||||||
-- local multi, thread = require("multi"):init()
|
end)
|
||||||
-- local conn = GLOBAL["conn"]:init()
|
local console = THREAD.getConsole()
|
||||||
-- local console = THREAD.getConsole()
|
conn(function(a,b,c)
|
||||||
-- conn(function(a,b,c)
|
console.print("Mainloop conn got triggered!",a,b,c)
|
||||||
-- console.print(THREAD:getName().." was triggered!",a,b,c)
|
end)
|
||||||
-- end)
|
|
||||||
-- multi:newAlarm(2):OnRing(function()
|
|
||||||
-- console.print("Fire 2!!!")
|
|
||||||
-- conn:Fire(4,5,6)
|
|
||||||
-- THREAD.kill()
|
|
||||||
-- end)
|
|
||||||
|
|
||||||
-- multi:mainloop()
|
alarm = multi:newAlarm(1)
|
||||||
-- end)
|
alarm:OnRing(function()
|
||||||
-- local console = THREAD.getConsole()
|
console.print("Fire 1!!!")
|
||||||
-- conn(function(a,b,c)
|
conn:Fire(1,2,3)
|
||||||
-- console.print("Mainloop conn got triggered!",a,b,c)
|
end)
|
||||||
-- end)
|
|
||||||
|
|
||||||
-- alarm = multi:newAlarm(1)
|
alarm = multi:newAlarm(3):OnRing(function()
|
||||||
-- alarm:OnRing(function()
|
multi:newSystemThread("Thread_Test_3",function()
|
||||||
-- console.print("Fire 1!!!")
|
local multi, thread = require("multi"):init()
|
||||||
-- conn:Fire(1,2,3)
|
local conn = GLOBAL["conn"]:init()
|
||||||
-- end)
|
local console = THREAD.getConsole()
|
||||||
|
conn(function(a,b,c)
|
||||||
|
console.print(THREAD:getName().." was triggered!",a,b,c)
|
||||||
|
end)
|
||||||
|
multi:newAlarm(4):OnRing(function()
|
||||||
|
console.print("Fire 3!!!")
|
||||||
|
conn:Fire(7,8,9)
|
||||||
|
end)
|
||||||
|
multi:mainloop()
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
-- alarm = multi:newAlarm(3):OnRing(function()
|
multi:newSystemThread("Thread_Test_4",function()
|
||||||
-- multi:newSystemThread("Thread_Test_3",function()
|
local multi, thread = require("multi"):init()
|
||||||
-- local multi, thread = require("multi"):init()
|
local conn = GLOBAL["conn"]:init()
|
||||||
-- local conn = GLOBAL["conn"]:init()
|
local conn2 = multi:newConnection()
|
||||||
-- local console = THREAD.getConsole()
|
local console = THREAD.getConsole()
|
||||||
-- conn(function(a,b,c)
|
multi:newAlarm(2):OnRing(function()
|
||||||
-- console.print(THREAD:getName().." was triggered!",a,b,c)
|
conn2:Fire()
|
||||||
-- end)
|
end)
|
||||||
-- multi:newAlarm(4):OnRing(function()
|
multi:newThread(function()
|
||||||
-- console.print("Fire 3!!!")
|
console.print("Conn Test!")
|
||||||
-- conn:Fire(7,8,9)
|
thread.hold(conn + conn2)
|
||||||
-- end)
|
console.print("It held!")
|
||||||
-- multi:mainloop()
|
end)
|
||||||
-- end)
|
multi:mainloop()
|
||||||
-- end)
|
end)
|
||||||
|
|
||||||
-- multi:newSystemThread("Thread_Test_4",function()
|
multi:mainloop()
|
||||||
-- local multi, thread = require("multi"):init()
|
|
||||||
-- local conn = GLOBAL["conn"]:init()
|
|
||||||
-- local conn2 = multi:newConnection()
|
|
||||||
-- local console = THREAD.getConsole()
|
|
||||||
-- multi:newAlarm(2):OnRing(function()
|
|
||||||
-- conn2:Fire()
|
|
||||||
-- end)
|
|
||||||
-- multi:newThread(function()
|
|
||||||
-- console.print("Conn Test!")
|
|
||||||
-- thread.hold(conn + conn2)
|
|
||||||
-- console.print("It held!")
|
|
||||||
-- end)
|
|
||||||
-- multi:mainloop()
|
|
||||||
-- end)
|
|
||||||
|
|
||||||
-- multi:mainloop()
|
|
||||||
--[[
|
|
||||||
newFunction function: 0x00fad170
|
|
||||||
waitFor function: 0x00fad0c8
|
|
||||||
request function: 0x00fa4f10
|
|
||||||
newThread function: 0x00fad1b8
|
|
||||||
--__threads table: 0x00fa4dc8
|
|
||||||
defer function: 0x00fa4f98
|
|
||||||
isThread function: 0x00facd40
|
|
||||||
holdFor function: 0x00fa5058
|
|
||||||
yield function: 0x00faccf8
|
|
||||||
hold function: 0x00fa51a0
|
|
||||||
chain function: 0x00fa5180
|
|
||||||
__CORES 32
|
|
||||||
newISOThread function: 0x00fad250
|
|
||||||
newFunctionBase function: 0x00fad128
|
|
||||||
requests table: 0x00fa4e68
|
|
||||||
newProcessor function: 0x00fad190
|
|
||||||
exec function: 0x00fa50e8
|
|
||||||
pushStatus function: 0x00fad108
|
|
||||||
kill function: 0x00faccd8
|
|
||||||
get function: 0x00fad0a8
|
|
||||||
set function: 0x00fad088
|
|
||||||
getCores function: 0x00facd60
|
|
||||||
skip function: 0x00faccb0
|
|
||||||
--_Requests function: 0x00fa50a0
|
|
||||||
getRunningThread function: 0x00fa4fb8
|
|
||||||
holdWithin function: 0x00facc80
|
|
||||||
sleep function: 0x00fa4df0
|
|
||||||
]]
|
|
||||||
@ -1,246 +0,0 @@
|
|||||||
package.path = "D:/VSCWorkspace/?/init.lua;D:/VSCWorkspace/?.lua;"..package.path
|
|
||||||
package.cpath = "C:/luaInstalls/lua5.4/lib/lua/5.4/?/core.dll;" .. package.cpath
|
|
||||||
multi, thread = require("multi"):init{error=true,warning=true,print=true, priority=true}
|
|
||||||
proc = multi:newProcessor("Thread Test",true)
|
|
||||||
local LANES, LOVE, PSEUDO = 1, 2, 3
|
|
||||||
local env, we_good
|
|
||||||
|
|
||||||
if love then
|
|
||||||
GLOBAL, THREAD = require("multi.integration.loveManager"):init()
|
|
||||||
env = LOVE
|
|
||||||
elseif arg[1] == "l" then
|
|
||||||
GLOBAL, THREAD = require("multi.integration.lanesManager"):init()
|
|
||||||
env = LANES
|
|
||||||
elseif arg[1] == "p" then
|
|
||||||
GLOBAL, THREAD = require("multi.integration.pseudoManager"):init()
|
|
||||||
env = PSEUDO
|
|
||||||
else
|
|
||||||
io.write("Test Pseudo(p), Lanes(l), or love(Run in love environment) Threading: ")
|
|
||||||
choice = io.read()
|
|
||||||
if choice == "p" then
|
|
||||||
GLOBAL, THREAD = require("multi.integration.pseudoManager"):init()
|
|
||||||
env = PSEUDO
|
|
||||||
elseif choice == "l" then
|
|
||||||
GLOBAL, THREAD = require("multi.integration.lanesManager"):init()
|
|
||||||
env = LANES
|
|
||||||
else
|
|
||||||
error("Invalid threading choice")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
multi.print("Testing THREAD.setENV() if the multi_assert is not found then there is a problem")
|
|
||||||
THREAD.setENV({
|
|
||||||
multi_assert = function(expected, actual, s)
|
|
||||||
if expected ~= actual then
|
|
||||||
multi.error(s .. " Expected: '".. tostring(expected) .."' Actual: '".. tostring(actual) .."'")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
})
|
|
||||||
|
|
||||||
multi:newThread("Scheduler Thread",function()
|
|
||||||
multi:newThread(function()
|
|
||||||
thread.sleep(30)
|
|
||||||
print("Timeout tests took longer than 30 seconds")
|
|
||||||
multi:Stop()
|
|
||||||
os.exit(1)
|
|
||||||
end)
|
|
||||||
|
|
||||||
queue = multi:newSystemThreadedQueue("Test_Queue")
|
|
||||||
defer_queue = multi:newSystemThreadedQueue("Defer_Queue")
|
|
||||||
|
|
||||||
multi:newSystemThread("Test_Thread_0", function()
|
|
||||||
defer_queue = THREAD.waitFor("Defer_Queue"):init()
|
|
||||||
|
|
||||||
THREAD.defer(function()
|
|
||||||
defer_queue:push("done")
|
|
||||||
multi.print("This function was defered until the end of the threads life")
|
|
||||||
end)
|
|
||||||
|
|
||||||
multi.print("Testing defer, should print below this")
|
|
||||||
|
|
||||||
if THREAD_NAME~="Test_Thread_0" then
|
|
||||||
multi.error("The name should be Test_Thread_0",THREAD_NAME,THREAD_NAME,_G.THREAD_NAME)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
if thread.hold(function()
|
|
||||||
return defer_queue:pop() == "done"
|
|
||||||
end,{sleep=3}) == nil then
|
|
||||||
multi.error("Thread.defer didn't work!")
|
|
||||||
end
|
|
||||||
|
|
||||||
th1 = multi:newSystemThread("Test_Thread_1", function(a,b,c,d,e,f)
|
|
||||||
queue = THREAD.waitFor("Test_Queue"):init()
|
|
||||||
multi_assert("Test_Thread_1", THREAD_NAME, "Thread name does not match!")
|
|
||||||
multi_assert("Passing some args", a, "First argument is not as expected 'Passing some args'")
|
|
||||||
multi_assert(true, e, "Argument e is not true!")
|
|
||||||
multi_assert("table", type(f), "Argument f is not a table!")
|
|
||||||
queue:push("done")
|
|
||||||
end,"Passing some args", 1, 2, 3, true, {"Table"}).OnError(function(self,err)
|
|
||||||
multi.error(err)
|
|
||||||
os.exit(1)
|
|
||||||
end)
|
|
||||||
|
|
||||||
if thread.hold(function()
|
|
||||||
return queue:pop() == "done"
|
|
||||||
end,{sleep=1}) == nil then
|
|
||||||
thread.kill()
|
|
||||||
end
|
|
||||||
|
|
||||||
multi.success("Thread Spawning, THREAD namaspace in threads, global's working, and queues for passing data: Ok")
|
|
||||||
|
|
||||||
func = THREAD:newFunction(function(a,b,c)
|
|
||||||
assert(a == 3, "First argument expected '3' got '".. a .."'!")
|
|
||||||
assert(b == 2, "Second argument expected '2' got '".. b .."'!")
|
|
||||||
assert(c == 1, "Third argument expected '1' got '".. c .."'!")
|
|
||||||
return 1, 2, 3, {"a table"}
|
|
||||||
end, true) -- Hold this
|
|
||||||
|
|
||||||
a, b, c, d = func(3,2,1)
|
|
||||||
assert(a == 1, "First return was not '1'!")
|
|
||||||
assert(b == 2, "Second return was not '2'!")
|
|
||||||
assert(c == 3, "Third return was not '3'!")
|
|
||||||
assert(d[1] == "a table", "Fourth return is not table, or doesn't contain 'a table'!")
|
|
||||||
|
|
||||||
multi.success("Threaded Functions, arg passing, return passing, holding: Ok")
|
|
||||||
|
|
||||||
test=multi:newSystemThreadedTable("YO"):init()
|
|
||||||
test["test1"]="tabletest"
|
|
||||||
local worked = false
|
|
||||||
|
|
||||||
multi:newSystemThread("testing tables",function()
|
|
||||||
tab=THREAD.waitFor("YO")
|
|
||||||
THREAD.hold(function() return tab["test1"] end)
|
|
||||||
THREAD.sleep(.1)
|
|
||||||
tab["test2"] = "Whats so funny?"
|
|
||||||
end).OnError(multi.error)
|
|
||||||
|
|
||||||
multi:newThread("test2",function()
|
|
||||||
thread.hold(function() return test["test2"] end)
|
|
||||||
worked = true
|
|
||||||
end)
|
|
||||||
|
|
||||||
t, val = thread.hold(function()
|
|
||||||
return worked
|
|
||||||
end,{sleep=2})
|
|
||||||
|
|
||||||
if val == multi.TIMEOUT then
|
|
||||||
multi.error("SystemThreadedTables: Failed")
|
|
||||||
os.exit(1)
|
|
||||||
end
|
|
||||||
|
|
||||||
multi.success("SystemThreadedTables: Ok")
|
|
||||||
|
|
||||||
local ready = false
|
|
||||||
|
|
||||||
jq = multi:newSystemThreadedJobQueue(4) -- Job queue with 4 worker threads
|
|
||||||
func2 = jq:newFunction("sleep",function(a,b)
|
|
||||||
THREAD.sleep(.2)
|
|
||||||
end)
|
|
||||||
func = jq:newFunction("test-thread",function(a,b)
|
|
||||||
sleep()
|
|
||||||
return a+b
|
|
||||||
end)
|
|
||||||
local count = 0
|
|
||||||
for i = 1,10 do
|
|
||||||
func(i, i*3).OnReturn(function(data)
|
|
||||||
count = count + 1
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
t, val = thread.hold(function()
|
|
||||||
return count == 10
|
|
||||||
end,{sleep=3})
|
|
||||||
|
|
||||||
if val == multi.TIMEOUT then
|
|
||||||
multi.error("SystemThreadedJobQueues: Failed")
|
|
||||||
os.exit(1)
|
|
||||||
end
|
|
||||||
|
|
||||||
multi.success("SystemThreadedJobQueues: Ok")
|
|
||||||
|
|
||||||
local proxy_test = false
|
|
||||||
local stp = multi:newSystemThreadedProcessor(5)
|
|
||||||
|
|
||||||
local tloop = stp:newTLoop(function()
|
|
||||||
--print("Test")
|
|
||||||
end, 1)
|
|
||||||
|
|
||||||
multi:newSystemThread("PROX_THREAD",function(tloop)
|
|
||||||
local multi, thread = require("multi"):init()
|
|
||||||
tloop = tloop:init()
|
|
||||||
multi.print("tloop type:",tloop.Type)
|
|
||||||
multi.print("Testing proxies on other threads")
|
|
||||||
thread:newThread(function()
|
|
||||||
while true do
|
|
||||||
thread.hold(tloop.OnLoop)
|
|
||||||
print(THREAD_NAME,"Loopy")
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
tloop.OnLoop(function(a)
|
|
||||||
print(THREAD_NAME, "Got loop...")
|
|
||||||
end)
|
|
||||||
multi:mainloop()
|
|
||||||
end, tloop:getTransferable())
|
|
||||||
|
|
||||||
local test = tloop:getTransferable()
|
|
||||||
|
|
||||||
multi.print("tloop", tloop.Type)
|
|
||||||
multi.print("tloop.OnLoop", tloop.OnLoop.Type)
|
|
||||||
|
|
||||||
thread:newThread("Proxy Test Thread",function()
|
|
||||||
multi.print("Testing holding on a proxy connection!")
|
|
||||||
thread.hold(tloop.OnLoop)
|
|
||||||
multi.print("Held on proxy connection... once")
|
|
||||||
thread.hold(tloop.OnLoop)
|
|
||||||
multi.print("Held on proxy connection... twice")
|
|
||||||
thread.hold(tloop.OnLoop)
|
|
||||||
multi.print("Held on proxy connection... finally")
|
|
||||||
proxy_test = true
|
|
||||||
end).OnError(print)
|
|
||||||
|
|
||||||
thread:newThread(function()
|
|
||||||
thread.defer(function()
|
|
||||||
multi.print("Something happened!")
|
|
||||||
end)
|
|
||||||
while true do
|
|
||||||
thread.hold(tloop.OnLoop)
|
|
||||||
multi.print(THREAD_NAME,"Local Loopy")
|
|
||||||
end
|
|
||||||
end).OnError(function(...)
|
|
||||||
print("Error",...)
|
|
||||||
end)
|
|
||||||
|
|
||||||
tloop.OnLoop(function()
|
|
||||||
print("OnLoop", THREAD_NAME)
|
|
||||||
end)
|
|
||||||
|
|
||||||
t, val = thread.hold(function()
|
|
||||||
return proxy_test
|
|
||||||
end,{sleep=10})
|
|
||||||
|
|
||||||
if val == multi.TIMEOUT then
|
|
||||||
multi.error("SystemThreadedProcessor/Proxies: Failed")
|
|
||||||
os.exit(1)
|
|
||||||
else
|
|
||||||
multi.success("SystemThreadedProcessor: OK")
|
|
||||||
end
|
|
||||||
|
|
||||||
thread.sleep(2)
|
|
||||||
|
|
||||||
we_good = true
|
|
||||||
multi:Stop() -- Needed in love2d tests to stop the main runner
|
|
||||||
os.exit(0)
|
|
||||||
end)
|
|
||||||
|
|
||||||
multi.OnExit(function(err_or_errorcode)
|
|
||||||
multi.print("EC: ", err_or_errorcode)
|
|
||||||
if not we_good then
|
|
||||||
multi.print("There was an error running some tests!")
|
|
||||||
return
|
|
||||||
else
|
|
||||||
multi.success("Tests complete!")
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
multi:mainloop()
|
|
||||||
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user