diff --git a/README.html b/README.html index ed8067c..0ecffb4 100644 --- a/README.html +++ b/README.html @@ -9,7 +9,7 @@
-NOTE: I have been studying a lot about threading in the past few weeks and have some awesome additions in store! They will take a while to come out though. The goal of the library is still to provide a simple and efficient way to multi task in lua
In Changes you’ll find documentation for(In Order):
NOTE: I have been studying a lot about threading for the past few months and have some awesome additions in store! They will take a while to come out though. The goal of the library is still to provide a simple and efficient way to multi task in lua
In Changes you’ll find documentation for(In Order):
My multitasking library for lua. It is a pure lua binding if you ingore the integrations and the love2d compat. If you find any bugs or have any issues please let me know :). If you don’t see a table of contents try using the ReadMe.html file. It is eaiser to navigate the readme
Note: The latest version of lualanes is required if you want to make use of system threads on lua 5.2+. I will update the dependencies for luarocks since this library should work fine on lua 5.2+
I still need to test though
To install copy the multi folder into your enviroment and you are good to go
or use luarocks
INSTALLINGNote: The latest version of lualanes is required if you want to make use of system threads on lua 5.1+. I will update the dependencies for luarocks since this library should work fine on lua 5.1+
To install copy the multi folder into your enviroment and you are good to go
If you want to use the system threads then you’ll need to install lanes!
or use luarocks
luarocks install bin -- Inorder to use the new save state stuff
luarocks install multi
-
Discord
For real-time assistance with my libraries! A place where you can ask questions and get help with any of my libraries. Also you can request features and stuff there as well.
https://discord.gg/U8UspuA
Upcoming Plans: Adding network support for threading. Kinda like your own lua cloud. This will require the bin, net, and multi library. Once that happens I will include those libraries as a set. This also means that you can expect both a stand alone and joined versions of the libraries.
Planned features/TODO
--
Add system threads for love2d that works like the lanesManager (loveManager, slight differences). -
Improve performance of the library -
Improve coroutine based threading scheduling - Improve love2d Idle thread cpu usage/Fix the performance when using system threads in love2d… Tricky Look at the rambling section for insight.
-
Add more control to coroutine based threading - Add more control to system based threading
- Make practical examples that show how you can solve real problems
-
Add more features to support module creators -
Make a framework for eaiser thread task distributing -
Fix Error handling on threaded multi objects Non threaded multiobjs will crash your program if they error though! Use multi:newThread() of multi:newSystemThread() if your code can error! Unless you use multi:protect() this however lowers performance! -
Add multi:OnError(function(obj,err)) - sThread.wrap(obj) May or may not be completed Theory: Allows interaction in one thread to affect it in another. The addition to threaded tables may make this possible!
- SystemThreaded Actors — After some tests i figured out a way to make this work… It will work slightly different though. This is due to the actor needing to be splittable…
- LoadBalancing for system threads (Once SystemThreaded Actors are done)
-
Add more integrations - Fix SystemThreadedTables
- Finish the wiki stuff. (11% done)
- Test for unknown bugs
Known Bugs/Issues
In regards to integrations, thread cancellation works slightly different for love2d and lanes. Within love2d I was unable to (To lazy to…) not use the multi library within the thread. A fix for this is to call multi:Stop() when you are done with your threaded code! This may change however if I find a way to work around this. In love2d in order to mimic the GLOBAL table I needed the library to constantly sync tha data… You can use the sThread.waitFor(varname), or sThread.hold(func) methods to sync the globals, to get the value instead of using GLOBAL and this could work. If you want to go this route I suggest setting multi.isRunning=true to prevent the auto runner from doing its thing! This will make the multi manager no longer function, but thats the point :P
Another bug concerns the SystemThreadedJobQueue, Only 1 can be used for now… Creating more may not be a good idea.
And systemThreadedTables only supports 1 table between the main and worker thread! They do not work when shared between 2 or more threads. If you need that much flexiblity ust the GLOBAL table that all threads have.
For module creators using this library. I suggest using SystemThreadedQueues for data transfer instead of SystemThreadedTables for rapid data transfer, If you plan on having Constants that will always be the same then a table is a good idea! They support up to n threads and can be messed with and abused as much as you want :D
Love2D SystemThreadedTAbles do not send love2d userdata, use queues instead for that!
Usage:
DiscordFor real-time assistance with my libraries! A place where you can ask questions and get help with any of my libraries. Also you can request features and stuff there as well.
https://discord.gg/U8UspuA
Upcoming Plans: Adding network support for threading. Kinda like your own lua cloud. This will require the bin, net, and multi library. Once that happens I will include those libraries as a set. This also means that you can expect both a stand alone and joined versions of the libraries.
Planned features/TODO
+-
Add system threads for love2d that works like the lanesManager (loveManager, slight differences). -
Improve performance of the library -
Improve coroutine based threading scheduling - Improve love2d Idle thread cpu usage/Fix the performance when using system threads in love2d… Tricky Look at the rambling section for insight.
-
Add more control to coroutine based threading - Add more control to system based threading
- Make practical examples that show how you can solve real problems
-
Add more features to support module creators -
Make a framework for eaiser thread task distributing -
Fix Error handling on threaded multi objects Non threaded multiobjs will crash your program if they error though! Use multi:newThread() of multi:newSystemThread() if your code can error! Unless you use multi:protect() this however lowers performance! -
Add multi:OnError(function(obj,err)) - sThread.wrap(obj) May or may not be completed Theory: Allows interaction in one thread to affect it in another. The addition to threaded tables may make this possible!
- SystemThreaded Actors — After some tests i figured out a way to make this work… It will work slightly different though. This is due to the actor needing to be splittable…
- LoadBalancing for system threads (Once SystemThreaded Actors are done)
-
Add more integrations - Fix SystemThreadedTables
- Finish the wiki stuff. (11% done)
- Test for unknown bugs
Known Bugs/Issues
In regards to integrations, thread cancellation works slightly different for love2d and lanes. Within love2d I was unable to (To lazy to…) not use the multi library within the thread. A fix for this is to call multi:Stop() when you are done with your threaded code! This may change however if I find a way to work around this. In love2d in order to mimic the GLOBAL table I needed the library to constantly sync tha data… You can use the sThread.waitFor(varname), or sThread.hold(func) methods to sync the globals, to get the value instead of using GLOBAL and this could work. If you want to go this route I suggest setting multi.isRunning=true to prevent the auto runner from doing its thing! This will make the multi manager no longer function, but thats the point :P THREAD.kill() should do the trick from within the thread. A listener could be made to detect when thread kill has been requested and sent to the running thread.
Another bug concerns the SystemThreadedJobQueue, Only 1 can be used for now. Going to change in a future update
And systemThreadedTables only supports 1 table between the main and worker thread! They do not work when shared between 2 or more threads. If you need that much flexiblity ust the GLOBAL table that all threads have. FIXED
For module creators using this library. I suggest using SystemThreadedQueues for data transfer instead of SystemThreadedTables for rapid data transfer, If you plan on having Constants that will always be the same then a table is a good idea! They support up to n threads and can be messed with and abused as much as you want :D FIXED Use what you want!
Love2D SystemThreadedTAbles do not send love2d userdata, use queues instead for that! FIXED
Usage:
function<
end
end)
multi:mainloop()
-
Output (Change the value inc as indicated in the comment to see the outcomes!)
Looping…
Looping…
Looping…
Looping…
Looping…
Looping…
Looping…
Looping…
Looping…
Loop timed out! tloop Trying again…
Looping…
Looping…
Looping…
Looping…
Looping…
We did it! 1 2 3
Changes
Update: 1.9.2
Added:
-- (THREAD).kill() kills a thread. Note: THREAD is based on what you name it
- newTimeStamper() Part of the persistant systems… Useful for when you are running this library for a massive amount of time… like years stright!
Allows one to hook to timed events such as whenever the clock strikes midnight or when the day turns to monday. The event is only done once though. so as soon as monday is set it would trigger then not trigger again until next monday
works for seconds, minutes, days, months, year.stamper = multi:newTimeStamper()
-stamper:OnTime(int hour,int minute,int second,func) or stamper:OnTime(string time,func) time as 00:00:00
-stamper:OnHour(int hour,func)
-stamper:OnMinute(int minute,func)
-stamper:OnSecond(int second,func)
-stamper:OnDay(int day,func) or stamper:OnDay(string day,func) Mon, Tues, Wed, etc...
-stamper:OnMonth(int month,func)
-stamper:OnYear(int year,func)
-
-Updated: - LoadBalancing, well bettwr load balancing than existed before. This one allowd for multiple processes to have their own load reading. Calling this on the multi object will return the total load for the entire multi enviroment… loads of other processes are indeed affected by what other processes are doing. However if you combine prorioty to the mix of things then you will get differing results… these results however will most likely be higher than normal… different pirorities will have different default thresholds of performence.
Fixed:
-- Thread.getName() should now work on lanes and love2d, haven’t tested ut nuch with the luvit side of things…
- A bug with the lovemanager table.remove arguments were backwards haha
- The queue object in the love2d threading has been fixed! It now supports sending all objects (even functions as long as no upvalues are present!)
Changed:
-- SystemThreadedJobQueues now have built in load management so they are not constantly at 100% cpu usage.
- SystemThreadedJobQueues pushJob now retunts an id of that job which will match the same one that OnJobCompleted returns
Update: 1.9.1
Added:
-- Integration “multi.integration.luvitManager”
- Limited… Only the basic multi:newSystemThread(…) will work
- Not even data passing will work other than arguments… If using the bin library you can pass tables and function… Even full objects as long as inner recursion is not preasent.
Updated:
-- multi:newSystemThread(name,func,…)
- It will not pass the … to the func(). Do not know why this wasn’t done in the first place :P
- Also multi:getPlatform(will now return “luvit” if using luvit… Though Idk if module creators would use the multi library when inside the luvit enviroment
Update: 1.9.0
Added:
-- multiobj:ToString() — returns a string repersenting the object
- multi:newFromString(str) — creates an object from a string
Works on threads and regular objects. Requires the latest bin library to work!
talarm=multi:newThreadedAlarm("AlarmTest",5)
-talarm:OnRing(function()
- print("Ring!")
-end)
-bin.new(talarm:ToString()):tofile("test.dat")
--- multi:newFromString(bin.load("test.dat"))
-
— A more seamless way to use this will be made in the form of state saving.
This is still a WIP
processes, timers, timemasters, watchers, and queuers have not been worked on yet
Update: 1.8.7
Added:
-- multi.timer(func,…)
function test(a,b,c)
- print("Running...")
- a=0
- for i=1,1000000000 do
- a=a+1
- end
- return a,b+c
-end
-print(multi.timer(test,1,2,3))
-print(multi.timer(test,1,2,3))
--- multi.time returns the time taken then the arguments from the function... Uses unpack so careful of nil values!
-
Update: 1.8.6
Added:
-- jobQueue:doToAll(function)
- jobQueue:start() is now required Call this after all calls to registerJob()’s. Calling it afterwards will not guarantee your next push job with that job will work. Not calling this will make pushing jobs impossible!
- Fixed a bug with love2d Threaded Queue
- Fixed some bugs
- Old versions of this library! It stems back from 2012 see rambling for more info…
This will run said function in every thread.
-- Going to use love2d code this time, almost the same as last time... See ramblings
-require("core.Library")
-GLOBAL,sThread=require("multi.integration.loveManager").init() -- load the love2d version of the lanesManager and requires the entire multi library
-require("core.GuiManager")
-gui.ff.Color=Color.Black
-jQueue=multi:newSystemThreadedJobQueue()
-jQueue:registerJob("TEST_JOB",function(a,s)
- math.randomseed(s)
- TEST_JOB2()
- return math.random(0,255)
-end)
-jQueue:registerJob("TEST_JOB2",function()
- print("Test Works!")
-end)
--- 1.8.6 EXAMPLE Change
-jQueue:start() -- This is now needed!
---
-jQueue:doToAll(function()
- print("Doing this 2? times!")
-end)
-tableOfOrder={}
-jQueue.OnJobCompleted(function(JOBID,n)
- tableOfOrder[JOBID]=n
- if #tableOfOrder==10 then
- t.text="We got all of the pieces!"
- end
-end)
-for i=1,10 do -- Job Name of registered function, ... varargs
- jQueue:pushJob("TEST_JOB","This is a test!",math.random(1,1000000))
-end
-t=gui:newTextLabel("no done yet!",0,0,300,100)
-t:centerX()
-t:centerY()
-
Update: 1.8.5
Added:
-- SystemThreadedExecute(cmd)
Allows the execution of system calls without hold up. It is possible to do the same using io.popen()! You decide which works best for you!
local GLOBAL,sThread=require("multi.integration.lanesManager").init()
-cmd=multi:newSystemThreadedExecute("SystemThreadedExecuteTest.lua") -- This file is important!
-cmd.OnCMDFinished(function(code) -- callback function to grab the exit code... Called when the command goes through
- print("Got Code: "..code)
-end)
-multi:newTLoop(function()
- print("...") -- lets show that we aren't being held up
-end,1)
-multi:mainloop()
-
Update: 1.8.4
Added:
-- multi:newSystemThreadedJobQueue()
- Improved stability of the library
- Fixed a bug that made the benchmark and getload commands non-thread(coroutine) safe
- Tweaked the loveManager to help improve idle cpu usage
- Minor tweaks to the coroutine scheduling
Using multi:newSystemThreadedJobQueue()
First you need to create the object
This works the same way as love2d as it does with lanes… It is getting increasing harder to make both work the same way with speed in mind… Anyway…
-- Creating the object using lanes manager to show case this. Examples has the file for love2d
-local GLOBAL,sThread=require("multi.integration.lanesManager").init()
-jQueue=multi:newSystemThreadedJobQueue(n) -- this internally creates System threads. By defualt it will use the # of processors on your system You can set this number though.
--- Only create 1 jobqueue! For now making more than 1 is buggy. You only really need one though. Just register new functions if you want 1 queue to do more. The one reason though is keeping track of jobIDs. I have an idea that I will roll out in the next update.
-jQueue:registerJob("TEST_JOB",function(a,s)
- math.randomseed(s)
- -- We will push a random #
- TEST_JOB2() -- You can call other registered functions as well!
- return math.random(0,255) -- send the result to the main thread
-end)
-jQueue:registerJob("TEST_JOB2",function()
- print("Test Works!") -- this is called from the job since it is registered on the same queue
-end)
-tableOfOrder={} -- This is how we will keep order of our completed jobs. There is no guarantee that the order will be correct
-jQueue.OnJobCompleted(function(JOBID,n) -- whenever a job is completed you hook to the event that is called. This passes the JOBID folled by the returns of the job
- -- JOBID is the completed job, starts at 1 and counts up by 1.
- -- Threads finish at different times so jobids may be passed out of order! Be sure to have a way to order them
- tableOfOrder[JOBID]=n -- we order ours by putting them into a table
- if #tableOfOrder==10 then
- print("We got all of the pieces!")
- end
-end)
--- Lets push the jobs now
-for i=1,10 do -- Job Name of registered function, ... varargs
- jQueue:pushJob("TEST_JOB","This is a test!",math.random(1,1000000))
-end
-print("I pushed all of the jobs :)")
-multi:mainloop() -- Start the main loop :D
-
Thats it from this version!
Update: 1.8.3
Added:
New Mainloop functions Below you can see the slight differences… Function overhead is not too bad in lua, but has a real difference. multi:mainloop() and multi:unprotectedMainloop() use the same algorithm yet the dedicated unprotected one is slightly faster due to having less function overhead.
-- multi:mainloop()* — Bench: 16830003 Steps in 3 second(s)!
- multi:protectedMainloop() — Bench: 16699308 Steps in 3 second(s)!
- multi:unprotectedMainloop() — Bench: 16976627 Steps in 3 second(s)!
- multi:prioritizedMainloop1() — Bench: 15007133 Steps in 3 second(s)!
- multi:prioritizedMainloop2() — Bench: 15526248 Steps in 3 second(s)!
* The OG mainloop function remains the same and old methods to achieve what we have with the new ones still exist
These new methods help by removing function overhead that is caused through the original mainloop function. The one downside is that you no longer have the flexiblity to change the processing during runtime.
However there is a work around! You can use processes to run multiobjs as well and use the other methods on them.
I may make a full comparison between each method and which is faster, but for now trust that the dedicated ones with less function overhead are infact faster. Not by much but still faster. :D
Update: 1.8.2
Added:
-- multi:newsystemThreadedTable(name) NOTE: Metatables are not supported in transfers. However there is a work around obj:init() that you see does this. Take a look in the multi/integration/shared/shared.lua files to see how I did it!
- Modified the GLOBAL metatable to sync before doing its tests
- multi._VERSION was multi.Version, felt it would be more consistant this way… I left the old way of getting the version just incase someone has used that way. It will eventually be gone. Also multi:getVersion() will do the job just as well and keep your code nice and update related bug free!
- Also everything that is included in the: multi/integration/shared/shared.lua (Which is loaded automatically) works in both lanes and love2d enviroments!
The threaded table is setup just like the threaded queue.
It provids GLOBAL like features without having to write to GLOBAL!
This is useful for module creators who want to keep their data private, but also use GLOBAL like coding.
It has a few features that makes it a bit better than plain ol GLOBAL (For now…)
(ThreadedTable - TT for short)
-- TT:waitFor(name)
- TT:sync()
- TT[“var”]=value
- print(TT[“var”])
we also have the “sync” method, this one was made for love2d because we do a syncing trick to get data in a table format. The lanes side has a sync method as well so no worries. Using indexing calls sync once and may grab your variable. This allows you to have the lanes indexing ‘like’ syntax when doing regular indexing in love2d side of the module. As of right now both sides work flawlessly! And this effect is now the GLOBAL as well
On GLOBALS sync is a internal method for keeping the GLOBAL table in order. You can still use sThread.waitFor(name) to wait for variables that may of may not yet exist!
Time for some examples:
Using multi:newSystemThreadedTable(name)
Output (Change the value inc as indicated in the comment to see the outcomes!)Looping…
Looping…
Looping…
Looping…
Looping…
Looping…
Looping…
Looping…
Looping…
Loop timed out! tloop Trying again…
Looping…
Looping…
Looping…
Looping…
Looping…
We did it! 1 2 3
Rambling
5/23/18:
When it comes to running code across different systems we run into a problem. It takes time to send objects from one maching to another. In the beginning only local networks will be supported. I may add support to send commands to another network to do computing. Like having your own lus cloud. userdata will never be allowed to run on other machines. It is not possible unless the library you are using allows userdata to be turned into a string and back into an object. With this feature you want to send a command that will take time or needs tons of them done millions+, reason being networks are not that “fast” and only simple objects can be sent. If you mirror your enviroment then you can do some cool things.
The planned structure will be something like this:
multi-Single Threaded Multitasking
multi-Threads
multi-System Threads
multi-Network threads
where netThreads can contain systemThreads which can intern contain both Threads and single threaded multitasking
Nothing has been built yet, but the system will work something like this:
host:
-- lanes Desktop lua! NOTE: this is in lanesintergratetest6.lua in the examples folder
-local GLOBAL,sThread=require("multi.integration.lanesManager").init()
-test=multi:newSystemThreadedTable("YO"):init()
-test["test1"]="lol"
-multi:newSystemThread("test",function()
- tab=sThread.waitFor("YO"):init()
- print(tab:has("test1"))
- sThread.sleep(3)
- tab["test2"]="Whats so funny?"
-end)
-multi:newThread("test2",function()
- print(test:waitFor("test2"))
+
">sGLOBAL, nGlobal,sThread=require("multi.integration.networkManager").init() -- This will determine if one is using lanes,love2d, or luvit
+multi:Host("MainSystem") -- tell the network that this is the main system. Uses broadcast so that nodes know how to find the host!
+nThread = multi:newNetworkThread("NetThread_1",function(...)
+ -- basic usage
+ nGLOBAL["RemoteVaraible"] = true -- will sync data to all nodes and the host
+ sGLOBAL["LocalMachineVaraible"] = true -- will sync data to all system threads on the local machine
+ return "Hello Network!" -- send "Hello Network" back to the host node
end)
multi:mainloop()
-
-- love2d gaming lua! NOTE: this is in main4.lua in the love2d examples
-require("core.Library")
-GLOBAL,sThread=require("multi.integration.loveManager").init() -- load the love2d version of the lanesManager and requires the entire multi library
-require("core.GuiManager")
-gui.ff.Color=Color.Black
-test=multi:newSystemThreadedTable("YO"):init()
-test["test1"]="lol"
-multi:newSystemThread("test",function()
- tab=sThread.waitFor("YO"):init()
- print(tab["test1"])
- sThread.sleep(3)
- tab["test2"]="Whats so funny?"
-end)
-multi:newThread("test2",function()
- print(test:waitFor("test2"))
- t.text="DONE!"
-end)
-t=gui:newTextLabel("no done yet!",0,0,300,100)
-t:centerX()
-t:centerY()
-
Update: 1.8.1
No real change!
Changed the structure of the library. Combined the coroutine based threads into the core!
Only compat and integrations are not part of the core and never will be by nature.
This should make the library more convient to use.
I left multi/all.lua file so if anyone had libraries/projects that used that it will still work!
Updated from 1.7.6 to 1.8.0
(How much thread could a thread thread if a thread could thread thread?)
Added:
-- multi:newSystemThreadedQueue()
- multi:systemThreadedBenchmark()
- More example files
- multi:canSystemThread() — true if an integration was added false otherwise (For module creation)
- Fixed a few bugs in the loveManager
Using multi:systemThreadedBenchmark()
nodepackage.path="?/init.lua;"..package.path
-local GLOBAL,sThread=require("multi.integration.lanesManager").init()
-multi:systemThreadedBenchmark(3):OnBench(function(self,count)
- print("First Bench: "..count)
- multi:systemThreadedBenchmark(3,"All Threads: ")
-end)
+-- Note: the node will contain a log of all the commands that it gets. A file called "NodeName.log" will contain the info. You can set the limit by lines or file size. Also you can set it to clear the log every interval of time if an error does not exist. All errors are both logged and sent to the host as well. You can have more than one host and more than one node(duh :P).
+
">GLOBAL,sThread=require("multi.integration.networkManager").init() -- This will determine if one is using lanes,love2d, or luvit
+node = multi:newNode("NodeName","MainSystem") -- Search the network for the host, connect to it and be ready for requests!
+-- On the main thread, a simple multi:newNetworkThread thread and also non system threads, you can access global data without an issue. When dealing with system threads is when you have a problem.
+node:setLog{
+ maxLines = 10000,
+ cleanOnInterval = true,
+ cleanInterval = "day", -- every day Supports(day, week, month, year)
+ noLog = false -- default is false, make true if you do not need a log
+}
+node:settings{
+ maxJobs = 100, -- Job queues will respect this as well as the host when it is figuting out which node is under the least load. Default: 0 or infinite
+ sendLoadInterval = 60 -- every 60 seconds update the host of the nodes load
+ sendLoad = true -- default is true, tells the server how stressed the system is
+}
multi:mainloop()
-
Using multi:newSystemThreadedQueue()
Quick Note: queues shared across multiple objects will be pulling from the same “queue” keep this in mind when coding! Also the queue respects direction a push on the thread side cannot be popped on the thread side… Same goes for the mainthread!
Turns out i was wrong about this…
-- in love2d, this file will be in the same example folder as before, but is named main2.lua
-require("core.Library")
-GLOBAL,sThread=require("multi.integration.loveManager").init() -- load the love2d version of the lanesManager and requires the entire multi library
---IMPORTANT
--- Do not make the above local, this is the one difference that the lanesManager does not have
--- If these are local the functions will have the upvalues put into them that do not exist on the threaded side
--- You will need to ensure that the function does not refer to any upvalues in its code. It will print an error if it does though
--- Also each thread has a .1 second delay! This is used to generate a random values for each thread!
-require("core.GuiManager")
-gui.ff.Color=Color.Black
-function multi:newSystemThreadedQueue(name) -- in love2d this will spawn a channel on both ends
- local c={}
- c.name=name
- if love then
- if love.thread then
- function c:init()
- self.chan=love.thread.getChannel(self.name)
- function self:push(v)
- self.chan:push(v)
- end
- function self:pop()
- return self.chan:pop()
- end
- GLOBAL[self.name]=self
- return self
- end
- return c
- else
- error("Make sure you required the love.thread module!")
- end
- else
- c.linda=lanes.linda()
- function c:push(v)
- self.linda:send("Q",v)
- end
- function c:pop()
- return ({self.linda:receive(0,"Q")})[2]
- end
- function c:init()
- return self
- end
- GLOBAL[name]=c
- end
- return c
-end
-queue=multi:newSystemThreadedQueue("QUEUE"):init()
-queue:push("This is a test")
-queue:push("This is a test2")
-queue:push("This is a test3")
-queue:push("This is a test4")
-multi:newSystemThread("test2",function()
- queue=sThread.waitFor("QUEUE"):init()
- data=queue:pop()
- while data do
- print(data)
- data=queue:pop()
- end
- queue:push("DONE!")
-end)
-multi:newThread("test!",function()
- thread.hold(function() return queue:pop() end)
- t.text="Done!"
-end)
-t=gui:newTextLabel("no done yet!",0,0,300,100)
-t:centerX()
-t:centerY()
-
In Lanes
-- The code is compatible with each other, I just wanted to show different things you can do in both examples
--- This file can be found in the examples folder as lanesintegrationtest4.lua
-local GLOBAL,sThread=require("multi.integration.lanesManager").init()
-queue=multi:newSystemThreadedQueue("QUEUE"):init()
-queue:push("This is a test")
-queue:push("This is a test2")
-queue:push("This is a test3")
-queue:push("This is a test4")
-multi:newSystemThread("test2",function()
- queue=sThread.waitFor("QUEUE"):init()
- data=queue:pop()
- while data do
- print(data)
- data=queue:pop()
- end
- queue:push("This is a test5")
- queue:push("This is a test6")
- queue:push("This is a test7")
- queue:push("This is a test8")
-end)
-multi:newThread("test!",function() -- this is a lua thread
- thread.sleep(.1)
- data=queue:pop()
- while data do
- print(data)
- data=queue:pop()
- end
-end)
-multi:mainloop()
-
Update: 1.7.6
Fixed:
Typos like always
Added:
multi:getPlatform() — returns “love2d” if using the love2d platform or returns “lanes” if using lanes for threading
examples files
In Events added method setTask(func)
The old way still works and is more convient to be honest, but I felt a method to do this was ok.
Updated:
some example files to reflect changes to the core. Changes allow for less typing
loveManager to require the compat if used so you don’t need 2 require line to retrieve the library
Update: 1.7.5
Fixed some typos in the readme… (I am sure there are more there are always more)
Added more features for module support
TODO:
Work on performance of the library… I see 3 places where I can make this thing run quicker
I’ll show case some old versions of the multitasking library eventually so you can see its changes in days past!
Update: 1.7.4
Added: the example folder which will be populated with more examples in the near future!
The loveManager integration that mimics the lanesManager integration almost exactly to keep coding in both enviroments as close to possible. This is done mostly for library creation support!
An example of the loveManager in action using almost the same code as the lanesintergreationtest2.lua
NOTE: This code has only been tested to work on love2d version 1.10.2 thoough it should work version 0.9.0
require("core.Library") -- Didn't add this to a repo yet! Will do eventually... Allows for injections and other cool things
-require("multi.compat.love2d") -- allows for multitasking and binds my libraies to the love2d engine that i am using
-GLOBAL,sThread=require("multi.integration.loveManager").init() -- load the love2d version of the lanesManager
---IMPORTANT
--- Do not make the above local, this is the one difference that the lanesManager does not have
--- If these are local the functions will have the upvalues put into them that do not exist on the threaded side
--- You will need to ensure that the function does not refer to any upvalues in its code. It will print an error if it does though
--- Also each thread has a .1 second delay! This is used to generate a random values for each thread!
-require("core.GuiManager") -- allows the use of graphics in the program.
-gui.ff.Color=Color.Black
-function comma_value(amount)
- local formatted = amount
- while true do
- formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
- if (k==0) then
- break
- end
- end
- return formatted
-end
-multi:newSystemThread("test1",function() -- Another difference is that the multi library is already loaded in the threaded enviroment as well as a call to multi:mainloop()
- multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 1"):OnBench(function(self,c) GLOBAL["T1"]=c multi:Stop() end)
-end)
-multi:newSystemThread("test2",function() -- spawns a thread in another lua process
- multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 2"):OnBench(function(self,c) GLOBAL["T2"]=c multi:Stop() end)
-end)
-multi:newSystemThread("test3",function() -- spawns a thread in another lua process
- multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 3"):OnBench(function(self,c) GLOBAL["T3"]=c multi:Stop() end)
-end)
-multi:newSystemThread("test4",function() -- spawns a thread in another lua process
- multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 4"):OnBench(function(self,c) GLOBAL["T4"]=c multi:Stop() end)
-end)
-multi:newSystemThread("test5",function() -- spawns a thread in another lua process
- multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 5"):OnBench(function(self,c) GLOBAL["T5"]=c multi:Stop() end)
-end)
-multi:newSystemThread("test6",function() -- spawns a thread in another lua process
- multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 6"):OnBench(function(self,c) GLOBAL["T6"]=c multi:Stop() end)
-end)
-multi:newSystemThread("Combiner",function() -- spawns a thread in another lua process
- function comma_value(amount)
- local formatted = amount
- while true do
- formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
- if (k==0) then
- break
- end
- end
- return formatted
- end
- local b=comma_value(tostring(sThread.waitFor("T1")+sThread.waitFor("T2")+sThread.waitFor("T3")+sThread.waitFor("T4")+sThread.waitFor("T5")+sThread.waitFor("T6")))
- GLOBAL["DONE"]=b
-end)
-multi:newThread("test0",function()
- -- sThread.waitFor("DONE") -- lets hold the main thread completely so we don't eat up cpu
- -- os.exit()
- -- when the main thread is holding there is a chance that error handling on the system threads may not work!
- -- instead we can do this
- while true do
- thread.skip(1) -- allow error handling to take place... Otherwise lets keep the main thread running on the low
- -- Before we held just because we could... But this is a game and we need to have logic continue
- --sThreadM.sleep(.001) -- Sleeping for .001 is a greeat way to keep cpu usage down. Make sure if you aren't doing work to rest. Abuse the hell out of GLOBAL if you need to :P
- if GLOBAL["DONE"] then
- t.text="Bench: "..GLOBAL["DONE"]
- end
- end
-end)
-GLOBAL["Bench"]=3
-t=gui:newTextLabel("no done yet!",0,0,300,100)
-t:centerX()
-t:centerY()
-
Update: 1.7.3
Changed how requiring the library works!
require("multi.all") Will still work as expected; however, with the exception of threading, compat, and integrations everything else has been moved into the core of the library.
-- This means that these are no longer required and will cause an error if done so
-require("multi.loop")
-require("multi.alarm")
-require("multi.updater")
-require("multi.tloop")
-require("multi.watcher")
-require("multi.tstep")
-require("multi.step")
-require("multi.task")
--- ^ they are all part of the core now
-
Update: 1.7.2
Moved updaters, loops, and alarms into the init.lua file. I consider them core features and they are referenced in the init.lua file so they need to exist there. Threaded versions are still separate though. Added another example file
Update: 1.7.1 Bug Fixes Only
Update: 1.7.0
Modified: multi.integration.lanesManager.lua
It is now in a stable and simple state Works with the latest lanes version! Tested with version 3.11 I cannot promise that everything will work with eariler versions. Future versions are good though.
Example Usage:
sThread is a handle to a global interface for system threads to interact with themself
thread is the interface for multithreads as seen in the threading section
GLOBAL a table that can be used throughout each and every thread
sThreads have a few methods
sThread.set(name,val) — you can use the GLOBAL table instead modifies the same table anyway
sThread.get(name) — you can use the GLOBAL table instead modifies the same table anyway
sThread.waitFor(name) — waits until a value exists, if it does it returns it
sThread.getCores() — returns the number of cores on your cpu
sThread.sleep(n) — sleeps for a bit stopping the entire thread from running
sThread.hold(n) — sleeps until a condition is met
local GLOBAL,sThread=require("multi.integration.lanesManager").init()
-require("multi.all")
-multi:newAlarm(2):OnRing(function(self)
- GLOBAL["NumOfCores"]=sThread.getCores()
-end)
-multi:newAlarm(7):OnRing(function(self)
- GLOBAL["AnotherTest"]=true
-end)
-multi:newAlarm(13):OnRing(function(self)
- GLOBAL["FinalTest"]=true
-end)
-multi:newSystemThread("test",function() -- spawns a thread in another lua process
- require("multi.all") -- now you can do all of your coding with the multi library! You could even spawn more threads from here with the integration. You would need to require the interaction again though
- print("Waiting for variable: NumOfCores")
- print("Got it: ",sThread.waitFor("NumOfCores"))
- sThread.hold(function()
- return GLOBAL["AnotherTest"] -- note this would hold the entire systemthread. Spawn a coroutine thread using multi:newThread() or multi:newThreaded...
- end)
- print("Holding works!")
- multi:newThread("tests",function()
- thread.hold(function()
- return GLOBAL["FinalTest"] -- note this will not hold the entire systemthread. As seen with the TLoop constantly going!
- end)
- print("Final test works!")
- os.exit()
- end)
- local a=0
- multi:newTLoop(function()
- a=a+1
- print(a)
- end,.5)
- multi:mainloop()
-end)
-multi:mainloop()
-
Update: 1.6.0
Changed: steps and loops
-- Was
-step:OnStep(function(pos,self) -- same goes for tsteps as well
- print(pos)
-end)
-multi:newLoop(function(dt,self)
- print(dt)
-end)
--- Is now
-step:OnStep(function(self,pos) -- same goes for tsteps as well
- print(pos)
-end)
-multi:newLoop(function(self,dt)
- print(dt)
-end)
-
Reasoning I wanted to keep objects consistant, but a lot of my older libraries use the old way of doing things. Therefore I added a backwards module
require("multi.all")
-require("multi.compat.backwards[1,5,0]") -- allows for the use of features that were scrapped/changed in 1.6.0+
-
Update: 1.5.0
Added:
-- An easy way to manage timeouts
- Small bug fixes
Update: 1.4.1 - First Public release of the library
IMPORTANT:
Every update I make aims to make things simpler more efficent and just better, but a lot of old code, which can be really big, uses a lot of older features. I know the pain of having to rewrite everything. My promise to my library users is that I will always have backwards support for older features! New ways may exist that are quicker and eaiser, but the old features/methods will be supported.
Rambling
Love2d Sleeping reduces the cpu time making my load detection think the system is under more load, thus preventing it from sleeping… I will look into other means. As of right now it will not eat all of your cpu if threads are active. For now I suggest killing threads that aren’t needed anymore. On lanes threads at idle use 0% cpu and it is amazing. A state machine may solve what I need though. One state being idle state that sleeps and only goes into the active state if a job request or data is sent to it… after some time of not being under load it wil switch back into the idle state… We’ll see what happens.
Love2d doesn’t like to send functions through channels. By defualt it does not support this. I achieve this by dumping the function and loadstring it on the thread. This however is slow. For the System Threaded Job Queue I had to change my original idea of sending functions as jobs. The current way you do it now is register a job functions once and then call that job across the thread through a queue. Each worker thread pops from the queue and returns the job. The Job ID is automatically updated and allows you to keep track of the order that the data comes in. A table with # indexes can be used to originze the data…
In regards to benchmarking. If you see my bench marks and are wondering they are 10x better its because I am using luajit for my tests. I highly recommend using luajit for my library, but lua 5.1 will work just as well, but not as fast.
So while working on the jobQueue:doToAll() method I figured out why love2d’s threaded tables were acting up when more than 1 thread was sharing the table. It turns out 1 thread was eating all of the pops from the queue and starved all of the other queues… Ill need to use the same trick I did with GLOBAL to fix the problem… However at the rate I am going threading in love will become way slower. I might use the regualr GLOBAL to manage data internally for threadedtables…
It has been awhile since I had to bring out the Multi Functions… Syncing within threads are a pain! I had no idea what a task it would be to get something as simple as syncing data was going to be… I will probably add a SystemThreadedSyncer in the future because it will make life eaiser for you guys as well. SystemThreadedTables are still not going to work on love2d, but will work fine on lanes… I have a solution and it is being worked on… Depending on when I pust the next update to this library the second half of this ramble won’t apply anymore
I have been using this (EventManager —> MultiManager —> now multi) for my own purposes and started making this when I first started learning lua. You are able to see how the code changed and evolved throughout the years. I tried to include all the versions that still existed on my HDD.
I added my old versions to this library… It started out as the EventManager and was kinda crappy but it was the start to this library. It kept getting better and better until it became what it is today. There are some features that nolonger exist in the latest version, but they were remove because they were useless… I added these files to the github so for those interested can see into my mind in a sense and see how I developed the library before I used github.
The first version of the EventManager was function based not object based and benched at about 2000 steps per second… Yeah that was bad… I used loadstring and it was a mess… Take a look and see how it grew throughout the years I think it may intrest some of you guys!
+-- Note: the node will contain a log of all the commands that it gets. A file called "NodeName.log" will contain the info. You can set the limit by lines or file size. Also you can set it to clear the log every interval of time if an error does not exist. All errors are both logged and sent to the host as well. You can have more than one host and more than one node(duh :P).
+
The goal of the node is to set up a simple and easy way to run commands on a remote machine.
There are 2 main ways you can use this feature. 1. One node per machine with system threads being able to use the full processing power of the machine. 2. Multiple nodes on one machine where each node is acting like its own thread. And of course a mix of the two is indeed possible.
Love2d Sleeping reduces the cpu time making my load detection think the system is under more load, thus preventing it from sleeping… I will look into other means. As of right now it will not eat all of your cpu if threads are active. For now I suggest killing threads that aren’t needed anymore. On lanes threads at idle use 0% cpu and it is amazing. A state machine may solve what I need though. One state being idle state that sleeps and only goes into the active state if a job request or data is sent to it… after some time of not being under load it wil switch back into the idle state… We’ll see what happens.
Love2d doesn’t like to send functions through channels. By defualt it does not support this. I achieve this by dumping the function and loadstring it on the thread. This however is slow. For the System Threaded Job Queue I had to change my original idea of sending functions as jobs. The current way you do it now is register a job functions once and then call that job across the thread through a queue. Each worker thread pops from the queue and returns the job. The Job ID is automatically updated and allows you to keep track of the order that the data comes in. A table with # indexes can be used to originze the data…
In regards to benchmarking. If you see my bench marks and are wondering they are 10x better its because I am using luajit for my tests. I highly recommend using luajit for my library, but lua 5.1 will work just as well, but not as fast.
So while working on the jobQueue:doToAll() method I figured out why love2d’s threaded tables were acting up when more than 1 thread was sharing the table. It turns out 1 thread was eating all of the pops from the queue and starved all of the other queues… Ill need to use the same trick I did with GLOBAL to fix the problem… However at the rate I am going threading in love will become way slower. I might use the regualr GLOBAL to manage data internally for threadedtables…
It has been awhile since I had to bring out the Multi Functions… Syncing within threads are a pain! I had no idea what a task it would be to get something as simple as syncing data was going to be… I will probably add a SystemThreadedSyncer in the future because it will make life eaiser for you guys as well. SystemThreadedTables are still not going to work on love2d, but will work fine on lanes… I have a solution and it is being worked on… Depending on when I pust the next update to this library the second half of this ramble won’t apply anymore
I have been using this (EventManager —> MultiManager —> now multi) for my own purposes and started making this when I first started learning lua. You are able to see how the code changed and evolved throughout the years. I tried to include all the versions that still existed on my HDD.
I added my old versions to this library… It started out as the EventManager and was kinda crappy but it was the start to this library. It kept getting better and better until it became what it is today. There are some features that nolonger exist in the latest version, but they were remove because they were useless… I added these files to the github so for those interested can see into my mind in a sense and see how I developed the library before I used github.
The first version of the EventManager was function based not object based and benched at about 2000 steps per second… Yeah that was bad… I used loadstring and it was a mess… Take a look and see how it grew throughout the years I think it may intrest some of you guys!
diff --git a/README.md b/README.md
index 233ddd6..393af3e 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
-# multi Version: 1.9.2 (Yes I am alive, and some tweaks and additions have been made)
+# multi Version: 1.10.0 (Changelog has its own dedicated file now, also bug fixes and a new object)
-**NOTE: I have been studying a lot about threading in the past few weeks and have some awesome additions in store! They will take a while to come out though. The goal of the library is still to provide a simple and efficient way to multi task in lua**
+**NOTE: I have been studying a lot about threading for the past few months and have some awesome additions in store! They will take a while to come out though. The goal of the library is still to provide a simple and efficient way to multi task in lua**
In Changes you'll find documentation for(In Order):
- Sterilizing Objects
@@ -18,16 +18,20 @@ My multitasking library for lua. It is a pure lua binding if you ingore the inte
INSTALLING
----------
-Note: The latest version of lualanes is required if you want to make use of system threads on lua 5.2+. I will update the dependencies for luarocks since this library should work fine on lua 5.2+
-I still need to test though
-To install copy the multi folder into your enviroment and you are good to go
+Note: The latest version of lualanes is required if you want to make use of system threads on lua 5.1+. I will update the dependencies for luarocks since this library should work fine on lua 5.1+
+To install copy the multi folder into your enviroment and you are good to go
+If you want to use the system threads then you'll need to install lanes!
**or** use luarocks
```
luarocks install bin -- Inorder to use the new save state stuff
luarocks install multi
```
+Note: In the near future you may be able to run multitasking code on multiple machines, network paralisim. This however will have to wait until I hammer out some bugs within the core of system threading itself.
+
+See the rambling section to get an idea of how this will work.
+
Discord
-------
For real-time assistance with my libraries! A place where you can ask questions and get help with any of my libraries. Also you can request features and stuff there as well.
@@ -58,15 +62,15 @@ Planned features/TODO
Known Bugs/Issues
-----------------
-In regards to integrations, thread cancellation works slightly different for love2d and lanes. Within love2d I was unable to (To lazy to...) not use the multi library within the thread. A fix for this is to call `multi:Stop()` when you are done with your threaded code! This may change however if I find a way to work around this. In love2d in order to mimic the GLOBAL table I needed the library to constantly sync tha data... You can use the sThread.waitFor(varname), or sThread.hold(func) methods to sync the globals, to get the value instead of using GLOBAL and this could work. If you want to go this route I suggest setting multi.isRunning=true to prevent the auto runner from doing its thing! This will make the multi manager no longer function, but thats the point :P
+~~In regards to integrations, thread cancellation works slightly different for love2d and lanes. Within love2d I was unable to (To lazy to...) not use the multi library within the thread. A fix for this is to call `multi:Stop()` when you are done with your threaded code! This may change however if I find a way to work around this. In love2d in order to mimic the GLOBAL table I needed the library to constantly sync tha data... You can use the sThread.waitFor(varname), or sThread.hold(func) methods to sync the globals, to get the value instead of using GLOBAL and this could work. If you want to go this route I suggest setting multi.isRunning=true to prevent the auto runner from doing its thing! This will make the multi manager no longer function, but thats the point :P~~ THREAD.kill() should do the trick from within the thread. A listener could be made to detect when thread kill has been requested and sent to the running thread.
-Another bug concerns the SystemThreadedJobQueue, Only 1 can be used for now... Creating more may not be a good idea.
+Another bug concerns the SystemThreadedJobQueue, Only 1 can be used for now. Going to change in a future update
-And systemThreadedTables only supports 1 table between the main and worker thread! They do not work when shared between 2 or more threads. If you need that much flexiblity ust the GLOBAL table that all threads have.
+~~And systemThreadedTables only supports 1 table between the main and worker thread! They do not work when shared between 2 or more threads. If you need that much flexiblity ust the GLOBAL table that all threads have.~~ **FIXED**
-For module creators using this library. I suggest using SystemThreadedQueues for data transfer instead of SystemThreadedTables for rapid data transfer, If you plan on having Constants that will always be the same then a table is a good idea! They support up to **n** threads and can be messed with and abused as much as you want :D
+~~For module creators using this library. I suggest using SystemThreadedQueues for data transfer instead of SystemThreadedTables for rapid data transfer, If you plan on having Constants that will always be the same then a table is a good idea! They support up to **n** threads and can be messed with and abused as much as you want :D~~ FIXED Use what you want!
-Love2D SystemThreadedTAbles do not send love2d userdata, use queues instead for that!
+~~Love2D SystemThreadedTAbles do not send love2d userdata, use queues instead for that!~~ **FIXED**
Usage:
-----
@@ -811,619 +815,56 @@ Looping...
Looping...
We did it! 1 2 3
-Changes
--------
-Update: 1.9.2
--------------
-Added:
-- (THREAD).kill() kills a thread. Note: THREAD is based on what you name it
-- newTimeStamper() Part of the persistant systems... Useful for when you are running this library for a massive amount of time... like years stright!
-Allows one to hook to timed events such as whenever the clock strikes midnight or when the day turns to monday. The event is only done once though. so as soon as monday is set it would trigger then not trigger again until next monday
-works for seconds, minutes, days, months, year.
-```lua
-stamper = multi:newTimeStamper()
-stamper:OnTime(int hour,int minute,int second,func) or stamper:OnTime(string time,func) time as 00:00:00
-stamper:OnHour(int hour,func)
-stamper:OnMinute(int minute,func)
-stamper:OnSecond(int second,func)
-stamper:OnDay(int day,func) or stamper:OnDay(string day,func) Mon, Tues, Wed, etc...
-stamper:OnMonth(int month,func)
-stamper:OnYear(int year,func)
-```
-Updated:
-- LoadBalancing, well bettwr load balancing than existed before. This one allowd for multiple processes to have their own load reading. Calling this on the multi object will return the total load for the entire multi enviroment... loads of other processes are indeed affected by what other processes are doing. However if you combine prorioty to the mix of things then you will get differing results... these results however will most likely be higher than normal... different pirorities will have different default thresholds of performence.
-
-Fixed:
-- Thread.getName() should now work on lanes and love2d, haven't tested ut nuch with the luvit side of things...
-- A bug with the lovemanager table.remove arguments were backwards haha
-- The queue object in the love2d threading has been fixed! It now supports sending all objects (even functions as long as no upvalues are present!)
-
-Changed:
-- SystemThreadedJobQueues now have built in load management so they are not constantly at 100% cpu usage.
-- SystemThreadedJobQueues pushJob now retunts an id of that job which will match the same one that OnJobCompleted returns
-
-
-Update: 1.9.1
--------------
-Added:
-- Integration "multi.integration.luvitManager"
-- Limited... Only the basic multi:newSystemThread(...) will work
-- Not even data passing will work other than arguments... If using the bin library you can pass tables and function... Even full objects as long as inner recursion is not preasent.
-
-Updated:
-- multi:newSystemThread(name,func,...)
-- It will not pass the ... to the func(). Do not know why this wasn't done in the first place :P
-- Also multi:getPlatform(will now return "luvit" if using luvit... Though Idk if module creators would use the multi library when inside the luvit enviroment
-
-Update: 1.9.0
--------------
-Added:
-- multiobj:ToString() -- returns a string repersenting the object
-- multi:newFromString(str) -- creates an object from a string
-
-Works on threads and regular objects. Requires the latest bin library to work!
-```lua
-talarm=multi:newThreadedAlarm("AlarmTest",5)
-talarm:OnRing(function()
- print("Ring!")
-end)
-bin.new(talarm:ToString()):tofile("test.dat")
--- multi:newFromString(bin.load("test.dat"))
-```
--- A more seamless way to use this will be made in the form of state saving.
-This is still a WIP
-processes, timers, timemasters, watchers, and queuers have not been worked on yet
-Update: 1.8.7
--------------
-Added:
-- multi.timer(func,...)
-
-```lua
-function test(a,b,c)
- print("Running...")
- a=0
- for i=1,1000000000 do
- a=a+1
- end
- return a,b+c
-end
-print(multi.timer(test,1,2,3))
-print(multi.timer(test,1,2,3))
--- multi.time returns the time taken then the arguments from the function... Uses unpack so careful of nil values!
-```
-Update: 1.8.6
--------------
-Added:
-- jobQueue:doToAll(function)
-- jobQueue:start() is now required Call this after all calls to registerJob()'s. Calling it afterwards will not guarantee your next push job with that job will work. Not calling this will make pushing jobs impossible!
-- Fixed a bug with love2d Threaded Queue
-- Fixed some bugs
-- Old versions of this library! It stems back from 2012 see rambling for more info...
-
-This will run said function in every thread.
-```lua
--- Going to use love2d code this time, almost the same as last time... See ramblings
-require("core.Library")
-GLOBAL,sThread=require("multi.integration.loveManager").init() -- load the love2d version of the lanesManager and requires the entire multi library
-require("core.GuiManager")
-gui.ff.Color=Color.Black
-jQueue=multi:newSystemThreadedJobQueue()
-jQueue:registerJob("TEST_JOB",function(a,s)
- math.randomseed(s)
- TEST_JOB2()
- return math.random(0,255)
-end)
-jQueue:registerJob("TEST_JOB2",function()
- print("Test Works!")
-end)
--- 1.8.6 EXAMPLE Change
-jQueue:start() -- This is now needed!
---
-jQueue:doToAll(function()
- print("Doing this 2? times!")
-end)
-tableOfOrder={}
-jQueue.OnJobCompleted(function(JOBID,n)
- tableOfOrder[JOBID]=n
- if #tableOfOrder==10 then
- t.text="We got all of the pieces!"
- end
-end)
-for i=1,10 do -- Job Name of registered function, ... varargs
- jQueue:pushJob("TEST_JOB","This is a test!",math.random(1,1000000))
-end
-t=gui:newTextLabel("no done yet!",0,0,300,100)
-t:centerX()
-t:centerY()
-```
-Update: 1.8.5
--------------
-Added:
-- SystemThreadedExecute(cmd)
-
-Allows the execution of system calls without hold up. It is possible to do the same using io.popen()! You decide which works best for you!
-```lua
-local GLOBAL,sThread=require("multi.integration.lanesManager").init()
-cmd=multi:newSystemThreadedExecute("SystemThreadedExecuteTest.lua") -- This file is important!
-cmd.OnCMDFinished(function(code) -- callback function to grab the exit code... Called when the command goes through
- print("Got Code: "..code)
-end)
-multi:newTLoop(function()
- print("...") -- lets show that we aren't being held up
-end,1)
-multi:mainloop()
-```
-Update: 1.8.4
--------------
-Added:
-- multi:newSystemThreadedJobQueue()
-- Improved stability of the library
-- Fixed a bug that made the benchmark and getload commands non-thread(coroutine) safe
-- Tweaked the loveManager to help improve idle cpu usage
-- Minor tweaks to the coroutine scheduling
-
-# Using multi:newSystemThreadedJobQueue()
-First you need to create the object
-This works the same way as love2d as it does with lanes... It is getting increasing harder to make both work the same way with speed in mind... Anyway...
-```lua
--- Creating the object using lanes manager to show case this. Examples has the file for love2d
-local GLOBAL,sThread=require("multi.integration.lanesManager").init()
-jQueue=multi:newSystemThreadedJobQueue(n) -- this internally creates System threads. By defualt it will use the # of processors on your system You can set this number though.
--- Only create 1 jobqueue! For now making more than 1 is buggy. You only really need one though. Just register new functions if you want 1 queue to do more. The one reason though is keeping track of jobIDs. I have an idea that I will roll out in the next update.
-jQueue:registerJob("TEST_JOB",function(a,s)
- math.randomseed(s)
- -- We will push a random #
- TEST_JOB2() -- You can call other registered functions as well!
- return math.random(0,255) -- send the result to the main thread
-end)
-jQueue:registerJob("TEST_JOB2",function()
- print("Test Works!") -- this is called from the job since it is registered on the same queue
-end)
-tableOfOrder={} -- This is how we will keep order of our completed jobs. There is no guarantee that the order will be correct
-jQueue.OnJobCompleted(function(JOBID,n) -- whenever a job is completed you hook to the event that is called. This passes the JOBID folled by the returns of the job
- -- JOBID is the completed job, starts at 1 and counts up by 1.
- -- Threads finish at different times so jobids may be passed out of order! Be sure to have a way to order them
- tableOfOrder[JOBID]=n -- we order ours by putting them into a table
- if #tableOfOrder==10 then
- print("We got all of the pieces!")
- end
-end)
--- Lets push the jobs now
-for i=1,10 do -- Job Name of registered function, ... varargs
- jQueue:pushJob("TEST_JOB","This is a test!",math.random(1,1000000))
-end
-print("I pushed all of the jobs :)")
-multi:mainloop() -- Start the main loop :D
-```
-
-Thats it from this version!
-
-Update: 1.8.3
--------------
-Added:
-**New Mainloop functions** Below you can see the slight differences... Function overhead is not too bad in lua, but has a real difference. multi:mainloop() and multi:unprotectedMainloop() use the same algorithm yet the dedicated unprotected one is slightly faster due to having less function overhead.
-- multi:mainloop()\* -- Bench: 16830003 Steps in 3 second(s)!
-- multi:protectedMainloop() -- Bench: 16699308 Steps in 3 second(s)!
-- multi:unprotectedMainloop() -- Bench: 16976627 Steps in 3 second(s)!
-- multi:prioritizedMainloop1() -- Bench: 15007133 Steps in 3 second(s)!
-- multi:prioritizedMainloop2() -- Bench: 15526248 Steps in 3 second(s)!
-
-\* The OG mainloop function remains the same and old methods to achieve what we have with the new ones still exist
-
-These new methods help by removing function overhead that is caused through the original mainloop function. The one downside is that you no longer have the flexiblity to change the processing during runtime.
-
-However there is a work around! You can use processes to run multiobjs as well and use the other methods on them.
-
-I may make a full comparison between each method and which is faster, but for now trust that the dedicated ones with less function overhead are infact faster. Not by much but still faster. :D
-
-Update: 1.8.2
--------------
-Added:
-- multi:newsystemThreadedTable(name) NOTE: Metatables are not supported in transfers. However there is a work around obj:init() that you see does this. Take a look in the multi/integration/shared/shared.lua files to see how I did it!
-- Modified the GLOBAL metatable to sync before doing its tests
-- multi._VERSION was multi.Version, felt it would be more consistant this way... I left the old way of getting the version just incase someone has used that way. It will eventually be gone. Also multi:getVersion() will do the job just as well and keep your code nice and update related bug free!
-- Also everything that is included in the: multi/integration/shared/shared.lua (Which is loaded automatically) works in both lanes and love2d enviroments!
-
-The threaded table is setup just like the threaded queue.
-It provids GLOBAL like features without having to write to GLOBAL!
-This is useful for module creators who want to keep their data private, but also use GLOBAL like coding.
-It has a few features that makes it a bit better than plain ol GLOBAL (For now...)
-(ThreadedTable - TT for short)
-- TT:waitFor(name)
-- TT:sync()
-- TT["var"]=value
-- print(TT["var"])
-
-we also have the "sync" method, this one was made for love2d because we do a syncing trick to get data in a table format. The lanes side has a sync method as well so no worries. Using indexing calls sync once and may grab your variable. This allows you to have the lanes indexing 'like' syntax when doing regular indexing in love2d side of the module. As of right now both sides work flawlessly! And this effect is now the GLOBAL as well
-
-On GLOBALS sync is a internal method for keeping the GLOBAL table in order. You can still use sThread.waitFor(name) to wait for variables that may of may not yet exist!
-
-Time for some examples:
-# Using multi:newSystemThreadedTable(name)
-```lua
--- lanes Desktop lua! NOTE: this is in lanesintergratetest6.lua in the examples folder
-local GLOBAL,sThread=require("multi.integration.lanesManager").init()
-test=multi:newSystemThreadedTable("YO"):init()
-test["test1"]="lol"
-multi:newSystemThread("test",function()
- tab=sThread.waitFor("YO"):init()
- print(tab:has("test1"))
- sThread.sleep(3)
- tab["test2"]="Whats so funny?"
-end)
-multi:newThread("test2",function()
- print(test:waitFor("test2"))
-end)
-multi:mainloop()
-```
-
-```lua
--- love2d gaming lua! NOTE: this is in main4.lua in the love2d examples
-require("core.Library")
-GLOBAL,sThread=require("multi.integration.loveManager").init() -- load the love2d version of the lanesManager and requires the entire multi library
-require("core.GuiManager")
-gui.ff.Color=Color.Black
-test=multi:newSystemThreadedTable("YO"):init()
-test["test1"]="lol"
-multi:newSystemThread("test",function()
- tab=sThread.waitFor("YO"):init()
- print(tab["test1"])
- sThread.sleep(3)
- tab["test2"]="Whats so funny?"
-end)
-multi:newThread("test2",function()
- print(test:waitFor("test2"))
- t.text="DONE!"
-end)
-t=gui:newTextLabel("no done yet!",0,0,300,100)
-t:centerX()
-t:centerY()
-```
-
-Update: 1.8.1
--------------
-No real change!
-Changed the structure of the library. Combined the coroutine based threads into the core!
-Only compat and integrations are not part of the core and never will be by nature.
-This should make the library more convient to use.
-I left multi/all.lua file so if anyone had libraries/projects that used that it will still work!
-Updated from 1.7.6 to 1.8.0 (How much thread could a thread thread if a thread could thread thread?)
-Added:
-- multi:newSystemThreadedQueue()
-- multi:systemThreadedBenchmark()
-- More example files
-- multi:canSystemThread() -- true if an integration was added false otherwise (For module creation)
-- Fixed a few bugs in the loveManager
-
-# Using multi:systemThreadedBenchmark()
-```lua
-package.path="?/init.lua;"..package.path
-local GLOBAL,sThread=require("multi.integration.lanesManager").init()
-multi:systemThreadedBenchmark(3):OnBench(function(self,count)
- print("First Bench: "..count)
- multi:systemThreadedBenchmark(3,"All Threads: ")
-end)
-multi:mainloop()
-```
-
-# Using multi:newSystemThreadedQueue()
-Quick Note: queues shared across multiple objects will be pulling from the same "queue" keep this in mind when coding! ~~Also the queue respects direction a push on the thread side cannot be popped on the thread side... Same goes for the mainthread!~~ Turns out i was wrong about this...
-```lua
--- in love2d, this file will be in the same example folder as before, but is named main2.lua
-require("core.Library")
-GLOBAL,sThread=require("multi.integration.loveManager").init() -- load the love2d version of the lanesManager and requires the entire multi library
---IMPORTANT
--- Do not make the above local, this is the one difference that the lanesManager does not have
--- If these are local the functions will have the upvalues put into them that do not exist on the threaded side
--- You will need to ensure that the function does not refer to any upvalues in its code. It will print an error if it does though
--- Also each thread has a .1 second delay! This is used to generate a random values for each thread!
-require("core.GuiManager")
-gui.ff.Color=Color.Black
-function multi:newSystemThreadedQueue(name) -- in love2d this will spawn a channel on both ends
- local c={}
- c.name=name
- if love then
- if love.thread then
- function c:init()
- self.chan=love.thread.getChannel(self.name)
- function self:push(v)
- self.chan:push(v)
- end
- function self:pop()
- return self.chan:pop()
- end
- GLOBAL[self.name]=self
- return self
- end
- return c
- else
- error("Make sure you required the love.thread module!")
- end
- else
- c.linda=lanes.linda()
- function c:push(v)
- self.linda:send("Q",v)
- end
- function c:pop()
- return ({self.linda:receive(0,"Q")})[2]
- end
- function c:init()
- return self
- end
- GLOBAL[name]=c
- end
- return c
-end
-queue=multi:newSystemThreadedQueue("QUEUE"):init()
-queue:push("This is a test")
-queue:push("This is a test2")
-queue:push("This is a test3")
-queue:push("This is a test4")
-multi:newSystemThread("test2",function()
- queue=sThread.waitFor("QUEUE"):init()
- data=queue:pop()
- while data do
- print(data)
- data=queue:pop()
- end
- queue:push("DONE!")
-end)
-multi:newThread("test!",function()
- thread.hold(function() return queue:pop() end)
- t.text="Done!"
-end)
-t=gui:newTextLabel("no done yet!",0,0,300,100)
-t:centerX()
-t:centerY()
-```
-# In Lanes
-```lua
--- The code is compatible with each other, I just wanted to show different things you can do in both examples
--- This file can be found in the examples folder as lanesintegrationtest4.lua
-local GLOBAL,sThread=require("multi.integration.lanesManager").init()
-queue=multi:newSystemThreadedQueue("QUEUE"):init()
-queue:push("This is a test")
-queue:push("This is a test2")
-queue:push("This is a test3")
-queue:push("This is a test4")
-multi:newSystemThread("test2",function()
- queue=sThread.waitFor("QUEUE"):init()
- data=queue:pop()
- while data do
- print(data)
- data=queue:pop()
- end
- queue:push("This is a test5")
- queue:push("This is a test6")
- queue:push("This is a test7")
- queue:push("This is a test8")
-end)
-multi:newThread("test!",function() -- this is a lua thread
- thread.sleep(.1)
- data=queue:pop()
- while data do
- print(data)
- data=queue:pop()
- end
-end)
-multi:mainloop()
-```
-Update: 1.7.6
--------------
-Fixed:
-Typos like always
-Added:
-multi:getPlatform() -- returns "love2d" if using the love2d platform or returns "lanes" if using lanes for threading
-examples files
-In Events added method setTask(func)
-The old way still works and is more convient to be honest, but I felt a method to do this was ok.
-
-Updated:
-some example files to reflect changes to the core. Changes allow for less typing
-loveManager to require the compat if used so you don't need 2 require line to retrieve the library
-
-Update: 1.7.5
--------------
-Fixed some typos in the readme... (I am sure there are more there are always more)
-Added more features for module support
-TODO:
-Work on performance of the library... I see 3 places where I can make this thing run quicker
-
-I'll show case some old versions of the multitasking library eventually so you can see its changes in days past!
-
-Update: 1.7.4
--------------
-Added: the example folder which will be populated with more examples in the near future!
-The loveManager integration that mimics the lanesManager integration almost exactly to keep coding in both enviroments as close to possible. This is done mostly for library creation support!
-An example of the loveManager in action using almost the same code as the lanesintergreationtest2.lua
-NOTE: This code has only been tested to work on love2d version 1.10.2 thoough it should work version 0.9.0
-```lua
-require("core.Library") -- Didn't add this to a repo yet! Will do eventually... Allows for injections and other cool things
-require("multi.compat.love2d") -- allows for multitasking and binds my libraies to the love2d engine that i am using
-GLOBAL,sThread=require("multi.integration.loveManager").init() -- load the love2d version of the lanesManager
---IMPORTANT
--- Do not make the above local, this is the one difference that the lanesManager does not have
--- If these are local the functions will have the upvalues put into them that do not exist on the threaded side
--- You will need to ensure that the function does not refer to any upvalues in its code. It will print an error if it does though
--- Also each thread has a .1 second delay! This is used to generate a random values for each thread!
-require("core.GuiManager") -- allows the use of graphics in the program.
-gui.ff.Color=Color.Black
-function comma_value(amount)
- local formatted = amount
- while true do
- formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
- if (k==0) then
- break
- end
- end
- return formatted
-end
-multi:newSystemThread("test1",function() -- Another difference is that the multi library is already loaded in the threaded enviroment as well as a call to multi:mainloop()
- multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 1"):OnBench(function(self,c) GLOBAL["T1"]=c multi:Stop() end)
-end)
-multi:newSystemThread("test2",function() -- spawns a thread in another lua process
- multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 2"):OnBench(function(self,c) GLOBAL["T2"]=c multi:Stop() end)
-end)
-multi:newSystemThread("test3",function() -- spawns a thread in another lua process
- multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 3"):OnBench(function(self,c) GLOBAL["T3"]=c multi:Stop() end)
-end)
-multi:newSystemThread("test4",function() -- spawns a thread in another lua process
- multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 4"):OnBench(function(self,c) GLOBAL["T4"]=c multi:Stop() end)
-end)
-multi:newSystemThread("test5",function() -- spawns a thread in another lua process
- multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 5"):OnBench(function(self,c) GLOBAL["T5"]=c multi:Stop() end)
-end)
-multi:newSystemThread("test6",function() -- spawns a thread in another lua process
- multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 6"):OnBench(function(self,c) GLOBAL["T6"]=c multi:Stop() end)
-end)
-multi:newSystemThread("Combiner",function() -- spawns a thread in another lua process
- function comma_value(amount)
- local formatted = amount
- while true do
- formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
- if (k==0) then
- break
- end
- end
- return formatted
- end
- local b=comma_value(tostring(sThread.waitFor("T1")+sThread.waitFor("T2")+sThread.waitFor("T3")+sThread.waitFor("T4")+sThread.waitFor("T5")+sThread.waitFor("T6")))
- GLOBAL["DONE"]=b
-end)
-multi:newThread("test0",function()
- -- sThread.waitFor("DONE") -- lets hold the main thread completely so we don't eat up cpu
- -- os.exit()
- -- when the main thread is holding there is a chance that error handling on the system threads may not work!
- -- instead we can do this
- while true do
- thread.skip(1) -- allow error handling to take place... Otherwise lets keep the main thread running on the low
- -- Before we held just because we could... But this is a game and we need to have logic continue
- --sThreadM.sleep(.001) -- Sleeping for .001 is a greeat way to keep cpu usage down. Make sure if you aren't doing work to rest. Abuse the hell out of GLOBAL if you need to :P
- if GLOBAL["DONE"] then
- t.text="Bench: "..GLOBAL["DONE"]
- end
- end
-end)
-GLOBAL["Bench"]=3
-t=gui:newTextLabel("no done yet!",0,0,300,100)
-t:centerX()
-t:centerY()
-```
-Update: 1.7.3
--------------
-Changed how requiring the library works!
-`require("multi.all")` Will still work as expected; however, with the exception of threading, compat, and integrations everything else has been moved into the core of the library.
-```lua
--- This means that these are no longer required and will cause an error if done so
-require("multi.loop")
-require("multi.alarm")
-require("multi.updater")
-require("multi.tloop")
-require("multi.watcher")
-require("multi.tstep")
-require("multi.step")
-require("multi.task")
--- ^ they are all part of the core now
-```
-
-Update: 1.7.2
--------------
-Moved updaters, loops, and alarms into the init.lua file. I consider them core features and they are referenced in the init.lua file so they need to exist there. Threaded versions are still separate though. Added another example file
-
-Update: 1.7.1 Bug Fixes Only
--------------
-
-Update: 1.7.0
--------------
-Modified: multi.integration.lanesManager.lua
-It is now in a stable and simple state Works with the latest lanes version! Tested with version 3.11 I cannot promise that everything will work with eariler versions. Future versions are good though.
-Example Usage:
-sThread is a handle to a global interface for system threads to interact with themself
-thread is the interface for multithreads as seen in the threading section
-
-GLOBAL a table that can be used throughout each and every thread
-
-sThreads have a few methods
-sThread.set(name,val) -- you can use the GLOBAL table instead modifies the same table anyway
-sThread.get(name) -- you can use the GLOBAL table instead modifies the same table anyway
-sThread.waitFor(name) -- waits until a value exists, if it does it returns it
-sThread.getCores() -- returns the number of cores on your cpu
-sThread.sleep(n) -- sleeps for a bit stopping the entire thread from running
-sThread.hold(n) -- sleeps until a condition is met
-```lua
-local GLOBAL,sThread=require("multi.integration.lanesManager").init()
-require("multi.all")
-multi:newAlarm(2):OnRing(function(self)
- GLOBAL["NumOfCores"]=sThread.getCores()
-end)
-multi:newAlarm(7):OnRing(function(self)
- GLOBAL["AnotherTest"]=true
-end)
-multi:newAlarm(13):OnRing(function(self)
- GLOBAL["FinalTest"]=true
-end)
-multi:newSystemThread("test",function() -- spawns a thread in another lua process
- require("multi.all") -- now you can do all of your coding with the multi library! You could even spawn more threads from here with the integration. You would need to require the interaction again though
- print("Waiting for variable: NumOfCores")
- print("Got it: ",sThread.waitFor("NumOfCores"))
- sThread.hold(function()
- return GLOBAL["AnotherTest"] -- note this would hold the entire systemthread. Spawn a coroutine thread using multi:newThread() or multi:newThreaded...
- end)
- print("Holding works!")
- multi:newThread("tests",function()
- thread.hold(function()
- return GLOBAL["FinalTest"] -- note this will not hold the entire systemthread. As seen with the TLoop constantly going!
- end)
- print("Final test works!")
- os.exit()
- end)
- local a=0
- multi:newTLoop(function()
- a=a+1
- print(a)
- end,.5)
- multi:mainloop()
-end)
-multi:mainloop()
-```
-
-Update: 1.6.0
--------------
-Changed: steps and loops
-```lua
--- Was
-step:OnStep(function(pos,self) -- same goes for tsteps as well
- print(pos)
-end)
-multi:newLoop(function(dt,self)
- print(dt)
-end)
--- Is now
-step:OnStep(function(self,pos) -- same goes for tsteps as well
- print(pos)
-end)
-multi:newLoop(function(self,dt)
- print(dt)
-end)
-```
-Reasoning I wanted to keep objects consistant, but a lot of my older libraries use the old way of doing things. Therefore I added a backwards module
-```lua
-require("multi.all")
-require("multi.compat.backwards[1,5,0]") -- allows for the use of features that were scrapped/changed in 1.6.0+
-```
-Update: 1.5.0
--------------
-Added:
-- An easy way to manage timeouts
-- Small bug fixes
-
-Update: 1.4.1 - First Public release of the library
--------------
-
-IMPORTANT:
-Every update I make aims to make things simpler more efficent and just better, but a lot of old code, which can be really big, uses a lot of older features. I know the pain of having to rewrite everything. My promise to my library users is that I will always have backwards support for older features! New ways may exist that are quicker and eaiser, but the old features/methods will be supported.
Rambling
--------
+5/23/18:
+When it comes to running code across different systems we run into a problem. It takes time to send objects from one maching to another. In the beginning only local networks will be supported. I may add support to send commands to another network to do computing. Like having your own lus cloud. userdata will never be allowed to run on other machines. It is not possible unless the library you are using allows userdata to be turned into a string and back into an object. With this feature you want to send a command that will take time or needs tons of them done millions+, reason being networks are not that "fast" and only simple objects can be sent. If you mirror your enviroment then you can do some cool things.
+
+The planned structure will be something like this:
+multi-Single Threaded Multitasking
+multi-Threads
+multi-System Threads
+multi-Network threads
+
+where netThreads can contain systemThreads which can intern contain both Threads and single threaded multitasking
+
+Nothing has been built yet, but the system will work something like this:
+#host:
+```lua
+sGLOBAL, nGlobal,sThread=require("multi.integration.networkManager").init() -- This will determine if one is using lanes,love2d, or luvit
+multi:Host("MainSystem") -- tell the network that this is the main system. Uses broadcast so that nodes know how to find the host!
+nThread = multi:newNetworkThread("NetThread_1",function(...)
+ -- basic usage
+ nGLOBAL["RemoteVaraible"] = true -- will sync data to all nodes and the host
+ sGLOBAL["LocalMachineVaraible"] = true -- will sync data to all system threads on the local machine
+ return "Hello Network!" -- send "Hello Network" back to the host node
+end)
+multi:mainloop()
+```
+#node
+```lua
+GLOBAL,sThread=require("multi.integration.networkManager").init() -- This will determine if one is using lanes,love2d, or luvit
+node = multi:newNode("NodeName","MainSystem") -- Search the network for the host, connect to it and be ready for requests!
+-- On the main thread, a simple multi:newNetworkThread thread and also non system threads, you can access global data without an issue. When dealing with system threads is when you have a problem.
+node:setLog{
+ maxLines = 10000,
+ cleanOnInterval = true,
+ cleanInterval = "day", -- every day Supports(day, week, month, year)
+ noLog = false -- default is false, make true if you do not need a log
+}
+node:settings{
+ maxJobs = 100, -- Job queues will respect this as well as the host when it is figuting out which node is under the least load. Default: 0 or infinite
+ sendLoadInterval = 60 -- every 60 seconds update the host of the nodes load
+ sendLoad = true -- default is true, tells the server how stressed the system is
+}
+multi:mainloop()
+-- Note: the node will contain a log of all the commands that it gets. A file called "NodeName.log" will contain the info. You can set the limit by lines or file size. Also you can set it to clear the log every interval of time if an error does not exist. All errors are both logged and sent to the host as well. You can have more than one host and more than one node(duh :P).
+```
+The goal of the node is to set up a simple and easy way to run commands on a remote machine.
+
+There are 2 main ways you can use this feature. 1. One node per machine with system threads being able to use the full processing power of the machine. 2. Multiple nodes on one machine where each node is acting like its own thread. And of course a mix of the two is indeed possible.
+
+
Love2d Sleeping reduces the cpu time making my load detection think the system is under more load, thus preventing it from sleeping... I will look into other means. As of right now it will not eat all of your cpu if threads are active. For now I suggest killing threads that aren't needed anymore. On lanes threads at idle use 0% cpu and it is amazing. A state machine may solve what I need though. One state being idle state that sleeps and only goes into the active state if a job request or data is sent to it... after some time of not being under load it wil switch back into the idle state... We'll see what happens.
Love2d doesn't like to send functions through channels. By defualt it does not support this. I achieve this by dumping the function and loadstring it on the thread. This however is slow. For the System Threaded Job Queue I had to change my original idea of sending functions as jobs. The current way you do it now is register a job functions once and then call that job across the thread through a queue. Each worker thread pops from the queue and returns the job. The Job ID is automatically updated and allows you to keep track of the order that the data comes in. A table with # indexes can be used to originze the data...
@@ -1438,4 +879,4 @@ I have been using this (EventManager --> MultiManager --> now multi) for my own
I added my old versions to this library... It started out as the EventManager and was kinda crappy but it was the start to this library. It kept getting better and better until it became what it is today. There are some features that nolonger exist in the latest version, but they were remove because they were useless... I added these files to the github so for those interested can see into my mind in a sense and see how I developed the library before I used github.
-The first version of the EventManager was function based not object based and benched at about 2000 steps per second... Yeah that was bad... I used loadstring and it was a mess... Take a look and see how it grew throughout the years I think it may intrest some of you guys!
+The first version of the EventManager was function based not object based and benched at about 2000 steps per second... Yeah that was bad... I used loadstring and it was a mess... Take a look and see how it grew throughout the years I think it may intrest some of you guys!
\ No newline at end of file
diff --git a/changes.html b/changes.html
new file mode 100644
index 0000000..1fdfbed
--- /dev/null
+++ b/changes.html
@@ -0,0 +1,868 @@
+
+
+
+
+ changes.html
+
+
+
+
+
+
+Changes
Update: 1.10.0
Note: The library is now considered to be stable!
Upcoming: Network parallelism is on the way. It is in the works and should be released soon
Added:
+- isMainThread true/nil
- multi:newSystemThreadedConnection(name,protect) — Works like normal connections, but are able to trigger events across threads
Example of threaded connections
package.path="?/init.lua;?.lua;"..package.path
+local GLOBAL,THREAD=require("multi.integration.lanesManager").init()
+multi:newSystemThread("Test_Thread_1",function()
+ connOut = THREAD.waitFor("ConnectionNAMEHERE"):init()
+ connOut(function(arg)
+ print(THREAD.getName(),arg)
+ end)
+ multi:mainloop()
+end)
+multi:newSystemThread("Test_Thread_2",function()
+ connOut = THREAD.waitFor("ConnectionNAMEHERE"):init()
+ connOut(function(arg)
+ print(THREAD.getName(),arg)
+ end)
+ multi:mainloop()
+end)
+connOut = multi:newSystemThreadedConnection("ConnectionNAMEHERE"):init()
+a=0
+connOut(function(arg)
+ print("Main",arg)
+end)
+multi:newTLoop(function()
+ a=a+1
+ connOut:Fire("Test From Main Thread: "..a.."\n")
+end,1)
+
Fixed:
loveManager and shared threading objects
+- sThread.waitFor()
- sThread.hold()
- some typos
- SystemThreadedTables (They now work on both lanes and love2d as expected)
Example of threaded tables
package.path="?/init.lua;?.lua;"..package.path
+local GLOBAL,sThread=require("multi.integration.lanesManager").init()
+multi:newSystemThread("Test_Thread_1",function()
+ require("multi")
+ test = sThread.waitFor("testthing"):init()
+ multi:newTLoop(function()
+ print("------")
+ for i,v in pairs(test.tab) do
+ print("T1",i,v)
+ end
+ end,1)
+ multi:mainloop()
+end)
+multi:newSystemThread("Test_Thread_1",function()
+ require("multi")
+ test = sThread.waitFor("testthing"):init()
+ multi:newTLoop(function()
+ print("------")
+ for i,v in pairs(test.tab) do
+ print("T2",i,v)
+ end
+ end,1)
+ multi:mainloop()
+end)
+test = multi:newSystemThreadedTable("testthing"):init()
+multi:newTLoop(function()
+ local a,b = multi.randomString(8),multi.randomString(4)
+ print(">",a,b)
+ test[a]=b
+end,1)
+multi:mainloop()
+
Update: 1.9.2
Added:
+- (THREAD).kill() kills a thread. Note: THREAD is based on what you name it
- newTimeStamper() Part of the persistant systems… Useful for when you are running this library for a massive amount of time… like years stright!
Allows one to hook to timed events such as whenever the clock strikes midnight or when the day turns to monday. The event is only done once though. so as soon as monday is set it would trigger then not trigger again until next monday
works for seconds, minutes, days, months, year.stamper = multi:newTimeStamper()
+stamper:OnTime(int hour,int minute,int second,func) or stamper:OnTime(string time,func) time as 00:00:00
+stamper:OnHour(int hour,func)
+stamper:OnMinute(int minute,func)
+stamper:OnSecond(int second,func)
+stamper:OnDay(int day,func) or stamper:OnDay(string day,func) Mon, Tues, Wed, etc...
+stamper:OnMonth(int month,func)
+stamper:OnYear(int year,func)
+
+Updated: - LoadBalancing, well bettwr load balancing than existed before. This one allowd for multiple processes to have their own load reading. Calling this on the multi object will return the total load for the entire multi enviroment… loads of other processes are indeed affected by what other processes are doing. However if you combine prorioty to the mix of things then you will get differing results… these results however will most likely be higher than normal… different pirorities will have different default thresholds of performence.
Fixed:
+- Thread.getName() should now work on lanes and love2d, haven’t tested ut nuch with the luvit side of things…
- A bug with the lovemanager table.remove arguments were backwards haha
- The queue object in the love2d threading has been fixed! It now supports sending all objects (even functions as long as no upvalues are present!)
Changed:
+- SystemThreadedJobQueues now have built in load management so they are not constantly at 100% cpu usage.
- SystemThreadedJobQueues pushJob now retunts an id of that job which will match the same one that OnJobCompleted returns
Update: 1.9.1
Added:
+- Integration “multi.integration.luvitManager”
- Limited… Only the basic multi:newSystemThread(…) will work
- Not even data passing will work other than arguments… If using the bin library you can pass tables and function… Even full objects as long as inner recursion is not preasent.
Updated:
+- multi:newSystemThread(name,func,…)
- It will not pass the … to the func(). Do not know why this wasn’t done in the first place :P
- Also multi:getPlatform(will now return “luvit” if using luvit… Though Idk if module creators would use the multi library when inside the luvit enviroment
Update: 1.9.0
Added:
+- multiobj:ToString() — returns a string repersenting the object
- multi:newFromString(str) — creates an object from a string
Works on threads and regular objects. Requires the latest bin library to work!
talarm=multi:newThreadedAlarm("AlarmTest",5)
+talarm:OnRing(function()
+ print("Ring!")
+end)
+bin.new(talarm:ToString()):tofile("test.dat")
+-- multi:newFromString(bin.load("test.dat"))
+
— A more seamless way to use this will be made in the form of state saving.
This is still a WIP
processes, timers, timemasters, watchers, and queuers have not been worked on yet
Update: 1.8.7
Added:
+- multi.timer(func,…)
function test(a,b,c)
+ print("Running...")
+ a=0
+ for i=1,1000000000 do
+ a=a+1
+ end
+ return a,b+c
+end
+print(multi.timer(test,1,2,3))
+print(multi.timer(test,1,2,3))
+-- multi.timer returns the time taken then the arguments from the function... Uses unpack so careful of nil values!
+
Update: 1.8.6
Added:
+- jobQueue:doToAll(function)
- jobQueue:start() is now required Call this after all calls to registerJob()’s. Calling it afterwards will not guarantee your next push job with that job will work. Not calling this will make pushing jobs impossible!
- Fixed a bug with love2d Threaded Queue
- Fixed some bugs
- Old versions of this library! It stems back from 2012 see rambling for more info…
This will run said function in every thread.
-- Going to use love2d code this time, almost the same as last time... See ramblings
+require("core.Library")
+GLOBAL,sThread=require("multi.integration.loveManager").init() -- load the love2d version of the lanesManager and requires the entire multi library
+require("core.GuiManager")
+gui.ff.Color=Color.Black
+jQueue=multi:newSystemThreadedJobQueue()
+jQueue:registerJob("TEST_JOB",function(a,s)
+ math.randomseed(s)
+ TEST_JOB2()
+ return math.random(0,255)
+end)
+jQueue:registerJob("TEST_JOB2",function()
+ print("Test Works!")
+end)
+-- 1.8.6 EXAMPLE Change
+jQueue:start() -- This is now needed!
+--
+jQueue:doToAll(function()
+ print("Doing this 2? times!")
+end)
+tableOfOrder={}
+jQueue.OnJobCompleted(function(JOBID,n)
+ tableOfOrder[JOBID]=n
+ if #tableOfOrder==10 then
+ t.text="We got all of the pieces!"
+ end
+end)
+for i=1,10 do -- Job Name of registered function, ... varargs
+ jQueue:pushJob("TEST_JOB","This is a test!",math.random(1,1000000))
+end
+t=gui:newTextLabel("no done yet!",0,0,300,100)
+t:centerX()
+t:centerY()
+
Update: 1.8.5
Added:
+- SystemThreadedExecute(cmd)
Allows the execution of system calls without hold up. It is possible to do the same using io.popen()! You decide which works best for you!
local GLOBAL,sThread=require("multi.integration.lanesManager").init()
+cmd=multi:newSystemThreadedExecute("SystemThreadedExecuteTest.lua") -- This file is important!
+cmd.OnCMDFinished(function(code) -- callback function to grab the exit code... Called when the command goes through
+ print("Got Code: "..code)
+end)
+multi:newTLoop(function()
+ print("...") -- lets show that we aren't being held up
+end,1)
+multi:mainloop()
+
Update: 1.8.4
Added:
+- multi:newSystemThreadedJobQueue()
- Improved stability of the library
- Fixed a bug that made the benchmark and getload commands non-thread(coroutine) safe
- Tweaked the loveManager to help improve idle cpu usage
- Minor tweaks to the coroutine scheduling
Using multi:newSystemThreadedJobQueue()
First you need to create the object
This works the same way as love2d as it does with lanes… It is getting increasing harder to make both work the same way with speed in mind… Anyway…
-- Creating the object using lanes manager to show case this. Examples has the file for love2d
+local GLOBAL,sThread=require("multi.integration.lanesManager").init()
+jQueue=multi:newSystemThreadedJobQueue(n) -- this internally creates System threads. By defualt it will use the # of processors on your system You can set this number though.
+-- Only create 1 jobqueue! For now making more than 1 is buggy. You only really need one though. Just register new functions if you want 1 queue to do more. The one reason though is keeping track of jobIDs. I have an idea that I will roll out in the next update.
+jQueue:registerJob("TEST_JOB",function(a,s)
+ math.randomseed(s)
+ -- We will push a random #
+ TEST_JOB2() -- You can call other registered functions as well!
+ return math.random(0,255) -- send the result to the main thread
+end)
+jQueue:registerJob("TEST_JOB2",function()
+ print("Test Works!") -- this is called from the job since it is registered on the same queue
+end)
+tableOfOrder={} -- This is how we will keep order of our completed jobs. There is no guarantee that the order will be correct
+jQueue.OnJobCompleted(function(JOBID,n) -- whenever a job is completed you hook to the event that is called. This passes the JOBID folled by the returns of the job
+ -- JOBID is the completed job, starts at 1 and counts up by 1.
+ -- Threads finish at different times so jobids may be passed out of order! Be sure to have a way to order them
+ tableOfOrder[JOBID]=n -- we order ours by putting them into a table
+ if #tableOfOrder==10 then
+ print("We got all of the pieces!")
+ end
+end)
+-- Lets push the jobs now
+for i=1,10 do -- Job Name of registered function, ... varargs
+ jQueue:pushJob("TEST_JOB","This is a test!",math.random(1,1000000))
+end
+print("I pushed all of the jobs :)")
+multi:mainloop() -- Start the main loop :D
+
Thats it from this version!
Update: 1.8.3
Added:
New Mainloop functions Below you can see the slight differences… Function overhead is not too bad in lua, but has a real difference. multi:mainloop() and multi:unprotectedMainloop() use the same algorithm yet the dedicated unprotected one is slightly faster due to having less function overhead.
+- multi:mainloop()* — Bench: 16830003 Steps in 3 second(s)!
- multi:protectedMainloop() — Bench: 16699308 Steps in 3 second(s)!
- multi:unprotectedMainloop() — Bench: 16976627 Steps in 3 second(s)!
- multi:prioritizedMainloop1() — Bench: 15007133 Steps in 3 second(s)!
- multi:prioritizedMainloop2() — Bench: 15526248 Steps in 3 second(s)!
* The OG mainloop function remains the same and old methods to achieve what we have with the new ones still exist
These new methods help by removing function overhead that is caused through the original mainloop function. The one downside is that you no longer have the flexiblity to change the processing during runtime.
However there is a work around! You can use processes to run multiobjs as well and use the other methods on them.
I may make a full comparison between each method and which is faster, but for now trust that the dedicated ones with less function overhead are infact faster. Not by much but still faster. :D
Update: 1.8.2
Added:
+- multi:newsystemThreadedTable(name) NOTE: Metatables are not supported in transfers. However there is a work around obj:init() that you see does this. Take a look in the multi/integration/shared/shared.lua files to see how I did it!
- Modified the GLOBAL metatable to sync before doing its tests
- multi._VERSION was multi.Version, felt it would be more consistant this way… I left the old way of getting the version just incase someone has used that way. It will eventually be gone. Also multi:getVersion() will do the job just as well and keep your code nice and update related bug free!
- Also everything that is included in the: multi/integration/shared/shared.lua (Which is loaded automatically) works in both lanes and love2d enviroments!
The threaded table is setup just like the threaded queue.
It provids GLOBAL like features without having to write to GLOBAL!
This is useful for module creators who want to keep their data private, but also use GLOBAL like coding.
It has a few features that makes it a bit better than plain ol GLOBAL (For now…)
(ThreadedTable - TT for short)
+- TT:waitFor(name)
- TT:sync()
- TT[“var”]=value
- print(TT[“var”])
we also have the “sync” method, this one was made for love2d because we do a syncing trick to get data in a table format. The lanes side has a sync method as well so no worries. Using indexing calls sync once and may grab your variable. This allows you to have the lanes indexing ‘like’ syntax when doing regular indexing in love2d side of the module. As of right now both sides work flawlessly! And this effect is now the GLOBAL as well
On GLOBALS sync is a internal method for keeping the GLOBAL table in order. You can still use sThread.waitFor(name) to wait for variables that may of may not yet exist!
Time for some examples:
Using multi:newSystemThreadedTable(name)
-- lanes Desktop lua! NOTE: this is in lanesintergratetest6.lua in the examples folder
+local GLOBAL,sThread=require("multi.integration.lanesManager").init()
+test=multi:newSystemThreadedTable("YO"):init()
+test["test1"]="lol"
+multi:newSystemThread("test",function()
+ tab=sThread.waitFor("YO"):init()
+ print(tab:has("test1"))
+ sThread.sleep(3)
+ tab["test2"]="Whats so funny?"
+end)
+multi:newThread("test2",function()
+ print(test:waitFor("test2"))
+end)
+multi:mainloop()
+
-- love2d gaming lua! NOTE: this is in main4.lua in the love2d examples
+require("core.Library")
+GLOBAL,sThread=require("multi.integration.loveManager").init() -- load the love2d version of the lanesManager and requires the entire multi library
+require("core.GuiManager")
+gui.ff.Color=Color.Black
+test=multi:newSystemThreadedTable("YO"):init()
+test["test1"]="lol"
+multi:newSystemThread("test",function()
+ tab=sThread.waitFor("YO"):init()
+ print(tab["test1"])
+ sThread.sleep(3)
+ tab["test2"]="Whats so funny?"
+end)
+multi:newThread("test2",function()
+ print(test:waitFor("test2"))
+ t.text="DONE!"
+end)
+t=gui:newTextLabel("no done yet!",0,0,300,100)
+t:centerX()
+t:centerY()
+
Update: 1.8.1
No real change!
Changed the structure of the library. Combined the coroutine based threads into the core!
Only compat and integrations are not part of the core and never will be by nature.
This should make the library more convient to use.
I left multi/all.lua file so if anyone had libraries/projects that used that it will still work!
Updated from 1.7.6 to 1.8.0
(How much thread could a thread thread if a thread could thread thread?)
Added:
+- multi:newSystemThreadedQueue()
- multi:systemThreadedBenchmark()
- More example files
- multi:canSystemThread() — true if an integration was added false otherwise (For module creation)
- Fixed a few bugs in the loveManager
Using multi:systemThreadedBenchmark()
package.path="?/init.lua;"..package.path
+local GLOBAL,sThread=require("multi.integration.lanesManager").init()
+multi:systemThreadedBenchmark(3):OnBench(function(self,count)
+ print("First Bench: "..count)
+ multi:systemThreadedBenchmark(3,"All Threads: ")
+end)
+multi:mainloop()
+
Using multi:newSystemThreadedQueue()
Quick Note: queues shared across multiple objects will be pulling from the same “queue” keep this in mind when coding! Also the queue respects direction a push on the thread side cannot be popped on the thread side… Same goes for the mainthread!
Turns out i was wrong about this…
-- in love2d, this file will be in the same example folder as before, but is named main2.lua
+require("core.Library")
+GLOBAL,sThread=require("multi.integration.loveManager").init() -- load the love2d version of the lanesManager and requires the entire multi library
+--IMPORTANT
+-- Do not make the above local, this is the one difference that the lanesManager does not have
+-- If these are local the functions will have the upvalues put into them that do not exist on the threaded side
+-- You will need to ensure that the function does not refer to any upvalues in its code. It will print an error if it does though
+-- Also each thread has a .1 second delay! This is used to generate a random values for each thread!
+require("core.GuiManager")
+gui.ff.Color=Color.Black
+function multi:newSystemThreadedQueue(name) -- in love2d this will spawn a channel on both ends
+ local c={}
+ c.name=name
+ if love then
+ if love.thread then
+ function c:init()
+ self.chan=love.thread.getChannel(self.name)
+ function self:push(v)
+ self.chan:push(v)
+ end
+ function self:pop()
+ return self.chan:pop()
+ end
+ GLOBAL[self.name]=self
+ return self
+ end
+ return c
+ else
+ error("Make sure you required the love.thread module!")
+ end
+ else
+ c.linda=lanes.linda()
+ function c:push(v)
+ self.linda:send("Q",v)
+ end
+ function c:pop()
+ return ({self.linda:receive(0,"Q")})[2]
+ end
+ function c:init()
+ return self
+ end
+ GLOBAL[name]=c
+ end
+ return c
+end
+queue=multi:newSystemThreadedQueue("QUEUE"):init()
+queue:push("This is a test")
+queue:push("This is a test2")
+queue:push("This is a test3")
+queue:push("This is a test4")
+multi:newSystemThread("test2",function()
+ queue=sThread.waitFor("QUEUE"):init()
+ data=queue:pop()
+ while data do
+ print(data)
+ data=queue:pop()
+ end
+ queue:push("DONE!")
+end)
+multi:newThread("test!",function()
+ thread.hold(function() return queue:pop() end)
+ t.text="Done!"
+end)
+t=gui:newTextLabel("no done yet!",0,0,300,100)
+t:centerX()
+t:centerY()
+
In Lanes
-- The code is compatible with each other, I just wanted to show different things you can do in both examples
+-- This file can be found in the examples folder as lanesintegrationtest4.lua
+local GLOBAL,sThread=require("multi.integration.lanesManager").init()
+queue=multi:newSystemThreadedQueue("QUEUE"):init()
+queue:push("This is a test")
+queue:push("This is a test2")
+queue:push("This is a test3")
+queue:push("This is a test4")
+multi:newSystemThread("test2",function()
+ queue=sThread.waitFor("QUEUE"):init()
+ data=queue:pop()
+ while data do
+ print(data)
+ data=queue:pop()
+ end
+ queue:push("This is a test5")
+ queue:push("This is a test6")
+ queue:push("This is a test7")
+ queue:push("This is a test8")
+end)
+multi:newThread("test!",function() -- this is a lua thread
+ thread.sleep(.1)
+ data=queue:pop()
+ while data do
+ print(data)
+ data=queue:pop()
+ end
+end)
+multi:mainloop()
+
Update: 1.7.6
Fixed:
Typos like always
Added:
multi:getPlatform() — returns “love2d” if using the love2d platform or returns “lanes” if using lanes for threading
examples files
In Events added method setTask(func)
The old way still works and is more convient to be honest, but I felt a method to do this was ok.
Updated:
some example files to reflect changes to the core. Changes allow for less typing
loveManager to require the compat if used so you don’t need 2 require line to retrieve the library
Update: 1.7.5
Fixed some typos in the readme… (I am sure there are more there are always more)
Added more features for module support
TODO:
Work on performance of the library… I see 3 places where I can make this thing run quicker
I’ll show case some old versions of the multitasking library eventually so you can see its changes in days past!
Update: 1.7.4
Added: the example folder which will be populated with more examples in the near future!
The loveManager integration that mimics the lanesManager integration almost exactly to keep coding in both enviroments as close to possible. This is done mostly for library creation support!
An example of the loveManager in action using almost the same code as the lanesintergreationtest2.lua
NOTE: This code has only been tested to work on love2d version 1.10.2 thoough it should work version 0.9.0
require("core.Library") -- Didn't add this to a repo yet! Will do eventually... Allows for injections and other cool things
+require("multi.compat.love2d") -- allows for multitasking and binds my libraies to the love2d engine that i am using
+GLOBAL,sThread=require("multi.integration.loveManager").init() -- load the love2d version of the lanesManager
+--IMPORTANT
+-- Do not make the above local, this is the one difference that the lanesManager does not have
+-- If these are local the functions will have the upvalues put into them that do not exist on the threaded side
+-- You will need to ensure that the function does not refer to any upvalues in its code. It will print an error if it does though
+-- Also each thread has a .1 second delay! This is used to generate a random values for each thread!
+require("core.GuiManager") -- allows the use of graphics in the program.
+gui.ff.Color=Color.Black
+function comma_value(amount)
+ local formatted = amount
+ while true do
+ formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
+ if (k==0) then
+ break
+ end
+ end
+ return formatted
+end
+multi:newSystemThread("test1",function() -- Another difference is that the multi library is already loaded in the threaded enviroment as well as a call to multi:mainloop()
+ multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 1"):OnBench(function(self,c) GLOBAL["T1"]=c multi:Stop() end)
+end)
+multi:newSystemThread("test2",function() -- spawns a thread in another lua process
+ multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 2"):OnBench(function(self,c) GLOBAL["T2"]=c multi:Stop() end)
+end)
+multi:newSystemThread("test3",function() -- spawns a thread in another lua process
+ multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 3"):OnBench(function(self,c) GLOBAL["T3"]=c multi:Stop() end)
+end)
+multi:newSystemThread("test4",function() -- spawns a thread in another lua process
+ multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 4"):OnBench(function(self,c) GLOBAL["T4"]=c multi:Stop() end)
+end)
+multi:newSystemThread("test5",function() -- spawns a thread in another lua process
+ multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 5"):OnBench(function(self,c) GLOBAL["T5"]=c multi:Stop() end)
+end)
+multi:newSystemThread("test6",function() -- spawns a thread in another lua process
+ multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 6"):OnBench(function(self,c) GLOBAL["T6"]=c multi:Stop() end)
+end)
+multi:newSystemThread("Combiner",function() -- spawns a thread in another lua process
+ function comma_value(amount)
+ local formatted = amount
+ while true do
+ formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
+ if (k==0) then
+ break
+ end
+ end
+ return formatted
+ end
+ local b=comma_value(tostring(sThread.waitFor("T1")+sThread.waitFor("T2")+sThread.waitFor("T3")+sThread.waitFor("T4")+sThread.waitFor("T5")+sThread.waitFor("T6")))
+ GLOBAL["DONE"]=b
+end)
+multi:newThread("test0",function()
+ -- sThread.waitFor("DONE") -- lets hold the main thread completely so we don't eat up cpu
+ -- os.exit()
+ -- when the main thread is holding there is a chance that error handling on the system threads may not work!
+ -- instead we can do this
+ while true do
+ thread.skip(1) -- allow error handling to take place... Otherwise lets keep the main thread running on the low
+ -- Before we held just because we could... But this is a game and we need to have logic continue
+ --sThreadM.sleep(.001) -- Sleeping for .001 is a greeat way to keep cpu usage down. Make sure if you aren't doing work to rest. Abuse the hell out of GLOBAL if you need to :P
+ if GLOBAL["DONE"] then
+ t.text="Bench: "..GLOBAL["DONE"]
+ end
+ end
+end)
+GLOBAL["Bench"]=3
+t=gui:newTextLabel("no done yet!",0,0,300,100)
+t:centerX()
+t:centerY()
+
Update: 1.7.3
Changed how requiring the library works!
require("multi.all") Will still work as expected; however, with the exception of threading, compat, and integrations everything else has been moved into the core of the library.
-- This means that these are no longer required and will cause an error if done so
+require("multi.loop")
+require("multi.alarm")
+require("multi.updater")
+require("multi.tloop")
+require("multi.watcher")
+require("multi.tstep")
+require("multi.step")
+require("multi.task")
+-- ^ they are all part of the core now
+
Update: 1.7.2
Moved updaters, loops, and alarms into the init.lua file. I consider them core features and they are referenced in the init.lua file so they need to exist there. Threaded versions are still separate though. Added another example file
Update: 1.7.1 Bug Fixes Only
Update: 1.7.0
Modified: multi.integration.lanesManager.lua
It is now in a stable and simple state Works with the latest lanes version! Tested with version 3.11 I cannot promise that everything will work with eariler versions. Future versions are good though.
Example Usage:
sThread is a handle to a global interface for system threads to interact with themself
thread is the interface for multithreads as seen in the threading section
GLOBAL a table that can be used throughout each and every thread
sThreads have a few methods
sThread.set(name,val) — you can use the GLOBAL table instead modifies the same table anyway
sThread.get(name) — you can use the GLOBAL table instead modifies the same table anyway
sThread.waitFor(name) — waits until a value exists, if it does it returns it
sThread.getCores() — returns the number of cores on your cpu
sThread.sleep(n) — sleeps for a bit stopping the entire thread from running
sThread.hold(n) — sleeps until a condition is met
local GLOBAL,sThread=require("multi.integration.lanesManager").init()
+require("multi.all")
+multi:newAlarm(2):OnRing(function(self)
+ GLOBAL["NumOfCores"]=sThread.getCores()
+end)
+multi:newAlarm(7):OnRing(function(self)
+ GLOBAL["AnotherTest"]=true
+end)
+multi:newAlarm(13):OnRing(function(self)
+ GLOBAL["FinalTest"]=true
+end)
+multi:newSystemThread("test",function() -- spawns a thread in another lua process
+ require("multi.all") -- now you can do all of your coding with the multi library! You could even spawn more threads from here with the integration. You would need to require the interaction again though
+ print("Waiting for variable: NumOfCores")
+ print("Got it: ",sThread.waitFor("NumOfCores"))
+ sThread.hold(function()
+ return GLOBAL["AnotherTest"] -- note this would hold the entire systemthread. Spawn a coroutine thread using multi:newThread() or multi:newThreaded...
+ end)
+ print("Holding works!")
+ multi:newThread("tests",function()
+ thread.hold(function()
+ return GLOBAL["FinalTest"] -- note this will not hold the entire systemthread. As seen with the TLoop constantly going!
+ end)
+ print("Final test works!")
+ os.exit()
+ end)
+ local a=0
+ multi:newTLoop(function()
+ a=a+1
+ print(a)
+ end,.5)
+ multi:mainloop()
+end)
+multi:mainloop()
+
Update: 1.6.0
Changed: steps and loops
-- Was
+step:OnStep(function(pos,self) -- same goes for tsteps as well
+ print(pos)
+end)
+multi:newLoop(function(dt,self)
+ print(dt)
+end)
+-- Is now
+step:OnStep(function(self,pos) -- same goes for tsteps as well
+ print(pos)
+end)
+multi:newLoop(function(self,dt)
+ print(dt)
+end)
+
Reasoning I wanted to keep objects consistant, but a lot of my older libraries use the old way of doing things. Therefore I added a backwards module
require("multi.all")
+require("multi.compat.backwards[1,5,0]") -- allows for the use of features that were scrapped/changed in 1.6.0+
+
Update: 1.5.0
Added:
+- An easy way to manage timeouts
- Small bug fixes
Update: 1.4.1 - First Public release of the library
IMPORTANT:
Every update I make aims to make things simpler more efficent and just better, but a lot of old code, which can be really big, uses a lot of older features. I know the pain of having to rewrite everything. My promise to my library users is that I will always have backwards support for older features! New ways may exist that are quicker and eaiser, but the old features/methods will be supported.
+
+
+
+
diff --git a/changes.md b/changes.md
new file mode 100644
index 0000000..e9e6f9a
--- /dev/null
+++ b/changes.md
@@ -0,0 +1,693 @@
+Changes
+-------
+Update: 1.10.0
+-------------
+**Note:** The library is now considered to be stable!
+**Upcoming:** Network parallelism is on the way. It is in the works and should be released soon
+
+Added:
+------
+- isMainThread true/nil
+- multi:newSystemThreadedConnection(name,protect) -- Works like normal connections, but are able to trigger events across threads
+
+Example of threaded connections
+```lua
+package.path="?/init.lua;?.lua;"..package.path
+local GLOBAL,THREAD=require("multi.integration.lanesManager").init()
+multi:newSystemThread("Test_Thread_1",function()
+ connOut = THREAD.waitFor("ConnectionNAMEHERE"):init()
+ connOut(function(arg)
+ print(THREAD.getName(),arg)
+ end)
+ multi:mainloop()
+end)
+multi:newSystemThread("Test_Thread_2",function()
+ connOut = THREAD.waitFor("ConnectionNAMEHERE"):init()
+ connOut(function(arg)
+ print(THREAD.getName(),arg)
+ end)
+ multi:mainloop()
+end)
+connOut = multi:newSystemThreadedConnection("ConnectionNAMEHERE"):init()
+a=0
+connOut(function(arg)
+ print("Main",arg)
+end)
+multi:newTLoop(function()
+ a=a+1
+ connOut:Fire("Test From Main Thread: "..a.."\n")
+end,1)
+```
+
+Fixed:
+------
+**loveManager** and **shared threading objects**
+- sThread.waitFor()
+- sThread.hold()
+- some typos
+- SystemThreadedTables (They now work on both lanes and love2d as expected)
+
+Example of threaded tables
+```lua
+package.path="?/init.lua;?.lua;"..package.path
+local GLOBAL,sThread=require("multi.integration.lanesManager").init()
+multi:newSystemThread("Test_Thread_1",function()
+ require("multi")
+ test = sThread.waitFor("testthing"):init()
+ multi:newTLoop(function()
+ print("------")
+ for i,v in pairs(test.tab) do
+ print("T1",i,v)
+ end
+ end,1)
+ multi:mainloop()
+end)
+multi:newSystemThread("Test_Thread_1",function()
+ require("multi")
+ test = sThread.waitFor("testthing"):init()
+ multi:newTLoop(function()
+ print("------")
+ for i,v in pairs(test.tab) do
+ print("T2",i,v)
+ end
+ end,1)
+ multi:mainloop()
+end)
+test = multi:newSystemThreadedTable("testthing"):init()
+multi:newTLoop(function()
+ local a,b = multi.randomString(8),multi.randomString(4)
+ print(">",a,b)
+ test[a]=b
+end,1)
+multi:mainloop()
+```
+
+Update: 1.9.2
+-------------
+Added:
+- (THREAD).kill() kills a thread. Note: THREAD is based on what you name it
+- newTimeStamper() Part of the persistant systems... Useful for when you are running this library for a massive amount of time... like years stright!
+Allows one to hook to timed events such as whenever the clock strikes midnight or when the day turns to monday. The event is only done once though. so as soon as monday is set it would trigger then not trigger again until next monday
+works for seconds, minutes, days, months, year.
+```lua
+stamper = multi:newTimeStamper()
+stamper:OnTime(int hour,int minute,int second,func) or stamper:OnTime(string time,func) time as 00:00:00
+stamper:OnHour(int hour,func)
+stamper:OnMinute(int minute,func)
+stamper:OnSecond(int second,func)
+stamper:OnDay(int day,func) or stamper:OnDay(string day,func) Mon, Tues, Wed, etc...
+stamper:OnMonth(int month,func)
+stamper:OnYear(int year,func)
+```
+Updated:
+- LoadBalancing, well bettwr load balancing than existed before. This one allowd for multiple processes to have their own load reading. Calling this on the multi object will return the total load for the entire multi enviroment... loads of other processes are indeed affected by what other processes are doing. However if you combine prorioty to the mix of things then you will get differing results... these results however will most likely be higher than normal... different pirorities will have different default thresholds of performence.
+
+Fixed:
+- Thread.getName() should now work on lanes and love2d, haven't tested ut nuch with the luvit side of things...
+- A bug with the lovemanager table.remove arguments were backwards haha
+- The queue object in the love2d threading has been fixed! It now supports sending all objects (even functions as long as no upvalues are present!)
+
+Changed:
+- SystemThreadedJobQueues now have built in load management so they are not constantly at 100% cpu usage.
+- SystemThreadedJobQueues pushJob now retunts an id of that job which will match the same one that OnJobCompleted returns
+
+
+Update: 1.9.1
+-------------
+Added:
+- Integration "multi.integration.luvitManager"
+- Limited... Only the basic multi:newSystemThread(...) will work
+- Not even data passing will work other than arguments... If using the bin library you can pass tables and function... Even full objects as long as inner recursion is not preasent.
+
+Updated:
+- multi:newSystemThread(name,func,...)
+- It will not pass the ... to the func(). Do not know why this wasn't done in the first place :P
+- Also multi:getPlatform(will now return "luvit" if using luvit... Though Idk if module creators would use the multi library when inside the luvit enviroment
+
+Update: 1.9.0
+-------------
+Added:
+- multiobj:ToString() -- returns a string repersenting the object
+- multi:newFromString(str) -- creates an object from a string
+
+Works on threads and regular objects. Requires the latest bin library to work!
+```lua
+talarm=multi:newThreadedAlarm("AlarmTest",5)
+talarm:OnRing(function()
+ print("Ring!")
+end)
+bin.new(talarm:ToString()):tofile("test.dat")
+-- multi:newFromString(bin.load("test.dat"))
+```
+-- A more seamless way to use this will be made in the form of state saving.
+This is still a WIP
+processes, timers, timemasters, watchers, and queuers have not been worked on yet
+Update: 1.8.7
+-------------
+Added:
+- multi.timer(func,...)
+
+```lua
+function test(a,b,c)
+ print("Running...")
+ a=0
+ for i=1,1000000000 do
+ a=a+1
+ end
+ return a,b+c
+end
+print(multi.timer(test,1,2,3))
+print(multi.timer(test,1,2,3))
+-- multi.timer returns the time taken then the arguments from the function... Uses unpack so careful of nil values!
+```
+Update: 1.8.6
+-------------
+Added:
+- jobQueue:doToAll(function)
+- jobQueue:start() is now required Call this after all calls to registerJob()'s. Calling it afterwards will not guarantee your next push job with that job will work. Not calling this will make pushing jobs impossible!
+- Fixed a bug with love2d Threaded Queue
+- Fixed some bugs
+- Old versions of this library! It stems back from 2012 see rambling for more info...
+
+This will run said function in every thread.
+```lua
+-- Going to use love2d code this time, almost the same as last time... See ramblings
+require("core.Library")
+GLOBAL,sThread=require("multi.integration.loveManager").init() -- load the love2d version of the lanesManager and requires the entire multi library
+require("core.GuiManager")
+gui.ff.Color=Color.Black
+jQueue=multi:newSystemThreadedJobQueue()
+jQueue:registerJob("TEST_JOB",function(a,s)
+ math.randomseed(s)
+ TEST_JOB2()
+ return math.random(0,255)
+end)
+jQueue:registerJob("TEST_JOB2",function()
+ print("Test Works!")
+end)
+-- 1.8.6 EXAMPLE Change
+jQueue:start() -- This is now needed!
+--
+jQueue:doToAll(function()
+ print("Doing this 2? times!")
+end)
+tableOfOrder={}
+jQueue.OnJobCompleted(function(JOBID,n)
+ tableOfOrder[JOBID]=n
+ if #tableOfOrder==10 then
+ t.text="We got all of the pieces!"
+ end
+end)
+for i=1,10 do -- Job Name of registered function, ... varargs
+ jQueue:pushJob("TEST_JOB","This is a test!",math.random(1,1000000))
+end
+t=gui:newTextLabel("no done yet!",0,0,300,100)
+t:centerX()
+t:centerY()
+```
+Update: 1.8.5
+-------------
+Added:
+- SystemThreadedExecute(cmd)
+
+Allows the execution of system calls without hold up. It is possible to do the same using io.popen()! You decide which works best for you!
+```lua
+local GLOBAL,sThread=require("multi.integration.lanesManager").init()
+cmd=multi:newSystemThreadedExecute("SystemThreadedExecuteTest.lua") -- This file is important!
+cmd.OnCMDFinished(function(code) -- callback function to grab the exit code... Called when the command goes through
+ print("Got Code: "..code)
+end)
+multi:newTLoop(function()
+ print("...") -- lets show that we aren't being held up
+end,1)
+multi:mainloop()
+```
+Update: 1.8.4
+-------------
+Added:
+- multi:newSystemThreadedJobQueue()
+- Improved stability of the library
+- Fixed a bug that made the benchmark and getload commands non-thread(coroutine) safe
+- Tweaked the loveManager to help improve idle cpu usage
+- Minor tweaks to the coroutine scheduling
+
+# Using multi:newSystemThreadedJobQueue()
+First you need to create the object
+This works the same way as love2d as it does with lanes... It is getting increasing harder to make both work the same way with speed in mind... Anyway...
+```lua
+-- Creating the object using lanes manager to show case this. Examples has the file for love2d
+local GLOBAL,sThread=require("multi.integration.lanesManager").init()
+jQueue=multi:newSystemThreadedJobQueue(n) -- this internally creates System threads. By defualt it will use the # of processors on your system You can set this number though.
+-- Only create 1 jobqueue! For now making more than 1 is buggy. You only really need one though. Just register new functions if you want 1 queue to do more. The one reason though is keeping track of jobIDs. I have an idea that I will roll out in the next update.
+jQueue:registerJob("TEST_JOB",function(a,s)
+ math.randomseed(s)
+ -- We will push a random #
+ TEST_JOB2() -- You can call other registered functions as well!
+ return math.random(0,255) -- send the result to the main thread
+end)
+jQueue:registerJob("TEST_JOB2",function()
+ print("Test Works!") -- this is called from the job since it is registered on the same queue
+end)
+tableOfOrder={} -- This is how we will keep order of our completed jobs. There is no guarantee that the order will be correct
+jQueue.OnJobCompleted(function(JOBID,n) -- whenever a job is completed you hook to the event that is called. This passes the JOBID folled by the returns of the job
+ -- JOBID is the completed job, starts at 1 and counts up by 1.
+ -- Threads finish at different times so jobids may be passed out of order! Be sure to have a way to order them
+ tableOfOrder[JOBID]=n -- we order ours by putting them into a table
+ if #tableOfOrder==10 then
+ print("We got all of the pieces!")
+ end
+end)
+-- Lets push the jobs now
+for i=1,10 do -- Job Name of registered function, ... varargs
+ jQueue:pushJob("TEST_JOB","This is a test!",math.random(1,1000000))
+end
+print("I pushed all of the jobs :)")
+multi:mainloop() -- Start the main loop :D
+```
+
+Thats it from this version!
+
+Update: 1.8.3
+-------------
+Added:
+**New Mainloop functions** Below you can see the slight differences... Function overhead is not too bad in lua, but has a real difference. multi:mainloop() and multi:unprotectedMainloop() use the same algorithm yet the dedicated unprotected one is slightly faster due to having less function overhead.
+- multi:mainloop()\* -- Bench: 16830003 Steps in 3 second(s)!
+- multi:protectedMainloop() -- Bench: 16699308 Steps in 3 second(s)!
+- multi:unprotectedMainloop() -- Bench: 16976627 Steps in 3 second(s)!
+- multi:prioritizedMainloop1() -- Bench: 15007133 Steps in 3 second(s)!
+- multi:prioritizedMainloop2() -- Bench: 15526248 Steps in 3 second(s)!
+
+\* The OG mainloop function remains the same and old methods to achieve what we have with the new ones still exist
+
+These new methods help by removing function overhead that is caused through the original mainloop function. The one downside is that you no longer have the flexiblity to change the processing during runtime.
+
+However there is a work around! You can use processes to run multiobjs as well and use the other methods on them.
+
+I may make a full comparison between each method and which is faster, but for now trust that the dedicated ones with less function overhead are infact faster. Not by much but still faster. :D
+
+Update: 1.8.2
+-------------
+Added:
+- multi:newsystemThreadedTable(name) NOTE: Metatables are not supported in transfers. However there is a work around obj:init() that you see does this. Take a look in the multi/integration/shared/shared.lua files to see how I did it!
+- Modified the GLOBAL metatable to sync before doing its tests
+- multi._VERSION was multi.Version, felt it would be more consistant this way... I left the old way of getting the version just incase someone has used that way. It will eventually be gone. Also multi:getVersion() will do the job just as well and keep your code nice and update related bug free!
+- Also everything that is included in the: multi/integration/shared/shared.lua (Which is loaded automatically) works in both lanes and love2d enviroments!
+
+The threaded table is setup just like the threaded queue.
+It provids GLOBAL like features without having to write to GLOBAL!
+This is useful for module creators who want to keep their data private, but also use GLOBAL like coding.
+It has a few features that makes it a bit better than plain ol GLOBAL (For now...)
+(ThreadedTable - TT for short)
+- TT:waitFor(name)
+- TT:sync()
+- TT["var"]=value
+- print(TT["var"])
+
+we also have the "sync" method, this one was made for love2d because we do a syncing trick to get data in a table format. The lanes side has a sync method as well so no worries. Using indexing calls sync once and may grab your variable. This allows you to have the lanes indexing 'like' syntax when doing regular indexing in love2d side of the module. As of right now both sides work flawlessly! And this effect is now the GLOBAL as well
+
+On GLOBALS sync is a internal method for keeping the GLOBAL table in order. You can still use sThread.waitFor(name) to wait for variables that may of may not yet exist!
+
+Time for some examples:
+# Using multi:newSystemThreadedTable(name)
+```lua
+-- lanes Desktop lua! NOTE: this is in lanesintergratetest6.lua in the examples folder
+local GLOBAL,sThread=require("multi.integration.lanesManager").init()
+test=multi:newSystemThreadedTable("YO"):init()
+test["test1"]="lol"
+multi:newSystemThread("test",function()
+ tab=sThread.waitFor("YO"):init()
+ print(tab:has("test1"))
+ sThread.sleep(3)
+ tab["test2"]="Whats so funny?"
+end)
+multi:newThread("test2",function()
+ print(test:waitFor("test2"))
+end)
+multi:mainloop()
+```
+
+```lua
+-- love2d gaming lua! NOTE: this is in main4.lua in the love2d examples
+require("core.Library")
+GLOBAL,sThread=require("multi.integration.loveManager").init() -- load the love2d version of the lanesManager and requires the entire multi library
+require("core.GuiManager")
+gui.ff.Color=Color.Black
+test=multi:newSystemThreadedTable("YO"):init()
+test["test1"]="lol"
+multi:newSystemThread("test",function()
+ tab=sThread.waitFor("YO"):init()
+ print(tab["test1"])
+ sThread.sleep(3)
+ tab["test2"]="Whats so funny?"
+end)
+multi:newThread("test2",function()
+ print(test:waitFor("test2"))
+ t.text="DONE!"
+end)
+t=gui:newTextLabel("no done yet!",0,0,300,100)
+t:centerX()
+t:centerY()
+```
+
+Update: 1.8.1
+-------------
+No real change!
+Changed the structure of the library. Combined the coroutine based threads into the core!
+Only compat and integrations are not part of the core and never will be by nature.
+This should make the library more convient to use.
+I left multi/all.lua file so if anyone had libraries/projects that used that it will still work!
+Updated from 1.7.6 to 1.8.0 (How much thread could a thread thread if a thread could thread thread?)
+Added:
+- multi:newSystemThreadedQueue()
+- multi:systemThreadedBenchmark()
+- More example files
+- multi:canSystemThread() -- true if an integration was added false otherwise (For module creation)
+- Fixed a few bugs in the loveManager
+
+# Using multi:systemThreadedBenchmark()
+```lua
+package.path="?/init.lua;"..package.path
+local GLOBAL,sThread=require("multi.integration.lanesManager").init()
+multi:systemThreadedBenchmark(3):OnBench(function(self,count)
+ print("First Bench: "..count)
+ multi:systemThreadedBenchmark(3,"All Threads: ")
+end)
+multi:mainloop()
+```
+
+# Using multi:newSystemThreadedQueue()
+Quick Note: queues shared across multiple objects will be pulling from the same "queue" keep this in mind when coding! ~~Also the queue respects direction a push on the thread side cannot be popped on the thread side... Same goes for the mainthread!~~ Turns out i was wrong about this...
+```lua
+-- in love2d, this file will be in the same example folder as before, but is named main2.lua
+require("core.Library")
+GLOBAL,sThread=require("multi.integration.loveManager").init() -- load the love2d version of the lanesManager and requires the entire multi library
+--IMPORTANT
+-- Do not make the above local, this is the one difference that the lanesManager does not have
+-- If these are local the functions will have the upvalues put into them that do not exist on the threaded side
+-- You will need to ensure that the function does not refer to any upvalues in its code. It will print an error if it does though
+-- Also each thread has a .1 second delay! This is used to generate a random values for each thread!
+require("core.GuiManager")
+gui.ff.Color=Color.Black
+function multi:newSystemThreadedQueue(name) -- in love2d this will spawn a channel on both ends
+ local c={}
+ c.name=name
+ if love then
+ if love.thread then
+ function c:init()
+ self.chan=love.thread.getChannel(self.name)
+ function self:push(v)
+ self.chan:push(v)
+ end
+ function self:pop()
+ return self.chan:pop()
+ end
+ GLOBAL[self.name]=self
+ return self
+ end
+ return c
+ else
+ error("Make sure you required the love.thread module!")
+ end
+ else
+ c.linda=lanes.linda()
+ function c:push(v)
+ self.linda:send("Q",v)
+ end
+ function c:pop()
+ return ({self.linda:receive(0,"Q")})[2]
+ end
+ function c:init()
+ return self
+ end
+ GLOBAL[name]=c
+ end
+ return c
+end
+queue=multi:newSystemThreadedQueue("QUEUE"):init()
+queue:push("This is a test")
+queue:push("This is a test2")
+queue:push("This is a test3")
+queue:push("This is a test4")
+multi:newSystemThread("test2",function()
+ queue=sThread.waitFor("QUEUE"):init()
+ data=queue:pop()
+ while data do
+ print(data)
+ data=queue:pop()
+ end
+ queue:push("DONE!")
+end)
+multi:newThread("test!",function()
+ thread.hold(function() return queue:pop() end)
+ t.text="Done!"
+end)
+t=gui:newTextLabel("no done yet!",0,0,300,100)
+t:centerX()
+t:centerY()
+```
+# In Lanes
+```lua
+-- The code is compatible with each other, I just wanted to show different things you can do in both examples
+-- This file can be found in the examples folder as lanesintegrationtest4.lua
+local GLOBAL,sThread=require("multi.integration.lanesManager").init()
+queue=multi:newSystemThreadedQueue("QUEUE"):init()
+queue:push("This is a test")
+queue:push("This is a test2")
+queue:push("This is a test3")
+queue:push("This is a test4")
+multi:newSystemThread("test2",function()
+ queue=sThread.waitFor("QUEUE"):init()
+ data=queue:pop()
+ while data do
+ print(data)
+ data=queue:pop()
+ end
+ queue:push("This is a test5")
+ queue:push("This is a test6")
+ queue:push("This is a test7")
+ queue:push("This is a test8")
+end)
+multi:newThread("test!",function() -- this is a lua thread
+ thread.sleep(.1)
+ data=queue:pop()
+ while data do
+ print(data)
+ data=queue:pop()
+ end
+end)
+multi:mainloop()
+```
+Update: 1.7.6
+-------------
+Fixed:
+Typos like always
+Added:
+multi:getPlatform() -- returns "love2d" if using the love2d platform or returns "lanes" if using lanes for threading
+examples files
+In Events added method setTask(func)
+The old way still works and is more convient to be honest, but I felt a method to do this was ok.
+
+Updated:
+some example files to reflect changes to the core. Changes allow for less typing
+loveManager to require the compat if used so you don't need 2 require line to retrieve the library
+
+Update: 1.7.5
+-------------
+Fixed some typos in the readme... (I am sure there are more there are always more)
+Added more features for module support
+TODO:
+Work on performance of the library... I see 3 places where I can make this thing run quicker
+
+I'll show case some old versions of the multitasking library eventually so you can see its changes in days past!
+
+Update: 1.7.4
+-------------
+Added: the example folder which will be populated with more examples in the near future!
+The loveManager integration that mimics the lanesManager integration almost exactly to keep coding in both enviroments as close to possible. This is done mostly for library creation support!
+An example of the loveManager in action using almost the same code as the lanesintergreationtest2.lua
+NOTE: This code has only been tested to work on love2d version 1.10.2 thoough it should work version 0.9.0
+```lua
+require("core.Library") -- Didn't add this to a repo yet! Will do eventually... Allows for injections and other cool things
+require("multi.compat.love2d") -- allows for multitasking and binds my libraies to the love2d engine that i am using
+GLOBAL,sThread=require("multi.integration.loveManager").init() -- load the love2d version of the lanesManager
+--IMPORTANT
+-- Do not make the above local, this is the one difference that the lanesManager does not have
+-- If these are local the functions will have the upvalues put into them that do not exist on the threaded side
+-- You will need to ensure that the function does not refer to any upvalues in its code. It will print an error if it does though
+-- Also each thread has a .1 second delay! This is used to generate a random values for each thread!
+require("core.GuiManager") -- allows the use of graphics in the program.
+gui.ff.Color=Color.Black
+function comma_value(amount)
+ local formatted = amount
+ while true do
+ formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
+ if (k==0) then
+ break
+ end
+ end
+ return formatted
+end
+multi:newSystemThread("test1",function() -- Another difference is that the multi library is already loaded in the threaded enviroment as well as a call to multi:mainloop()
+ multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 1"):OnBench(function(self,c) GLOBAL["T1"]=c multi:Stop() end)
+end)
+multi:newSystemThread("test2",function() -- spawns a thread in another lua process
+ multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 2"):OnBench(function(self,c) GLOBAL["T2"]=c multi:Stop() end)
+end)
+multi:newSystemThread("test3",function() -- spawns a thread in another lua process
+ multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 3"):OnBench(function(self,c) GLOBAL["T3"]=c multi:Stop() end)
+end)
+multi:newSystemThread("test4",function() -- spawns a thread in another lua process
+ multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 4"):OnBench(function(self,c) GLOBAL["T4"]=c multi:Stop() end)
+end)
+multi:newSystemThread("test5",function() -- spawns a thread in another lua process
+ multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 5"):OnBench(function(self,c) GLOBAL["T5"]=c multi:Stop() end)
+end)
+multi:newSystemThread("test6",function() -- spawns a thread in another lua process
+ multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 6"):OnBench(function(self,c) GLOBAL["T6"]=c multi:Stop() end)
+end)
+multi:newSystemThread("Combiner",function() -- spawns a thread in another lua process
+ function comma_value(amount)
+ local formatted = amount
+ while true do
+ formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
+ if (k==0) then
+ break
+ end
+ end
+ return formatted
+ end
+ local b=comma_value(tostring(sThread.waitFor("T1")+sThread.waitFor("T2")+sThread.waitFor("T3")+sThread.waitFor("T4")+sThread.waitFor("T5")+sThread.waitFor("T6")))
+ GLOBAL["DONE"]=b
+end)
+multi:newThread("test0",function()
+ -- sThread.waitFor("DONE") -- lets hold the main thread completely so we don't eat up cpu
+ -- os.exit()
+ -- when the main thread is holding there is a chance that error handling on the system threads may not work!
+ -- instead we can do this
+ while true do
+ thread.skip(1) -- allow error handling to take place... Otherwise lets keep the main thread running on the low
+ -- Before we held just because we could... But this is a game and we need to have logic continue
+ --sThreadM.sleep(.001) -- Sleeping for .001 is a greeat way to keep cpu usage down. Make sure if you aren't doing work to rest. Abuse the hell out of GLOBAL if you need to :P
+ if GLOBAL["DONE"] then
+ t.text="Bench: "..GLOBAL["DONE"]
+ end
+ end
+end)
+GLOBAL["Bench"]=3
+t=gui:newTextLabel("no done yet!",0,0,300,100)
+t:centerX()
+t:centerY()
+```
+Update: 1.7.3
+-------------
+Changed how requiring the library works!
+`require("multi.all")` Will still work as expected; however, with the exception of threading, compat, and integrations everything else has been moved into the core of the library.
+```lua
+-- This means that these are no longer required and will cause an error if done so
+require("multi.loop")
+require("multi.alarm")
+require("multi.updater")
+require("multi.tloop")
+require("multi.watcher")
+require("multi.tstep")
+require("multi.step")
+require("multi.task")
+-- ^ they are all part of the core now
+```
+
+Update: 1.7.2
+-------------
+Moved updaters, loops, and alarms into the init.lua file. I consider them core features and they are referenced in the init.lua file so they need to exist there. Threaded versions are still separate though. Added another example file
+
+Update: 1.7.1 Bug Fixes Only
+-------------
+
+Update: 1.7.0
+-------------
+Modified: multi.integration.lanesManager.lua
+It is now in a stable and simple state Works with the latest lanes version! Tested with version 3.11 I cannot promise that everything will work with eariler versions. Future versions are good though.
+Example Usage:
+sThread is a handle to a global interface for system threads to interact with themself
+thread is the interface for multithreads as seen in the threading section
+
+GLOBAL a table that can be used throughout each and every thread
+
+sThreads have a few methods
+sThread.set(name,val) -- you can use the GLOBAL table instead modifies the same table anyway
+sThread.get(name) -- you can use the GLOBAL table instead modifies the same table anyway
+sThread.waitFor(name) -- waits until a value exists, if it does it returns it
+sThread.getCores() -- returns the number of cores on your cpu
+sThread.sleep(n) -- sleeps for a bit stopping the entire thread from running
+sThread.hold(n) -- sleeps until a condition is met
+```lua
+local GLOBAL,sThread=require("multi.integration.lanesManager").init()
+require("multi.all")
+multi:newAlarm(2):OnRing(function(self)
+ GLOBAL["NumOfCores"]=sThread.getCores()
+end)
+multi:newAlarm(7):OnRing(function(self)
+ GLOBAL["AnotherTest"]=true
+end)
+multi:newAlarm(13):OnRing(function(self)
+ GLOBAL["FinalTest"]=true
+end)
+multi:newSystemThread("test",function() -- spawns a thread in another lua process
+ require("multi.all") -- now you can do all of your coding with the multi library! You could even spawn more threads from here with the integration. You would need to require the interaction again though
+ print("Waiting for variable: NumOfCores")
+ print("Got it: ",sThread.waitFor("NumOfCores"))
+ sThread.hold(function()
+ return GLOBAL["AnotherTest"] -- note this would hold the entire systemthread. Spawn a coroutine thread using multi:newThread() or multi:newThreaded...
+ end)
+ print("Holding works!")
+ multi:newThread("tests",function()
+ thread.hold(function()
+ return GLOBAL["FinalTest"] -- note this will not hold the entire systemthread. As seen with the TLoop constantly going!
+ end)
+ print("Final test works!")
+ os.exit()
+ end)
+ local a=0
+ multi:newTLoop(function()
+ a=a+1
+ print(a)
+ end,.5)
+ multi:mainloop()
+end)
+multi:mainloop()
+```
+
+Update: 1.6.0
+-------------
+Changed: steps and loops
+```lua
+-- Was
+step:OnStep(function(pos,self) -- same goes for tsteps as well
+ print(pos)
+end)
+multi:newLoop(function(dt,self)
+ print(dt)
+end)
+-- Is now
+step:OnStep(function(self,pos) -- same goes for tsteps as well
+ print(pos)
+end)
+multi:newLoop(function(self,dt)
+ print(dt)
+end)
+```
+Reasoning I wanted to keep objects consistant, but a lot of my older libraries use the old way of doing things. Therefore I added a backwards module
+```lua
+require("multi.all")
+require("multi.compat.backwards[1,5,0]") -- allows for the use of features that were scrapped/changed in 1.6.0+
+```
+Update: 1.5.0
+-------------
+Added:
+- An easy way to manage timeouts
+- Small bug fixes
+
+Update: 1.4.1 - First Public release of the library
+-------------
+
+**IMPORTANT:**
+Every update I make aims to make things simpler more efficent and just better, but a lot of old code, which can be really big, uses a lot of older features. I know the pain of having to rewrite everything. My promise to my library users is that I will always have backwards support for older features! New ways may exist that are quicker and eaiser, but the old features/methods will be supported.
diff --git a/examples/love2d Threading Example/bin/compressors/lzw.lua b/examples/love2d Threading Example/bin/compressors/lzw.lua
new file mode 100644
index 0000000..57653b3
--- /dev/null
+++ b/examples/love2d Threading Example/bin/compressors/lzw.lua
@@ -0,0 +1,73 @@
+--[[
+LZW String Compression demo for Gideros
+This code is MIT licensed, see http://www.opensource.org/licenses/mit-license.php
+(C) 2013 - Guava7
+]]
+CLZWCompression = {}
+function CLZWCompression:InitDictionary(isEncode)
+ self.mDictionary = {}
+ -- local s = " !#$%&'\"()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
+ local s={}
+ for i=1,255 do
+ s[#s+1]=string.char(i)
+ end
+ s=table.concat(s)
+ local len = #s
+ for i = 1, len do
+ if isEncode then
+ self.mDictionary[s:sub(i, i)] = i
+ else
+ self.mDictionary[i] = s:sub(i, i)
+ end
+ end
+ self.mDictionaryLen = len
+end
+function CLZWCompression:Encode(sInput)
+ self:InitDictionary(true)
+ local s = ""
+ local ch
+ local len = #sInput
+ local result = {}
+ local dic = self.mDictionary
+ local temp
+ for i = 1, len do
+ ch = sInput:sub(i, i)
+ temp = s..ch
+ if dic[temp] then
+ s = temp
+ else
+ result[#result + 1] = dic[s]
+ self.mDictionaryLen = self.mDictionaryLen + 1
+ dic[temp] = self.mDictionaryLen
+ s = ch
+ end
+ end
+ result[#result + 1] = dic[s]
+ return result
+end
+function CLZWCompression:Decode(data)
+ self:InitDictionary(false)
+ local dic = self.mDictionary
+ local entry
+ local ch
+ local prevCode, currCode
+ local result = {}
+ prevCode = data[1]
+ result[#result + 1] = dic[prevCode]
+ for i = 2, #data do
+ currCode = data[i]
+ entry = dic[currCode]
+ if entry then
+ ch = entry:sub(1, 1)
+ result[#result + 1] = entry
+ else
+ ch = dic[prevCode]:sub(1, 1)
+ result[#result + 1] = dic[prevCode]..ch
+ end
+ dic[#dic + 1] = dic[prevCode]..ch
+ prevCode = currCode
+ end
+ return table.concat(result)
+end
+
+return CLZWCompression
diff --git a/examples/love2d Threading Example/bin/converters/base64.lua b/examples/love2d Threading Example/bin/converters/base64.lua
new file mode 100644
index 0000000..6b134c3
--- /dev/null
+++ b/examples/love2d Threading Example/bin/converters/base64.lua
@@ -0,0 +1,30 @@
+local base64={}
+local bs = { [0] =
+ 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
+ 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
+ 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
+ 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/',
+}
+local bsd=table.flip(bs)
+local char=string.char
+function base64.encode(s)
+ local byte, rep, pad = string.byte, string.rep, 2 - ((#s-1) % 3)
+ s = (s..rep('\0', pad)):gsub("...", function(cs)
+ local a, b, c = byte(cs, 1, 3)
+ return bs[bit.rshift(a,2)] .. bs[bit.bor(bit.lshift(bit.band(a,3),4),bit.rshift(b,4))] .. bs[bit.bor(bit.lshift(bit.band(b,15),2),bit.rshift(c,6))] .. bs[bit.band(c,63)]
+ end)
+ return s:sub(1, #s-pad) .. rep('=', pad)
+end
+function base64.decode(s)
+ local s=s:match("["..s.."=]+")
+ local p,cc=s:gsub("=","A")
+ local r=""
+ local n=0
+ s=s:sub(1,#s-#p)..p
+ for c = 1,#s,4 do
+ n = bit.lshift(bsd[s:sub(c, c)], 18) + bit.lshift(bsd[s:sub(c+1, c+1)], 12) + bit.lshift(bsd[s:sub(c + 2, c + 2)], 6) + bsd[s:sub(c + 3, c + 3)]
+ r = r .. char(bit.band(bit.arshift(n, 16), 0xFF)) .. char(bit.band(bit.arshift(n, 8), 0xFF)) .. char(bit.band(n, 0xFF))
+ end
+ return r:sub(1,-(cc+1))
+end
+return base64
diff --git a/examples/love2d Threading Example/bin/converters/base91.lua b/examples/love2d Threading Example/bin/converters/base91.lua
new file mode 100644
index 0000000..157f312
--- /dev/null
+++ b/examples/love2d Threading Example/bin/converters/base91.lua
@@ -0,0 +1,72 @@
+local base91={}
+local b91enc={[0]=
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
+ 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
+ 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '!', '#', '$',
+ '%', '&', '(', ')', '*', '+', ',', '.', '/', ':', ';', '<', '=',
+ '>', '?', '@', '[', ']', '^', '_', '`', '{', '|', '}', '~', '"'
+}
+local b91dec=table.flip(b91enc)
+function base91.decode(d)
+ local l,v,o,b,n = #d,-1,"",0,0
+ for i in d:gmatch(".") do
+ local c=b91dec[i]
+ if not(c) then
+ -- Continue
+ else
+ if v < 0 then
+ v = c
+ else
+ v = v+c*91
+ b = bit.bor(b, bit.lshift(v,n))
+ if bit.band(v,8191) then
+ n = n + 13
+ else
+ n = n + 14
+ end
+ while true do
+ o=o..string.char(bit.band(b,255))
+ b=bit.rshift(b,8)
+ n=n-8
+ if not (n>7) then
+ break
+ end
+ end
+ v=-1
+ end
+ end
+ end
+ if v + 1>0 then
+ o=o..string.char(bit.band(bit.bor(b,bit.lshift(v,n)),255))
+ end
+ return o
+end
+function base91.encode(d)
+ local b,n,o,l=0,0,"",#d
+ for i in d:gmatch(".") do
+ b=bit.bor(b,bit.lshift(string.byte(i),n))
+ n=n+8
+ if n>13 then
+ v=bit.band(b,8191)
+ if v>88 then
+ b=bit.rshift(b,13)
+ n=n-13
+ else
+ v=bit.band(b,16383)
+ b=bit.rshift(b,14)
+ n=n-14
+ end
+ o=o..b91enc[v % 91] .. b91enc[math.floor(v / 91)]
+ end
+ end
+ if n>0 then
+ o=o..b91enc[b % 91]
+ if n>7 or b>90 then
+ o=o .. b91enc[math.floor(b / 91)]
+ end
+ end
+ return o
+end
+return base91
diff --git a/examples/love2d Threading Example/bin/hashes/md5.lua b/examples/love2d Threading Example/bin/hashes/md5.lua
new file mode 100644
index 0000000..7ffa55a
--- /dev/null
+++ b/examples/love2d Threading Example/bin/hashes/md5.lua
@@ -0,0 +1,377 @@
+local md5 = {
+ _VERSION = "md5.lua 1.1.0",
+ _DESCRIPTION = "MD5 computation in Lua (5.1-3, LuaJIT)",
+ _URL = "https://github.com/kikito/md5.lua",
+ _LICENSE = [[
+ MIT LICENSE
+
+ Copyright (c) 2013 Enrique García Cota + Adam Baldwin + hanzao + Equi 4 Software
+ ]]
+}
+
+-- bit lib implementions
+
+local char, byte, format, rep, sub =
+ string.char, string.byte, string.format, string.rep, string.sub
+local bit_or, bit_and, bit_not, bit_xor, bit_rshift, bit_lshift
+
+local ok, bit = pcall(require, 'bit')
+if ok then
+ bit_or, bit_and, bit_not, bit_xor, bit_rshift, bit_lshift = bit.bor, bit.band, bit.bnot, bit.bxor, bit.rshift, bit.lshift
+else
+ ok, bit = pcall(require, 'bit32')
+
+ if ok then
+
+ bit_not = bit.bnot
+
+ local tobit = function(n)
+ return n <= 0x7fffffff and n or -(bit_not(n) + 1)
+ end
+
+ local normalize = function(f)
+ return function(a,b) return tobit(f(tobit(a), tobit(b))) end
+ end
+
+ bit_or, bit_and, bit_xor = normalize(bit.bor), normalize(bit.band), normalize(bit.bxor)
+ bit_rshift, bit_lshift = normalize(bit.rshift), normalize(bit.lshift)
+
+ else
+
+ local function tbl2number(tbl)
+ local result = 0
+ local power = 1
+ for i = 1, #tbl do
+ result = result + tbl[i] * power
+ power = power * 2
+ end
+ return result
+ end
+
+ local function expand(t1, t2)
+ local big, small = t1, t2
+ if(#big < #small) then
+ big, small = small, big
+ end
+ -- expand small
+ for i = #small + 1, #big do
+ small[i] = 0
+ end
+ end
+
+ local to_bits -- needs to be declared before bit_not
+
+ bit_not = function(n)
+ local tbl = to_bits(n)
+ local size = math.max(#tbl, 32)
+ for i = 1, size do
+ if(tbl[i] == 1) then
+ tbl[i] = 0
+ else
+ tbl[i] = 1
+ end
+ end
+ return tbl2number(tbl)
+ end
+
+ -- defined as local above
+ to_bits = function (n)
+ if(n < 0) then
+ -- negative
+ return to_bits(bit_not(math.abs(n)) + 1)
+ end
+ -- to bits table
+ local tbl = {}
+ local cnt = 1
+ local last
+ while n > 0 do
+ last = n % 2
+ tbl[cnt] = last
+ n = (n-last)/2
+ cnt = cnt + 1
+ end
+
+ return tbl
+ end
+
+ bit_or = function(m, n)
+ local tbl_m = to_bits(m)
+ local tbl_n = to_bits(n)
+ expand(tbl_m, tbl_n)
+
+ local tbl = {}
+ for i = 1, #tbl_m do
+ if(tbl_m[i]== 0 and tbl_n[i] == 0) then
+ tbl[i] = 0
+ else
+ tbl[i] = 1
+ end
+ end
+
+ return tbl2number(tbl)
+ end
+
+ bit_and = function(m, n)
+ local tbl_m = to_bits(m)
+ local tbl_n = to_bits(n)
+ expand(tbl_m, tbl_n)
+
+ local tbl = {}
+ for i = 1, #tbl_m do
+ if(tbl_m[i]== 0 or tbl_n[i] == 0) then
+ tbl[i] = 0
+ else
+ tbl[i] = 1
+ end
+ end
+
+ return tbl2number(tbl)
+ end
+
+ bit_xor = function(m, n)
+ local tbl_m = to_bits(m)
+ local tbl_n = to_bits(n)
+ expand(tbl_m, tbl_n)
+
+ local tbl = {}
+ for i = 1, #tbl_m do
+ if(tbl_m[i] ~= tbl_n[i]) then
+ tbl[i] = 1
+ else
+ tbl[i] = 0
+ end
+ end
+
+ return tbl2number(tbl)
+ end
+
+ bit_rshift = function(n, bits)
+ local high_bit = 0
+ if(n < 0) then
+ -- negative
+ n = bit_not(math.abs(n)) + 1
+ high_bit = 0x80000000
+ end
+
+ local floor = math.floor
+
+ for i=1, bits do
+ n = n/2
+ n = bit_or(floor(n), high_bit)
+ end
+ return floor(n)
+ end
+
+ bit_lshift = function(n, bits)
+ if(n < 0) then
+ -- negative
+ n = bit_not(math.abs(n)) + 1
+ end
+
+ for i=1, bits do
+ n = n*2
+ end
+ return bit_and(n, 0xFFFFFFFF)
+ end
+ end
+end
+
+-- convert little-endian 32-bit int to a 4-char string
+local function lei2str(i)
+ local f=function (s) return char( bit_and( bit_rshift(i, s), 255)) end
+ return f(0)..f(8)..f(16)..f(24)
+end
+
+-- convert raw string to big-endian int
+local function str2bei(s)
+ local v=0
+ for i=1, #s do
+ v = v * 256 + byte(s, i)
+ end
+ return v
+end
+
+-- convert raw string to little-endian int
+local function str2lei(s)
+ local v=0
+ for i = #s,1,-1 do
+ v = v*256 + byte(s, i)
+ end
+ return v
+end
+
+-- cut up a string in little-endian ints of given size
+local function cut_le_str(s,...)
+ local o, r = 1, {}
+ local args = {...}
+ for i=1, #args do
+ table.insert(r, str2lei(sub(s, o, o + args[i] - 1)))
+ o = o + args[i]
+ end
+ return r
+end
+
+local swap = function (w) return str2bei(lei2str(w)) end
+
+-- An MD5 mplementation in Lua, requires bitlib (hacked to use LuaBit from above, ugh)
+-- 10/02/2001 jcw@equi4.com
+
+local CONSTS = {
+ 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
+ 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
+ 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
+ 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
+ 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
+ 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
+ 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
+ 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
+ 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
+ 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
+ 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
+ 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
+ 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
+ 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
+ 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
+ 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391,
+ 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476
+}
+
+local f=function (x,y,z) return bit_or(bit_and(x,y),bit_and(-x-1,z)) end
+local g=function (x,y,z) return bit_or(bit_and(x,z),bit_and(y,-z-1)) end
+local h=function (x,y,z) return bit_xor(x,bit_xor(y,z)) end
+local i=function (x,y,z) return bit_xor(y,bit_or(x,-z-1)) end
+local z=function (ff,a,b,c,d,x,s,ac)
+ a=bit_and(a+ff(b,c,d)+x+ac,0xFFFFFFFF)
+ -- be *very* careful that left shift does not cause rounding!
+ return bit_or(bit_lshift(bit_and(a,bit_rshift(0xFFFFFFFF,s)),s),bit_rshift(a,32-s))+b
+end
+
+local function transform(A,B,C,D,X)
+ local a,b,c,d=A,B,C,D
+ local t=CONSTS
+
+ a=z(f,a,b,c,d,X[ 0], 7,t[ 1])
+ d=z(f,d,a,b,c,X[ 1],12,t[ 2])
+ c=z(f,c,d,a,b,X[ 2],17,t[ 3])
+ b=z(f,b,c,d,a,X[ 3],22,t[ 4])
+ a=z(f,a,b,c,d,X[ 4], 7,t[ 5])
+ d=z(f,d,a,b,c,X[ 5],12,t[ 6])
+ c=z(f,c,d,a,b,X[ 6],17,t[ 7])
+ b=z(f,b,c,d,a,X[ 7],22,t[ 8])
+ a=z(f,a,b,c,d,X[ 8], 7,t[ 9])
+ d=z(f,d,a,b,c,X[ 9],12,t[10])
+ c=z(f,c,d,a,b,X[10],17,t[11])
+ b=z(f,b,c,d,a,X[11],22,t[12])
+ a=z(f,a,b,c,d,X[12], 7,t[13])
+ d=z(f,d,a,b,c,X[13],12,t[14])
+ c=z(f,c,d,a,b,X[14],17,t[15])
+ b=z(f,b,c,d,a,X[15],22,t[16])
+
+ a=z(g,a,b,c,d,X[ 1], 5,t[17])
+ d=z(g,d,a,b,c,X[ 6], 9,t[18])
+ c=z(g,c,d,a,b,X[11],14,t[19])
+ b=z(g,b,c,d,a,X[ 0],20,t[20])
+ a=z(g,a,b,c,d,X[ 5], 5,t[21])
+ d=z(g,d,a,b,c,X[10], 9,t[22])
+ c=z(g,c,d,a,b,X[15],14,t[23])
+ b=z(g,b,c,d,a,X[ 4],20,t[24])
+ a=z(g,a,b,c,d,X[ 9], 5,t[25])
+ d=z(g,d,a,b,c,X[14], 9,t[26])
+ c=z(g,c,d,a,b,X[ 3],14,t[27])
+ b=z(g,b,c,d,a,X[ 8],20,t[28])
+ a=z(g,a,b,c,d,X[13], 5,t[29])
+ d=z(g,d,a,b,c,X[ 2], 9,t[30])
+ c=z(g,c,d,a,b,X[ 7],14,t[31])
+ b=z(g,b,c,d,a,X[12],20,t[32])
+
+ a=z(h,a,b,c,d,X[ 5], 4,t[33])
+ d=z(h,d,a,b,c,X[ 8],11,t[34])
+ c=z(h,c,d,a,b,X[11],16,t[35])
+ b=z(h,b,c,d,a,X[14],23,t[36])
+ a=z(h,a,b,c,d,X[ 1], 4,t[37])
+ d=z(h,d,a,b,c,X[ 4],11,t[38])
+ c=z(h,c,d,a,b,X[ 7],16,t[39])
+ b=z(h,b,c,d,a,X[10],23,t[40])
+ a=z(h,a,b,c,d,X[13], 4,t[41])
+ d=z(h,d,a,b,c,X[ 0],11,t[42])
+ c=z(h,c,d,a,b,X[ 3],16,t[43])
+ b=z(h,b,c,d,a,X[ 6],23,t[44])
+ a=z(h,a,b,c,d,X[ 9], 4,t[45])
+ d=z(h,d,a,b,c,X[12],11,t[46])
+ c=z(h,c,d,a,b,X[15],16,t[47])
+ b=z(h,b,c,d,a,X[ 2],23,t[48])
+
+ a=z(i,a,b,c,d,X[ 0], 6,t[49])
+ d=z(i,d,a,b,c,X[ 7],10,t[50])
+ c=z(i,c,d,a,b,X[14],15,t[51])
+ b=z(i,b,c,d,a,X[ 5],21,t[52])
+ a=z(i,a,b,c,d,X[12], 6,t[53])
+ d=z(i,d,a,b,c,X[ 3],10,t[54])
+ c=z(i,c,d,a,b,X[10],15,t[55])
+ b=z(i,b,c,d,a,X[ 1],21,t[56])
+ a=z(i,a,b,c,d,X[ 8], 6,t[57])
+ d=z(i,d,a,b,c,X[15],10,t[58])
+ c=z(i,c,d,a,b,X[ 6],15,t[59])
+ b=z(i,b,c,d,a,X[13],21,t[60])
+ a=z(i,a,b,c,d,X[ 4], 6,t[61])
+ d=z(i,d,a,b,c,X[11],10,t[62])
+ c=z(i,c,d,a,b,X[ 2],15,t[63])
+ b=z(i,b,c,d,a,X[ 9],21,t[64])
+
+ return bit_and(A+a,0xFFFFFFFF),bit_and(B+b,0xFFFFFFFF),
+ bit_and(C+c,0xFFFFFFFF),bit_and(D+d,0xFFFFFFFF)
+end
+
+----------------------------------------------------------------
+
+local function md5_update(self, s)
+ self.pos = self.pos + #s
+ s = self.buf .. s
+ for ii = 1, #s - 63, 64 do
+ local X = cut_le_str(sub(s,ii,ii+63),4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4)
+ assert(#X == 16)
+ X[0] = table.remove(X,1) -- zero based!
+ self.a,self.b,self.c,self.d = transform(self.a,self.b,self.c,self.d,X)
+ end
+ self.buf = sub(s, math.floor(#s/64)*64 + 1, #s)
+ return self
+end
+
+local function md5_finish(self)
+ local msgLen = self.pos
+ local padLen = 56 - msgLen % 64
+
+ if msgLen % 64 > 56 then padLen = padLen + 64 end
+
+ if padLen == 0 then padLen = 64 end
+
+ local s = char(128) .. rep(char(0),padLen-1) .. lei2str(bit_and(8*msgLen, 0xFFFFFFFF)) .. lei2str(math.floor(msgLen/0x20000000))
+ md5_update(self, s)
+
+ assert(self.pos % 64 == 0)
+ return lei2str(self.a) .. lei2str(self.b) .. lei2str(self.c) .. lei2str(self.d)
+end
+
+----------------------------------------------------------------
+
+function md5.new()
+ return { a = CONSTS[65], b = CONSTS[66], c = CONSTS[67], d = CONSTS[68],
+ pos = 0,
+ buf = '',
+ update = md5_update,
+ finish = md5_finish }
+end
+
+function md5.tohex(s)
+ return format("%08x%08x%08x%08x", str2bei(sub(s, 1, 4)), str2bei(sub(s, 5, 8)), str2bei(sub(s, 9, 12)), str2bei(sub(s, 13, 16)))
+end
+
+function md5.sum(s)
+ return md5.new():update(s):finish()
+end
+
+function md5.sumhexa(s)
+ return md5.tohex(md5.sum(s))
+end
+
+return md5
diff --git a/examples/love2d Threading Example/bin/init.lua b/examples/love2d Threading Example/bin/init.lua
new file mode 100644
index 0000000..3728c6f
--- /dev/null
+++ b/examples/love2d Threading Example/bin/init.lua
@@ -0,0 +1,739 @@
+bin={}
+bin.Version={5,1,0}
+bin.stage='stable'
+bin.data=''
+bin.t='bin'
+bin.__index = bin
+bin.__tostring=function(self) return self:getData() end
+bin.__len=function(self) return self:getlength() end
+bin.lastBlockSize=0
+bin.streams={}
+-- Helpers
+function bin.getVersion()
+ return bin.Version[1]..'.'..bin.Version[2]..'.'..bin.Version[3]
+end
+require("bin.support.utils")
+if jit then
+ bit=require("bit")
+elseif bit32 then
+ bit=bit32
+else
+ bit=require("bin.numbers.no_jit_bit")
+end
+base64=require("bin.converters.base64")
+base91=require("bin.converters.base91")
+bin.lzw=require("bin.compressors.lzw") -- A WIP
+bits=require("bin.numbers.bits")
+infinabits=require("bin.numbers.infinabits") -- like the bits library but works past 32 bits for 32bit lua and 64 bits for 64 bit lua.
+bin.md5=require("bin.hashes.md5")
+randomGen=require("bin.numbers.random")
+function bin.setBitsInterface(int)
+ bin.defualtBit=int or bits
+end
+bin.setBitsInterface()
+function bin.normalizeData(data) -- unified function to allow for all types to string
+ if type(data)=="string" then return data end
+ if type(data)=="table" then
+ if data.Type=="bin" or data.Type=="streamable" or data.Type=="buffer" then
+ return data:getData()
+ elseif data.Type=="bits" or data.Type=="infinabits" then
+ return data:toSbytes()
+ elseif data.Type=="sink" then
+ -- LATER
+ else
+ return ""
+ end
+ elseif type(data)=="userdata" then
+ if tostring(data):sub(1,4)=="file" then
+ local cur=data:seek("cur")
+ data:seek("set",0)
+ local dat=data:read("*a")
+ data:seek("set",cur)
+ return dat
+ else
+ error("File handles are the only userdata that can be used!")
+ end
+ end
+end
+function bin.resolveType(tab) -- used in getblock for auto object creation. Internal method
+ if tab.Type then
+ if tab.Type=="bin" then
+ return bin.new(tab.data)
+ elseif tab.Type=="streamable" then
+ if bin.fileExist(tab.file) then return nil,"Cannot load the stream file, source file does not exist!" end
+ return bin.stream(tab.file,tab.lock)
+ elseif tab.Type=="buffer" then
+ local buf=bin.newDataBuffer(tab.size)
+ buf[1]=tab:getData()
+ return buf
+ elseif tab.Type=="bits" then
+ local b=bits.new("")
+ b.data=tab.data
+ return b
+ elseif tab.Type=="infinabits" then
+ local b=infinabits.new("")
+ b.data=tab.data
+ return b
+ elseif tab.Type=="sink" then
+ return bin.newSync(tab.data)
+ else -- maybe a type from another library
+ return tab
+ end
+ else return tab end
+end
+function bin.fileExist(path)
+ g=io.open(path or '','r')
+ if path =='' then
+ p='empty path'
+ return nil
+ end
+ if g~=nil and true or false then
+ p=(g~=nil and true or false)
+ end
+ if g~=nil then
+ io.close(g)
+ else
+ return false
+ end
+ return p
+end
+function bin.toHex(str)
+ local str=bin.normalizeData(str)
+ return (str:gsub('.', function (c)
+ return string.format('%02X', string.byte(c))
+ end))
+end
+function bin.fromHex(str)
+ return (str:gsub('..', function (cc)
+ return string.char(tonumber(cc, 16))
+ end))
+end
+function bin.toBase64(s)
+ return base64.encode(s)
+end
+function bin.fromBase64(s)
+ return base64.decode(s)
+end
+function bin.toBase91(s)
+ return base91.encode(s)
+end
+function bin.fromBase91(s)
+ return base91.decode(s)
+end
+-- Constructors
+function bin.new(data)
+ data=bin.normalizeData(data)
+ local c = {}
+ setmetatable(c, bin)
+ c.data=data
+ c.Type="bin"
+ c.t="bin"
+ c.pos=1
+ c.stream=false
+ return c
+end
+function bin.newFromBase64(data)
+ return bin.new(bin.fromBase64(data))
+end
+function bin.newFromBase91(data)
+ return bin.new(bin.fromBase91(data))
+end
+function bin.newFromHex(data)
+ return bin.new(bin.fromHex(data))
+end
+function bin.load(path)
+ if type(path) ~= "string" then error("Path must be a string!") end
+ local f = io.open(path, 'rb')
+ local content = f:read('*a')
+ f:close()
+ return bin.new(content)
+end
+function bin.stream(file,l)
+ if not(l==false) then l=true end
+ local c=bin.new()
+ c.Type="streamable"
+ c.t="streamable"
+ if bin.streams[file]~=nil then
+ c.file=file
+ c.lock = l
+ c.workingfile=bin.streams[file][1].workingfile
+ bin.streams[file][2]=bin.streams[file][2]+1
+ c.stream=true
+ return c
+ end
+ if bin.fileExist(file) then
+ c.file=file
+ c.lock = l
+ c.workingfile=io.open(file,'rb+')
+ else
+ c.file=file
+ c.lock = l
+ c.workingfile=io.open(file,'w')
+ io.close(c.workingfile)
+ c.workingfile=io.open(file,'rb+')
+ end
+ c.stream=true
+ bin.streams[file]={c,1}
+ return c
+end
+function bin.newTempFile()
+ local c=bin.new()
+ c.file=file
+ c.lock = false
+ c.workingfile=io.tmpfile()
+ c.stream=true
+ return c
+end
+function bin.freshStream(file)
+ bin.new():tofile(file)
+ return bin.stream(file,false)
+end
+function bin.newStreamFileObject(file)
+ local c=bin.new()
+ c.Type="streamable"
+ c.t="streamable"
+ c.file="FILE_OBJECT"
+ c.lock = false
+ c.workingfile=file
+ c.stream=true
+ return c
+end
+-- Core Methods
+function bin:canStreamWrite()
+ return (self.stream and not(self.lock))
+end
+function bin:getSeek()
+ if self.stream then
+ return self.workingfile:seek("cur")+1
+ else
+ return self.pos
+ end
+end
+function bin:setSeek(n)
+ if self.stream then
+ self.workingfile:seek("set",n-1)
+ else
+ self.pos=n
+ end
+end
+function bin:seek(n)
+ if self.stream then
+ if not n then return self.workingfile:seek("cur") end
+ local cur=self.workingfile:seek("cur")
+ self.workingfile:seek("set",cur+n)
+ else
+ if not n then return self.pos end
+ if #self.data-(self.pos-1)size then
+ data = data:sub(1,size)
+ elseif dsize255 then
+ nn=nn%256
+ elseif nn<0 then
+ nn=256-math.abs(nn)
+ end
+ buf[i]=nn
+ end
+ self:setSeek(1)
+ self:write(buf:getData())
+end
+function bin:getData(a,b,fmt)
+ local data=""
+ if a or b then
+ data=self:sub(a,b)
+ else
+ if self.stream then
+ local cur=self.workingfile:seek("cur")
+ self.workingfile:seek("set",0)
+ data=self.workingfile:read("*a")
+ self.workingfile:seek("set",cur)
+ else
+ data=self.data
+ end
+ end
+ if fmt=="%x" or fmt=="hex" then
+ return bin.toHex(data):lower()
+ elseif fmt=="%X" or fmt=="HEX" then
+ return bin.toHex(data)
+ elseif fmt=="%b" or fmt=="b64" then
+ return bin.toB64(data)
+ elseif fmt then
+ return bin.new(data):getBlock(fmt,#data)
+ end
+ return data
+end
+function bin:getSize(fmt)
+ local len=0
+ if self.stream then
+ local cur=self.workingfile:seek("cur")
+ len=self.workingfile:seek("end")
+ self.workingfile:seek("set",cur)
+ else
+ len=#self.data
+ end
+ if fmt=="%b" then
+ return bin.toB64()
+ elseif fmt then
+ return string.format(fmt, len)
+ else
+ return len
+ end
+end
+function bin:tackE(data,size,h)
+ local data=bin.normalizeData(data)
+ local cur=self:getSize()
+ self:setSeek(self:getSize()+1)
+ self:write(data,size)
+ if h then
+ self:setSeek(cur+1)
+ end
+end
+function bin:tonumber(a,b)
+ local temp={}
+ if a then
+ temp.data=self:sub(a,b)
+ else
+ temp=self
+ end
+ local l,r=0,0
+ local g=#temp.data
+ for i=1,g do
+ r=r+(256^(g-i))*string.byte(string.sub(temp.data,i,i))
+ l=l+(256^(i-1))*string.byte(string.sub(temp.data,i,i))
+ end
+ return r,l
+end
+function bin.endianflop(data)
+ return string.reverse(data)
+end
+function bin:tofile(name)
+ if self.stream then return end
+ if not name then error("Must include a filename to save as!") end
+ file = io.open(name, "wb")
+ file:write(self.data)
+ file:close()
+end
+function bin:close()
+ if self.stream then
+ if bin.streams[self.file][2]==1 then
+ bin.streams[self.file]=nil
+ self.workingfile:close()
+ else
+ bin.streams[self.file][2]=bin.streams[self.file][2]-1
+ self.workingfile=io.tmpfile()
+ self.workingfile:close()
+ end
+ end
+end
+function bin:getBlock(t,n)
+ local data=""
+ if not n then
+ if bin.registerBlocks[t] then
+ return bin.registerBlocks[t][1](nil,self)
+ else
+ error("Unknown format! Cannot read from file: "..tostring(t))
+ end
+ else
+ if t=="n" or t=="%e" or t=="%E" then
+ data=self:read(n)
+ local numB=bin.defualtBit.new(data)
+ local numL=bin.defualtBit.new(string.reverse(data))
+ local little=numL:tonumber(0)
+ local big=numB:tonumber(0)
+ if t=="%E" then
+ return big
+ elseif t=="%X" then
+ return bin.toHex(data):upper()
+ elseif t=="%x" then
+ return bin.toHex(data):lower()
+ elseif t=="%b" then
+ return bin.toB64(data)
+ elseif t=="%e" then
+ return little
+ end
+ return big,little
+ elseif t=="s" then
+ return self:read(n)
+ elseif bin.registerBlocks[t] then
+ return bin.registerBlocks[t][1](n,self)
+ else
+ error("Unknown format! Cannot read from file: "..tostring(t))
+ end
+ end
+end
+function bin:addBlock(d,fit,fmt)
+ if not fmt then fmt=type(d):sub(1,1) end
+ if bin.registerBlocks[fmt] then
+ self:tackE(bin.registerBlocks[fmt][2](d,fit,fmt,self,bin.registerBlocks[fmt][2]))
+ elseif type(d)=="number" then
+ local data=bin.defualtBit.numToBytes(d,fit or 4,fmt,function()
+ error("Overflow! Space allotted for number is smaller than the number takes up. Increase the fit!")
+ end)
+ self:tackE(data)
+ elseif type(d)=="string" then
+ local data=d:sub(1,fit or -1)
+ if #data<(fit or #data) then
+ data=data..string.rep("\0",fit-#data)
+ end
+ self:tackE(data)
+ end
+end
+bin.registerBlocks={}
+function bin.registerBlock(t,funcG,funcA)
+ bin.registerBlocks[t]={funcG,funcA}
+end
+function bin.newDataBuffer(size,fill) -- fills with \0 or nul or with what you enter
+ local c={}
+ local fill=fill or "\0"
+ c.data={self=c}
+ c.Type="buffer"
+ c.size=size or 0 -- 0 means an infinite buffer, sometimes useful
+ for i=1,c.size do
+ c.data[i]=fill
+ end
+ local mt={
+ __index=function(t,k)
+ if type(k)=="number" then
+ local data=t.data[k]
+ if data then
+ return string.byte(data)
+ else
+ error("Index out of range!")
+ end
+ elseif type(k)=="string" then
+ local num=tonumber(k)
+ if num then
+ local data=t.data[num]
+ if data then
+ return data
+ else
+ error("Index out of range!")
+ end
+ else
+ error("Only number-strings and numbers can be indexed!")
+ end
+ else
+ error("Only number-strings and numbers can be indexed!")
+ end
+ end,
+ __newindex=function(t,k,v)
+ if type(k)~="number" then error("Can only set a buffers data with a numeric index!") end
+ local data=""
+ if type(v)=="string" then
+ data=v
+ elseif type(v)=="number" then
+ data=string.char(v)
+ else
+ -- try to normalize the data of type v
+ data=bin.normalizeData(v)
+ end
+ t:fillBuffer(k,data)
+ end,
+ __tostring=function(t)
+ return t:getData()
+ end,
+ }
+ function c:fillBuffer(a,data)
+ local len=#data
+ if len==1 then
+ self.data[a]=data
+ else
+ local i=a-1
+ for d in data:gmatch(".") do
+ i=i+1
+ if i>c.size then
+ return #data-i+a
+ end
+ self.data[i]=d
+ end
+ return #data-i+(a-1)
+ end
+ end
+ function c:getData(a,b,fmt) -- LATER
+ local dat=bin.new(table.concat(self.data,"",a,b))
+ local n=dat:getSize()
+ return dat:getBlock(fmt or "s",n)
+ end
+ function c:getSize()
+ return #self:getData()
+ end
+ setmetatable(c,mt)
+ return c
+end
+function bin:newDataBufferFromStream(pos,size,fill) -- fills with \0 or nul or with what you enter IF the nothing exists inside the bin file.
+ local s=self:getSize()
+ if not self.stream then error("Can only created a streamed buffer on a streamable file!") end
+ if s==0 then
+ self:write(string.rep("\0",pos+size))
+ end
+ self:setSeek(1)
+ local c=bin.newDataBuffer(size,fill)
+ rawset(c,"pos",pos)
+ rawset(c,"size",size)
+ rawset(c,"fill",fill)
+ rawset(c,"bin",self)
+ rawset(c,"sync",function(self)
+ local cur=self.bin:getSeek()
+ self.bin:setSeek(self.pos)
+ self.bin:write(self:getData(),size)
+ self.bin:setSeek(cur)
+ end)
+ c:fillBuffer(1,self:sub(pos,pos+size))
+ function c:fillBuffer(a,data)
+ local len=#data
+ if len==1 then
+ self.data[a]=data
+ self:sync()
+ else
+ local i=a-1
+ for d in data:gmatch(".") do
+ i=i+1
+ if i>c.size then
+ self:sync()
+ return #data-i+a
+ end
+ self.data[i]=d
+ end
+ self:sync()
+ return #data-i+(a-1)
+ end
+ end
+ return c
+end
+function bin:toDataBuffer()
+ local s=self:getSize()
+ -- if self:canStreamWrite() then
+ -- return self:newDataBufferFromStream(0,s)
+ -- end
+ local buf=bin.newDataBuffer(s)
+ local data=self:read(512)
+ local i=1
+ while data~=nil do
+ buf[i]=data
+ data=self:read(512)
+ i=i+512
+ end
+ return buf
+end
+function bin:getMD5Hash()
+ self:setSeek(1)
+ local len=self:getSize()
+ local md5=bin.md5.new()
+ local SIZE=2048
+ if len>SIZE then
+ local dat=self:read(SIZE)
+ while dat~=nil do
+ md5:update(dat)
+ dat=self:read(SIZE)
+ end
+ return bin.md5.tohex(md5:finish()):upper()
+ else
+ return bin.md5.sumhexa(self:getData()):upper()
+ end
+end
+function bin:getHash()
+ if self:getSize()==0 then
+ return "NaN"
+ end
+ n=32
+ local rand = randomGen:newND(1,self:getSize(),self:getSize())
+ local h,g={},0
+ for i=1,n do
+ g=rand:nextInt()
+ table.insert(h,bin.toHex(self:sub(g,g)))
+ end
+ return table.concat(h,'')
+end
+function bin:flipbits()
+ if self:canStreamWrite() then
+ self:setSeek(1)
+ for i=1,self:getSize() do
+ self:write(string.char(255-string.byte(self:sub(i,i))))
+ end
+ else
+ local temp={}
+ for i=1,#self.data do
+ table.insert(temp,string.char(255-string.byte(string.sub(self.data,i,i))))
+ end
+ self.data=table.concat(temp,'')
+ end
+end
+function bin:encrypt()
+ self:flipbits()
+end
+function bin:decrypt()
+ self:flipbits()
+end
+-- Use with small files!
+function bin:gsub(...)
+ local data=self:getData()
+ local pos=self:getSeek()
+ self:setSeek(1)
+ self:write((data:gsub(...)) or data)
+ self:setSeek(loc)
+end
+function bin:gmatch(pat)
+ return self:getData():gmatch(pat)
+end
+function bin:match(pat)
+ return self:getData():match(pat)
+end
+function bin:trim()
+ local data=self:getData()
+ local pos=self:getSeek()
+ self:setSeek(1)
+ self:write(data:match'^()%s*$' and '' or data:match'^%s*(.*%S)')
+ self:setSeek(loc)
+end
+function bin:lines()
+ local t = {}
+ local function helper(line) table.insert(t, line) return '' end
+ helper((self:getData():gsub('(.-)\r?\n', helper)))
+ return t
+end
+function bin._lines(str)
+ local t = {}
+ local function helper(line) table.insert(t, line) return '' end
+ helper((str:gsub('(.-)\r?\n', helper)))
+ return t
+end
+function bin:wipe()
+ if self:canStreamWrite() then
+ self:close()
+ local c=bin.freshStream(self.file)
+ self.workingfile=c.workingfile
+ else
+ self.data=""
+ end
+ self:setSeek(1)
+end
+function bin:fullTrim(empty)
+ local t=self:lines()
+ for i=#t,1,-1 do
+ t[i]=bin._trim(t[i])
+ if empty then
+ if t[i]=="" then
+ table.remove(t,i)
+ end
+ end
+ end
+ self:wipe()
+ self:write(table.concat(t,"\n"))
+end
+require("bin.support.extraBlocks") -- registered blocks that you can use
+if love then
+ function bin.load(file,s,r)
+ content, size = love.filesystem.read(file)
+ local temp=bin.new(content)
+ temp.filepath=file
+ return temp
+ end
+ function bin:tofile(filename)
+ if not(filename) or self.Stream then return nil end
+ love.filesystem.write(filename,self.data)
+ end
+ function bin.stream(file)
+ return bin.newStreamFileObject(love.filesystem.newFile(file))
+ end
+ function bin:getSize(fmt)
+ local len=0
+ if self.stream then
+ local len=self.workingfile:getSize()
+ else
+ len=#self.data
+ end
+ if fmt=="%b" then
+ return bin.toB64()
+ elseif fmt then
+ return string.format(fmt, len)
+ else
+ return len
+ end
+ end
+ function bin:getSeek()
+ if self.stream then
+ return self.workingfile:tell()+1
+ else
+ return self.pos
+ end
+ end
+ function bin:setSeek(n)
+ if self.stream then
+ self.workingfile:seek(n-1)
+ else
+ self.pos=n
+ end
+ end
+ function bin:seek(n)
+ if self.stream then
+ self.workingfile:seek(n)
+ else
+ if not n then return self.pos end
+ if #self.data-(self.pos-1) 0 then
+ for i = bnum.len - 2 , 0 , -1 do
+ for j = 0 , RADIX_LEN - string.len( bnum[i] ) - 1 do
+ temp = temp .. '0' ;
+ end
+ temp = temp .. bnum[i] ;
+ end
+ if bnum[bnum.len - 1]==nil then
+ return "nil"
+ end
+ temp = bnum[bnum.len - 1] .. temp ;
+ if bnum.signal == '-' then
+ temp = bnum.signal .. temp ;
+ end
+ return temp ;
+ else
+ return "" ;
+ end
+end
+
+function BigNum.mt.pow( num1 , num2 )
+ local bnum1 = BigNum.new( num1 ) ;
+ local bnum2 = BigNum.new( num2 ) ;
+ return BigNum.pow( bnum1 , bnum2 ) ;
+end
+
+function BigNum.mt.eq( num1 , num2 )
+ local bnum1 = BigNum.new( num1 ) ;
+ local bnum2 = BigNum.new( num2 ) ;
+ return BigNum.eq( bnum1 , bnum2 ) ;
+end
+
+function BigNum.mt.lt( num1 , num2 )
+ local bnum1 = BigNum.new( num1 ) ;
+ local bnum2 = BigNum.new( num2 ) ;
+ return BigNum.lt( bnum1 , bnum2 ) ;
+end
+
+function BigNum.mt.le( num1 , num2 )
+ local bnum1 = BigNum.new( num1 ) ;
+ local bnum2 = BigNum.new( num2 ) ;
+ return BigNum.le( bnum1 , bnum2 ) ;
+end
+
+function BigNum.mt.unm( num )
+ local ret = BigNum.new( num )
+ if ret.signal == '+' then
+ ret.signal = '-'
+ else
+ ret.signal = '+'
+ end
+ return ret
+end
+
+BigNum.mt.__metatable = "hidden"
+BigNum.mt.__tostring = BigNum.mt.tostring ;
+BigNum.mt.__add = BigNum.mt.add ;
+BigNum.mt.__sub = BigNum.mt.sub ;
+BigNum.mt.__mul = BigNum.mt.mul ;
+BigNum.mt.__div = BigNum.mt.div ;
+BigNum.mt.__pow = BigNum.mt.pow ;
+BigNum.mt.__unm = BigNum.mt.unm ;
+BigNum.mt.__mod = BigNum.mt.mod ;
+BigNum.mt.__eq = BigNum.mt.eq ;
+BigNum.mt.__le = BigNum.mt.le ;
+BigNum.mt.__lt = BigNum.mt.lt ;
+setmetatable( BigNum.mt, { __index = "inexistent field", __newindex = "not available", __metatable="hidden" } ) ;
+function BigNum.add( bnum1 , bnum2 , bnum3 )
+ local maxlen = 0 ;
+ local i = 0 ;
+ local carry = 0 ;
+ local signal = '+' ;
+ local old_len = 0 ;
+ --Handle the signals
+ if bnum1 == nil or bnum2 == nil or bnum3 == nil then
+ error("Function BigNum.add: parameter nil") ;
+ elseif bnum1.signal == '-' and bnum2.signal == '+' then
+ bnum1.signal = '+' ;
+ BigNum.sub( bnum2 , bnum1 , bnum3 ) ;
+
+ if not rawequal(bnum1, bnum3) then
+ bnum1.signal = '-' ;
+ end
+ return 0 ;
+ elseif bnum1.signal == '+' and bnum2.signal == '-' then
+ bnum2.signal = '+' ;
+ BigNum.sub( bnum1 , bnum2 , bnum3 ) ;
+ if not rawequal(bnum2, bnum3) then
+ bnum2.signal = '-' ;
+ end
+ return 0 ;
+ elseif bnum1.signal == '-' and bnum2.signal == '-' then
+ signal = '-' ;
+ end
+ --
+ old_len = bnum3.len ;
+ if bnum1.len > bnum2.len then
+ maxlen = bnum1.len ;
+ else
+ maxlen = bnum2.len ;
+ bnum1 , bnum2 = bnum2 , bnum1 ;
+ end
+ --School grade sum
+ for i = 0 , maxlen - 1 do
+ if bnum2[i] ~= nil then
+ bnum3[i] = bnum1[i] + bnum2[i] + carry ;
+ else
+ bnum3[i] = bnum1[i] + carry ;
+ end
+ if bnum3[i] >= RADIX then
+ bnum3[i] = bnum3[i] - RADIX ;
+ carry = 1 ;
+ else
+ carry = 0 ;
+ end
+ end
+ --Update the answer's size
+ if carry == 1 then
+ bnum3[maxlen] = 1 ;
+ end
+ bnum3.len = maxlen + carry ;
+ bnum3.signal = signal ;
+ for i = bnum3.len, old_len do
+ bnum3[i] = nil ;
+ end
+ return 0 ;
+end
+
+function BigNum.sub( bnum1 , bnum2 , bnum3 )
+ local maxlen = 0 ;
+ local i = 0 ;
+ local carry = 0 ;
+ local old_len = 0 ;
+ if bnum1 == nil or bnum2 == nil or bnum3 == nil then
+ error("Function BigNum.sub: parameter nil") ;
+ elseif bnum1.signal == '-' and bnum2.signal == '+' then
+ bnum1.signal = '+' ;
+ BigNum.add( bnum1 , bnum2 , bnum3 ) ;
+ bnum3.signal = '-' ;
+ if not rawequal(bnum1, bnum3) then
+ bnum1.signal = '-' ;
+ end
+ return 0 ;
+ elseif bnum1.signal == '-' and bnum2.signal == '-' then
+ bnum1.signal = '+' ;
+ bnum2.signal = '+' ;
+ BigNum.sub( bnum2, bnum1 , bnum3 ) ;
+ if not rawequal(bnum1, bnum3) then
+ bnum1.signal = '-' ;
+ end
+ if not rawequal(bnum2, bnum3) then
+ bnum2.signal = '-' ;
+ end
+ return 0 ;
+ elseif bnum1.signal == '+' and bnum2.signal == '-' then
+ bnum2.signal = '+' ;
+ BigNum.add( bnum1 , bnum2 , bnum3 ) ;
+ if not rawequal(bnum2, bnum3) then
+ bnum2.signal = '-' ;
+ end
+ return 0 ;
+ end
+ --Tests if bnum2 > bnum1
+ if BigNum.compareAbs( bnum1 , bnum2 ) == 2 then
+ BigNum.sub( bnum2 , bnum1 , bnum3 ) ;
+ bnum3.signal = '-' ;
+ return 0 ;
+ else
+ maxlen = bnum1.len ;
+ end
+ old_len = bnum3.len ;
+ bnum3.len = 0 ;
+ --School grade subtraction
+ for i = 0 , maxlen - 1 do
+ if bnum2[i] ~= nil then
+ bnum3[i] = bnum1[i] - bnum2[i] - carry ;
+ else
+ bnum3[i] = bnum1[i] - carry ;
+ end
+ if bnum3[i] < 0 then
+ bnum3[i] = RADIX + bnum3[i] ;
+ carry = 1 ;
+ else
+ carry = 0 ;
+ end
+
+ if bnum3[i] ~= 0 then
+ bnum3.len = i + 1 ;
+ end
+ end
+ bnum3.signal = '+' ;
+ --Check if answer's size if zero
+ if bnum3.len == 0 then
+ bnum3.len = 1 ;
+ bnum3[0] = 0 ;
+ end
+ if carry == 1 then
+ error( "Error in function sub" ) ;
+ end
+ for i = bnum3.len , max( old_len , maxlen - 1 ) do
+ bnum3[i] = nil ;
+ end
+ return 0 ;
+end
+
+function BigNum.mul( bnum1 , bnum2 , bnum3 )
+ local i = 0 ; j = 0 ;
+ local temp = BigNum.new( ) ;
+ local temp2 = 0 ;
+ local carry = 0 ;
+ local oldLen = bnum3.len ;
+ if bnum1 == nil or bnum2 == nil or bnum3 == nil then
+ error("Function BigNum.mul: parameter nil") ;
+ --Handle the signals
+ elseif bnum1.signal ~= bnum2.signal then
+ BigNum.mul( bnum1 , -bnum2 , bnum3 ) ;
+ bnum3.signal = '-' ;
+ return 0 ;
+ end
+ bnum3.len = ( bnum1.len ) + ( bnum2.len ) ;
+ --Fill with zeros
+ for i = 1 , bnum3.len do
+ bnum3[i - 1] = 0 ;
+ end
+ --Places nil where passes through this
+ for i = bnum3.len , oldLen do
+ bnum3[i] = nil ;
+ end
+ --School grade multiplication
+ for i = 0 , bnum1.len - 1 do
+ for j = 0 , bnum2.len - 1 do
+ carry = ( bnum1[i] * bnum2[j] + carry ) ;
+ carry = carry + bnum3[i + j] ;
+ bnum3[i + j] = ( carry % RADIX ) ;
+ temp2 = bnum3[i + j] ;
+ carry = math.floor ( carry / RADIX ) ;
+ end
+ if carry ~= 0 then
+ bnum3[i + bnum2.len] = carry ;
+ end
+ carry = 0 ;
+ end
+
+ --Update the answer's size
+ for i = bnum3.len - 1 , 1 , -1 do
+ if bnum3[i] ~= nil and bnum3[i] ~= 0 then
+ break ;
+ else
+ bnum3[i] = nil ;
+ end
+ bnum3.len = bnum3.len - 1 ;
+ end
+ return 0 ;
+end
+
+function BigNum.div( bnum1 , bnum2 , bnum3 , bnum4 )
+ local temp = BigNum.new() ;
+ local temp2 = BigNum.new() ;
+ local one = BigNum.new( "1" ) ;
+ local zero = BigNum.new( "0" ) ;
+ --Check division by zero
+ if BigNum.compareAbs( bnum2 , zero ) == 0 then
+ error( "Function BigNum.div: Division by zero" ) ;
+ end
+ --Handle the signals
+ if bnum1 == nil or bnum2 == nil or bnum3 == nil or bnum4 == nil then
+ error( "Function BigNum.div: parameter nil" ) ;
+ elseif bnum1.signal == "+" and bnum2.signal == "-" then
+ bnum2.signal = "+" ;
+ BigNum.div( bnum1 , bnum2 , bnum3 , bnum4 ) ;
+ bnum2.signal = "-" ;
+ bnum3.signal = "-" ;
+ return 0 ;
+ elseif bnum1.signal == "-" and bnum2.signal == "+" then
+ bnum1.signal = "+" ;
+ BigNum.div( bnum1 , bnum2 , bnum3 , bnum4 ) ;
+ bnum1.signal = "-" ;
+ if bnum4 < zero then --Check if remainder is negative
+ BigNum.add( bnum3 , one , bnum3 ) ;
+ BigNum.sub( bnum2 , bnum4 , bnum4 ) ;
+ end
+ bnum3.signal = "-" ;
+ return 0 ;
+ elseif bnum1.signal == "-" and bnum2.signal == "-" then
+ bnum1.signal = "+" ;
+ bnum2.signal = "+" ;
+ BigNum.div( bnum1 , bnum2 , bnum3 , bnum4 ) ;
+ bnum1.signal = "-" ;
+ if bnum4 < zero then --Check if remainder is negative
+ BigNum.add( bnum3 , one , bnum3 ) ;
+ BigNum.sub( bnum2 , bnum4 , bnum4 ) ;
+ end
+ bnum2.signal = "-" ;
+ return 0 ;
+ end
+ temp.len = bnum1.len - bnum2.len - 1 ;
+
+ --Reset variables
+ BigNum.change( bnum3 , "0" ) ;
+ BigNum.change( bnum4 , "0" ) ;
+
+ BigNum.copy( bnum1 , bnum4 ) ;
+
+ --Check if can continue dividing
+ while( BigNum.compareAbs( bnum4 , bnum2 ) ~= 2 ) do
+ if bnum4[bnum4.len - 1] >= bnum2[bnum2.len - 1] then
+ BigNum.put( temp , math.floor( bnum4[bnum4.len - 1] / bnum2[bnum2.len - 1] ) , bnum4.len - bnum2.len ) ;
+ temp.len = bnum4.len - bnum2.len + 1 ;
+ else
+ BigNum.put( temp , math.floor( ( bnum4[bnum4.len - 1] * RADIX + bnum4[bnum4.len - 2] ) / bnum2[bnum2.len -1] ) , bnum4.len - bnum2.len - 1 ) ;
+ temp.len = bnum4.len - bnum2.len ;
+ end
+
+ if bnum4.signal ~= bnum2.signal then
+ temp.signal = "-";
+ else
+ temp.signal = "+";
+ end
+ BigNum.add( temp , bnum3 , bnum3 ) ;
+ temp = temp * bnum2 ;
+ BigNum.sub( bnum4 , temp , bnum4 ) ;
+ end
+
+ --Update if the remainder is negative
+ if bnum4.signal == '-' then
+ decr( bnum3 ) ;
+ BigNum.add( bnum2 , bnum4 , bnum4 ) ;
+ end
+ return 0 ;
+end
+
+function BigNum.pow( bnum1 , bnum2 )
+ local n = BigNum.new( bnum2 ) ;
+ local y = BigNum.new( 1 ) ;
+ local z = BigNum.new( bnum1 ) ;
+ local zero = BigNum.new( "0" ) ;
+ if bnum2 < zero then
+ error( "Function BigNum.exp: domain error" ) ;
+ elseif bnum2 == zero then
+ return y ;
+ end
+ while 1 do
+ if ( n[0] % 2 ) == 0 then
+ n = n / 2 ;
+ else
+ n = n / 2 ;
+ y = z * y ;
+ if n == zero then
+ return y ;
+ end
+ end
+ z = z * z ;
+ end
+end
+-- Portugus :
+BigNum.exp = BigNum.pow
+
+function BigNum.gcd( bnum1 , bnum2 )
+ local a = {} ;
+ local b = {} ;
+ local c = {} ;
+ local d = {} ;
+ local zero = {} ;
+ zero = BigNum.new( "0" ) ;
+ if bnum1 == zero or bnum2 == zero then
+ return BigNum.new( "1" ) ;
+ end
+ a = BigNum.new( bnum1 ) ;
+ b = BigNum.new( bnum2 ) ;
+ a.signal = '+' ;
+ b.signal = '+' ;
+ c = BigNum.new() ;
+ d = BigNum.new() ;
+ while b > zero do
+ BigNum.div( a , b , c , d ) ;
+ a , b , d = b , d , a ;
+ end
+ return a ;
+end
+-- Portugus:
+BigNum.mmc = BigNum.gcd
+
+function BigNum.eq( bnum1 , bnum2 )
+ if BigNum.compare( bnum1 , bnum2 ) == 0 then
+ return true ;
+ else
+ return false ;
+ end
+end
+
+function BigNum.lt( bnum1 , bnum2 )
+ if BigNum.compare( bnum1 , bnum2 ) == 2 then
+ return true ;
+ else
+ return false ;
+ end
+end
+
+function BigNum.le( bnum1 , bnum2 )
+ local temp = -1 ;
+ temp = BigNum.compare( bnum1 , bnum2 )
+ if temp == 0 or temp == 2 then
+ return true ;
+ else
+ return false ;
+ end
+end
+
+function BigNum.compareAbs( bnum1 , bnum2 )
+ if bnum1 == nil or bnum2 == nil then
+ error("Function compare: parameter nil") ;
+ elseif bnum1.len > bnum2.len then
+ return 1 ;
+ elseif bnum1.len < bnum2.len then
+ return 2 ;
+ else
+ local i ;
+ for i = bnum1.len - 1 , 0 , -1 do
+ if bnum1[i] > bnum2[i] then
+ return 1 ;
+ elseif bnum1[i] < bnum2[i] then
+ return 2 ;
+ end
+ end
+ end
+ return 0 ;
+end
+
+function BigNum.compare( bnum1 , bnum2 )
+ local signal = 0 ;
+
+ if bnum1 == nil or bnum2 == nil then
+ error("Funtion BigNum.compare: parameter nil") ;
+ elseif bnum1.signal == '+' and bnum2.signal == '-' then
+ return 1 ;
+ elseif bnum1.signal == '-' and bnum2.signal == '+' then
+ return 2 ;
+ elseif bnum1.signal == '-' and bnum2.signal == '-' then
+ signal = 1 ;
+ end
+ if bnum1.len > bnum2.len then
+ return 1 + signal ;
+ elseif bnum1.len < bnum2.len then
+ return 2 - signal ;
+ else
+ local i ;
+ for i = bnum1.len - 1 , 0 , -1 do
+ if bnum1[i] > bnum2[i] then
+ return 1 + signal ;
+ elseif bnum1[i] < bnum2[i] then
+ return 2 - signal ;
+ end
+ end
+ end
+ return 0 ;
+end
+
+function BigNum.copy( bnum1 , bnum2 )
+ if bnum1 ~= nil and bnum2 ~= nil then
+ local i ;
+ for i = 0 , bnum1.len - 1 do
+ bnum2[i] = bnum1[i] ;
+ end
+ bnum2.len = bnum1.len ;
+ else
+ error("Function BigNum.copy: parameter nil") ;
+ end
+end
+
+function BigNum.change( bnum1 , num )
+ local j = 0 ;
+ local len = 0 ;
+ local num = num ;
+ local l ;
+ local oldLen = 0 ;
+ if bnum1 == nil then
+ error( "BigNum.change: parameter nil" ) ;
+ elseif type( bnum1 ) ~= "table" then
+ error( "BigNum.change: parameter error, type unexpected" ) ;
+ elseif num == nil then
+ bnum1.len = 1 ;
+ bnum1[0] = 0 ;
+ bnum1.signal = "+";
+ elseif type( num ) == "table" and num.len ~= nil then --check if num is a big number
+ --copy given table to the new one
+ for i = 0 , num.len do
+ bnum1[i] = num[i] ;
+ end
+ if num.signal ~= '-' and num.signal ~= '+' then
+ bnum1.signal = '+' ;
+ else
+ bnum1.signal = num.signal ;
+ end
+ oldLen = bnum1.len ;
+ bnum1.len = num.len ;
+ elseif type( num ) == "string" or type( num ) == "number" then
+ if string.sub( num , 1 , 1 ) == '+' or string.sub( num , 1 , 1 ) == '-' then
+ bnum1.signal = string.sub( num , 1 , 1 ) ;
+ num = string.sub(num, 2) ;
+ else
+ bnum1.signal = '+' ;
+ end
+ num = string.gsub( num , " " , "" ) ;
+ local sf = string.find( num , "e" ) ;
+ --Handles if the number is in exp notation
+ if sf ~= nil then
+ num = string.gsub( num , "%." , "" ) ;
+ local e = string.sub( num , sf + 1 ) ;
+ e = tonumber(e) ;
+ if e ~= nil and e > 0 then
+ e = tonumber(e) ;
+ else
+ error( "Function BigNum.change: string is not a valid number" ) ;
+ end
+ num = string.sub( num , 1 , sf - 2 ) ;
+ for i = string.len( num ) , e do
+ num = num .. "0" ;
+ end
+ else
+ sf = string.find( num , "%." ) ;
+ if sf ~= nil then
+ num = string.sub( num , 1 , sf - 1 ) ;
+ end
+ end
+
+ l = string.len( num ) ;
+ oldLen = bnum1.len ;
+ if (l > RADIX_LEN) then
+ local mod = l-( math.floor( l / RADIX_LEN ) * RADIX_LEN ) ;
+ for i = 1 , l-mod, RADIX_LEN do
+ bnum1[j] = tonumber( string.sub( num, -( i + RADIX_LEN - 1 ) , -i ) );
+ --Check if string dosn't represents a number
+ if bnum1[j] == nil then
+ error( "Function BigNum.change: string is not a valid number" ) ;
+ bnum1.len = 0 ;
+ return 1 ;
+ end
+ j = j + 1 ;
+ len = len + 1 ;
+ end
+ if (mod ~= 0) then
+ bnum1[j] = tonumber( string.sub( num , 1 , mod ) ) ;
+ bnum1.len = len + 1 ;
+ else
+ bnum1.len = len ;
+ end
+ --Eliminate trailing zeros
+ for i = bnum1.len - 1 , 1 , -1 do
+ if bnum1[i] == 0 then
+ bnum1[i] = nil ;
+ bnum1.len = bnum1.len - 1 ;
+ else
+ break ;
+ end
+ end
+
+ else
+ -- string.len(num) <= RADIX_LEN
+ bnum1[j] = tonumber( num ) ;
+ bnum1.len = 1 ;
+ end
+ else
+ error( "Function BigNum.change: parameter error, type unexpected" ) ;
+ end
+
+ --eliminates the deprecated higher order 'algarisms'
+ if oldLen ~= nil then
+ for i = bnum1.len , oldLen do
+ bnum1[i] = nil ;
+ end
+ end
+
+ return 0 ;
+end
+
+function BigNum.put( bnum , int , pos )
+ if bnum == nil then
+ error("Function BigNum.put: parameter nil") ;
+ end
+ local i = 0 ;
+ for i = 0 , pos - 1 do
+ bnum[i] = 0 ;
+ end
+ bnum[pos] = int ;
+ for i = pos + 1 , bnum.len do
+ bnum[i] = nil ;
+ end
+ bnum.len = pos ;
+ return 0 ;
+end
+
+--printraw{{{2
+function printraw( bnum )
+ local i = 0 ;
+ if bnum == nil then
+ error( "Function printraw: parameter nil" ) ;
+ end
+ while 1 == 1 do
+ if bnum[i] == nil then
+ io.write( ' len '..bnum.len ) ;
+ if i ~= bnum.len then
+ io.write( ' ERRO!!!!!!!!' ) ;
+ end
+ io.write( "\n" ) ;
+ return 0 ;
+ end
+ io.write( 'r'..bnum[i] ) ;
+ i = i + 1 ;
+ end
+end
+--max{{{2
+function max( int1 , int2 )
+ if int1 > int2 then
+ return int1 ;
+ else
+ return int2 ;
+ end
+end
+
+--decr{{{2
+function decr( bnum1 )
+ local temp = {} ;
+ temp = BigNum.new( "1" ) ;
+ BigNum.sub( bnum1 , temp , bnum1 ) ;
+ return 0 ;
+end
diff --git a/examples/love2d Threading Example/bin/numbers/BigRat.lua b/examples/love2d Threading Example/bin/numbers/BigRat.lua
new file mode 100644
index 0000000..40e9777
--- /dev/null
+++ b/examples/love2d Threading Example/bin/numbers/BigRat.lua
@@ -0,0 +1,227 @@
+require( "bin.numbers.BigNum" ) ;
+
+BigRat = {} ;
+BigRat.mt = {} ;
+function BigRat.new( num1 , num2 ) --{{{2
+ local bigrat = {} ;
+ local f ;
+ setmetatable(bigrat, BigRat.mt) ;
+ if type( num1 ) == "table" then
+ if num1.num ~= nil and num1.den ~= nil then
+ bigrat.num = BigNum.new( num1.num ) ;
+ bigrat.den = BigNum.new( num1.den ) ;
+ else
+ bigrat.num = BigNum.new( num1 ) ;
+ bigrat.den = BigNum.new( "1" ) ;
+ end
+ elseif num1 ~= nil then
+ if num2 == nil then
+ bigrat.den = BigNum.new( "1" ) ;
+ else
+ bigrat.den = BigNum.new( num2 ) ;
+ end
+ bigrat.num = BigNum.new( num1 ) ;
+ else
+ bigrat.den = BigNum.new( ) ;
+ bigrat.num = BigNum.new( ) ;
+ end
+
+ --Update the signals
+ if bigrat.den.signal == "-" then
+ if bigrat.num.signal == "-" then
+ bigrat.num.signal = "+" ;
+ else
+ bigrat.num.signal = "-" ;
+ end
+ bigrat.den.signal = "+" ;
+ end
+
+ return bigrat ;
+end
+
+function BigRat.mt.sub( num1 , num2 )
+ local temp = BigRat.new() ;
+ local brat1 = BigRat.new( num1 ) ;
+ local brat2 = BigRat.new( num2 ) ;
+ BigRat.sub( brat1 , brat2 , temp ) ;
+ return temp ;
+end
+
+function BigRat.mt.add( num1 , num2 )
+ local temp = BigRat.new() ;
+ local brat1 = BigRat.new( num1 ) ;
+ local brat2 = BigRat.new( num2 ) ;
+ BigRat.add( brat1 , brat2 , temp ) ;
+ return temp ;
+end
+
+function BigRat.mt.mul( num1 , num2 )
+ local temp = BigRat.new() ;
+ local brat1 = BigRat.new( num1 ) ;
+ local brat2 = BigRat.new( num2 ) ;
+ BigRat.mul( brat1 , brat2 , temp ) ;
+ return temp ;
+end
+
+function BigRat.mt.div( num1 , num2 )
+ local brat1 = BigRat.new( num1 ) ;
+ local brat2 = BigRat.new( num2 ) ;
+ local brat3 = BigRat.new() ;
+ local brat4 = BigRat.new() ;
+ BigRat.div( brat1 , brat2 , brat3 , brat4 ) ;
+ return brat3 , brat4 ;
+end
+
+function BigRat.mt.tostring( brat )
+ BigRat.simplify( brat ) ;
+ return BigNum.mt.tostring( brat.num ) .. " / " .. BigNum.mt.tostring( brat.den ) ;
+end
+
+function BigRat.mt.pow ( num1 , num2 )
+ local brat1 = BigRat.new( num1 ) ;
+ local brat2 = BigRat.new( num2 ) ;
+ return BigRat.pow( brat1 , brat2 )
+end
+
+function BigRat.mt.eq ( num1 , num2 )
+ return BigRat.eq( num1 , num2 )
+end
+
+function BigRat.mt.lt ( num1 , num2 )
+ return BigRat.lt( num1 , num2 )
+end
+
+function BigRat.mt.le ( num1 , num2 )
+ return BigRat.le( num1 , num2 )
+end
+
+function BigRat.mt.unm ( num )
+ local ret = BigRat.new( num )
+ if ret.num.signal == '-' then
+ ret.num.signal = '+'
+ else
+ ret.num.signal = '-'
+ end
+ return ret
+end
+
+BigRat.mt.__metatable = "hidden"
+BigRat.mt.__tostring = BigRat.mt.tostring
+BigRat.mt.__add = BigRat.mt.add
+BigRat.mt.__sub = BigRat.mt.sub
+BigRat.mt.__mul = BigRat.mt.mul
+BigRat.mt.__div = BigRat.mt.div
+BigRat.mt.__pow = BigRat.mt.pow
+BigRat.mt.__unm = BigRat.mt.unm
+BigRat.mt.__eq = BigRat.mt.eq
+BigRat.mt.__le = BigRat.mt.le
+BigRat.mt.__lt = BigRat.mt.lt
+setmetatable( BigRat.mt, { __index = "inexistent field", __newindex = "not available", __metatable="hidden" } ) ;
+function BigRat.add( brat1 , brat2 , brat3 )
+ brat3.den = brat1.den * brat2.den ;
+ brat3.num = ( brat1.num * brat2.den ) + ( brat2.num * brat1.den ) ;
+ return brat3 ;
+end
+function BigRat.sub( brat1 , brat2 , brat3 )
+ brat3.den = brat1.den * brat2.den ;
+ brat3.num = ( brat1.num * brat2.den ) - ( brat2.num * brat1.den ) ;
+ return brat3 ;
+end
+
+function BigRat.mul( brat1 , brat2 , brat3 )
+ brat3.num = brat1.num * brat2.num ;
+ brat3.den = brat1.den * brat2.den ;
+ return 0 ;
+end
+
+function BigRat.div( brat1 , brat2 , brat3 )
+ brat3.num = brat1.num * brat2.den ;
+ brat3.den = brat1.den * brat2.num ;
+ return brat3 ;
+end
+
+function BigRat.pow( bnum1 , bnum2 )
+ if bnum1 == nil or bnum2 == nil then
+ error( "Function BigRat.pow: parameter nil" ) ;
+ end
+ local x = BigRat.new( "8" ) ;
+ local n = BigRat.new( bnum2.den ) ;
+ local n2 ;
+ local y = BigRat.new( ) ;
+ local i ;
+ local temp = BigRat.new( ) ;
+
+ BigRat.simplify( bnum2 ) ;
+ temp.num = BigNum.exp( bnum1.num , bnum2.num ) ;
+ temp.den = BigNum.exp( bnum1.den , bnum2.num ) ;
+ n2 = n - 1 ;
+
+ for i = 0 , 4 do
+ y.num = x.num ^ n2.num ;
+ y.den = x.den ^ n2.num ;
+ x = (( temp / y ) + ( n2 * x )) / n ;
+ end
+ return x ;
+end
+
+function BigRat.simplify( brat )
+ if brat == nil then
+ error( "Function BigRat.simplify: parameter nil" ) ;
+ end
+ local gcd = BigNum.new( ) ;
+ local temp = BigRat.new( brat ) ;
+ local devnull = BigNum.new( ) ;
+ local zero = BigNum.new( "0" ) ;
+ --Check if numerator is zero
+ if BigNum.compareAbs( brat.num , zero ) == 0 then
+ brat.den = BigNum.new( "1" ) ;
+ return 0 ;
+ end
+ gcd = BigNum.gcd( brat.num , brat.den ) ;
+ BigNum.div( temp.num , gcd , brat.num , devnull ) ;
+ BigNum.div( temp.den , gcd , brat.den , devnull ) ;
+ --Update the signal
+ if brat.num.signal == '-' and brat.den.signal == '-' then
+ brat.num.signal = '+' ;
+ brat.den.signal = '+' ;
+ end
+ return 0 ;
+end
+
+function BigRat.eq( brat1 , brat2 )
+ if BigRat.compare( brat1 , brat2 ) == 0 then
+ return true ;
+ else
+ return false ;
+ end
+end
+
+function BigRat.lt( brat1 , brat2 )
+ if BigRat.compare( brat1 , brat2 ) == 2 then
+ return true ;
+ else
+ return false ;
+ end
+end
+
+function BigRat.le( brat1 , brat2 )
+ local temp = -1 ;
+ temp = BigRat.compare( brat1 , brat2 )
+ if temp == 0 or temp == 2 then
+ return true ;
+ else
+ return false ;
+ end
+end
+
+function BigRat.compare( bnum1 , bnum2 )
+ local temp ;
+ temp = bnum1 - bnum2 ;
+ if temp.num[0] == 0 and temp.num.len == 1 then --Check if is zero
+ return 0 ;
+ elseif temp.num.signal == "-" then
+ return 2 ;
+ else
+ return 1 ;
+ end
+end
diff --git a/examples/love2d Threading Example/bin/numbers/bits.lua b/examples/love2d Threading Example/bin/numbers/bits.lua
new file mode 100644
index 0000000..579f320
--- /dev/null
+++ b/examples/love2d Threading Example/bin/numbers/bits.lua
@@ -0,0 +1,191 @@
+local bits={}
+bits.data=''
+bits.t='bits'
+bits.Type='bits'
+bits.__index = bits
+bits.__tostring=function(self) return self.data end
+bits.__len=function(self) return (#self.data)/8 end
+local floor,insert = math.floor, table.insert
+function bits.newBitBuffer(n)
+ --
+end
+function bits.newConverter(bitsIn,bitsOut)
+ local c={}
+ --
+end
+function basen(n,b)
+ if not b or b == 10 then return tostring(n) end
+ local digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ local t = {}
+ local sign = ""
+ if n < 0 then
+ sign = "-"
+ n = -n
+ end
+ repeat
+ local d = n % b + 1
+ n = n / b
+ insert(t, 1, digits:sub(d,d))
+ until n == 0
+ return sign .. table.concat(t,"")
+end
+bits.ref={}
+function bits.newByte(d)
+ local c={}
+ if type(d)=="string" then
+ if #d>1 or #d<1 then
+ error("A byte must be one character!")
+ else
+ c.data=string.byte(d)
+ end
+ elseif type(d)=="number" then
+ if d>255 or d<0 then
+ error("A byte must be between 0 and 255!")
+ else
+ c.data=d
+ end
+ else
+ error("cannot use type "..type(d).." as an argument! Takes only strings or numbers!")
+ end
+ c.__index=function(self,k)
+ if k>=0 and k<9 then
+ if self.data==0 then
+ return 0
+ elseif self.data==255 then
+ return 1
+ else
+ return bits.ref[self.data][k]
+ end
+ end
+ end
+ c.__tostring=function(self)
+ return bits.ref[tostring(self.data)]
+ end
+ setmetatable(c,c)
+ return c
+end
+function bits.newByteArray(s)
+ local c={}
+ if type(s)~="string" then
+ error("Must be a string type or bin/buffer type")
+ elseif type(s)=="table" then
+ if s.t=="sink" or s.t=="buffer" or s.t=="bin" then
+ local data=s:getData()
+ for i=1,#data do
+ c[#c+1]=bits.newByte(data:sub(i,i))
+ end
+ else
+ error("Must be a string type or bin/buffer type")
+ end
+ else
+ for i=1,#s do
+ c[#c+1]=bits.newByte(s:sub(i,i))
+ end
+ end
+ return c
+end
+function bits.new(n,binary)
+ local temp={}
+ temp.t="bits"
+ temp.Type="bits"
+ if type(n)=="string" then
+ if binary then
+ temp.data=n:match("[10]+")
+ else
+ local t={}
+ for i=#n,1,-1 do
+ table.insert(t,bits:conv(string.byte(n,i)))
+ end
+ temp.data=table.concat(t)
+ end
+ elseif type(n)=="number" or type(n)=="table" then
+ temp.data=basen(n,2)
+ end
+ if #temp.data%8~=0 then
+ temp.data=string.rep('0',8-#temp.data%8)..temp.data
+ end
+ setmetatable(temp, bits)
+ return temp
+end
+for i=0,255 do
+ local d=bits.new(i).data
+ bits.ref[i]={d:match("(%d)(%d)(%d)(%d)(%d)(%d)(%d)(%d)")}
+ bits.ref[tostring(i)]=d
+ bits.ref[d]=i
+ bits.ref["\255"..string.char(i)]=d
+end
+function bits.numToBytes(n,fit,func)
+ local num=string.reverse(bits.new(n):toSbytes())
+ local ref={["num"]=num,["fit"]=fit}
+ if fit then
+ if fit<#num then
+ if func then
+ print("Warning: attempting to store a number that takes up more space than allotted! Using provided method!")
+ func(ref)
+ else
+ print("Warning: attempting to store a number that takes up more space than allotted!")
+ end
+ return ref.num:sub(1,ref.fit)
+ elseif fit==#num then
+ return string.reverse(num)
+ else
+ return string.reverse(string.rep("\0",fit-#num)..num)
+ end
+ else
+ return string.reverse(num)
+ end
+end
+function bits:conv(n)
+ local tab={}
+ while n>=1 do
+ table.insert(tab,n%2)
+ n=math.floor(n/2)
+ end
+ local str=string.reverse(table.concat(tab))
+ if #str%8~=0 or #str==0 then
+ str=string.rep('0',8-#str%8)..str
+ end
+ return str
+end
+function bits:tonumber(s,e)
+ if s==0 then
+ return tonumber(self.data,2)
+ end
+ s=s or 1
+ return tonumber(string.sub(self.data,(8*(s-1))+1,8*s),2) or error('Bounds!')
+end
+function bits:isover()
+ return #self.data>8
+end
+function bits:flipbits()
+ tab={}
+ for i=1,#self.data do
+ if string.sub(self.data,i,i)=='1' then
+ table.insert(tab,'0')
+ else
+ table.insert(tab,'1')
+ end
+ end
+ self.data=table.concat(tab)
+end
+function bits:tobytes()
+ local tab={}
+ for i=self:getbytes(),1,-1 do
+ table.insert(tab,string.char(self:tonumber(i)))
+ end
+ return bin.new(table.concat(tab))
+end
+function bits:toSbytes()
+ local tab={}
+ for i=self:getbytes(),1,-1 do
+ table.insert(tab,string.char(self:tonumber(i)))
+ end
+ return table.concat(tab)
+end
+function bits:getBin()
+ return self.data
+end
+function bits:getbytes()
+ return #self.data/8
+end
+return bits
diff --git a/examples/love2d Threading Example/bin/numbers/infinabits.lua b/examples/love2d Threading Example/bin/numbers/infinabits.lua
new file mode 100644
index 0000000..69c45ff
--- /dev/null
+++ b/examples/love2d Threading Example/bin/numbers/infinabits.lua
@@ -0,0 +1,244 @@
+local binNum=require("bin.numbers.BigNum")
+local infinabits={}
+infinabits.data=''
+infinabits.t='infinabits'
+infinabits.Type='infinabits'
+infinabits.__index = infinabits
+infinabits.__tostring=function(self) return self.data end
+infinabits.__len=function(self) return (#self.data)/8 end
+local floor,insert = math.floor, table.insert
+function basen(n,b)
+ n=BigNum.new(n)
+ if not b or b == 10 then return tostring(n) end
+ local digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ local t = {}
+ local sign = ""
+ if n < BigNum.new(0) then
+ sign = "-"
+ n = -n
+ end
+ repeat
+ local d = tonumber(tostring(n % b)) + 1
+ n = n / b
+ insert(t, 1, digits:sub(d,d))
+ until n == BigNum.new(0)
+ return sign .. table.concat(t,"")
+end
+function base2to10(num)
+ local n=BigNum.new(0)
+ for i = #num-1,0,-1 do
+ nn=BigNum.new(num:sub(i+1,i+1))*(BigNum.new(2)^((#num-i)-1))
+ n=n+nn
+ end
+ return n
+end
+function infinabits.newBitBuffer(n)
+ -- WIP
+end
+function infinabits.newConverter(bitsIn,bitsOut)
+ local c={}
+ -- WIP
+end
+infinabits.ref={}
+function infinabits.newByte(d)-- WIP
+ local c={}
+ if type(d)=="string" then
+ if #d>1 or #d<1 then
+ error("A byte must be one character!")
+ else
+ c.data=string.byte(d)
+ end
+ elseif type(d)=="number" then
+ if d>255 or d<0 then
+ error("A byte must be between 0 and 255!")
+ else
+ c.data=d
+ end
+ else
+ error("cannot use type "..type(d).." as an argument! Takes only strings or numbers!")
+ end
+ c.__index=function(self,k)
+ if k>=0 and k<9 then
+ if self.data==0 then
+ return 0
+ elseif self.data==255 then
+ return 1
+ else
+ return infinabits.ref[self.data][k]
+ end
+ end
+ end
+ c.__tostring=function(self)
+ return infinabits.ref[tostring(self.data)]
+ end
+ setmetatable(c,c)
+ return c
+end
+function infinabits.newByteArray(s)-- WIP
+ local c={}
+ if type(s)~="string" then
+ error("Must be a string type or bin/buffer type")
+ elseif type(s)=="table" then
+ if s.t=="sink" or s.t=="buffer" or s.t=="bin" then
+ local data=s:getData()
+ for i=1,#data do
+ c[#c+1]=infinabits.newByte(data:sub(i,i))
+ end
+ else
+ error("Must be a string type or bin/buffer type")
+ end
+ else
+ for i=1,#s do
+ c[#c+1]=infinabits.newByte(s:sub(i,i))
+ end
+ end
+ return c
+end
+function infinabits.new(n,binary)
+ local temp={}
+ temp.t="infinabits"
+ temp.Type="infinabits"
+ if type(n)=="string" then
+ if binary then
+ temp.data=n:match("[10]+")
+ else
+ local t={}
+ for i=#n,1,-1 do
+ table.insert(t,infinabits:conv(string.byte(n,i)))
+ end
+ temp.data=table.concat(t)
+ end
+ elseif type(n)=="number" or type(n)=="table" then
+ temp.data=basen(tostring(n),2)
+ end
+ if #temp.data%8~=0 then
+ temp.data=string.rep('0',8-#temp.data%8)..temp.data
+ end
+ setmetatable(temp, infinabits)
+ return temp
+end
+for i=0,255 do
+ local d=infinabits.new(i).data
+ infinabits.ref[i]={d:match("(%d)(%d)(%d)(%d)(%d)(%d)(%d)(%d)")}
+ infinabits.ref[tostring(i)]=d
+ infinabits.ref[d]=i
+ infinabits.ref["\255"..string.char(i)]=d
+end
+function infinabits.numToBytes(n,fit,func)
+ local num=string.reverse(infinabits.new(BigNum.new(n)):toSbytes())
+ local ref={["num"]=num,["fit"]=fit}
+ if fit then
+ if fit<#num then
+ if func then
+ print("Warning: attempting to store a number that takes up more space than allotted! Using provided method!")
+ func(ref)
+ else
+ print("Warning: attempting to store a number that takes up more space than allotted!")
+ end
+ return ref.num:sub(1,ref.fit)
+ elseif fit==#num then
+ return string.reverse(num)
+ else
+ return string.reverse(string.rep("\0",fit-#num)..num)
+ end
+ else
+ return string.reverse(num)
+ end
+end
+function infinabits.numToBytes(n,fit,fmt,func)
+ if fmt=="%e" then
+ local num=string.reverse(infinabits.new(BigNum.new(n)):toSbytes())
+ local ref={["num"]=num,["fit"]=fit}
+ if fit then
+ if fit<#num then
+ if func then
+ print("Warning: attempting to store a number that takes up more space than allotted! Using provided method!")
+ func(ref)
+ else
+ print("Warning: attempting to store a number that takes up more space than allotted!")
+ end
+ return ref.num:sub(1,ref.fit)
+ elseif fit==#num then
+ return num
+ else
+ return string.rep("\0",fit-#num)..num
+ end
+ else
+ return num
+ end
+
+ else
+ local num=string.reverse(infinabits.new(BigNum.new(n)):toSbytes())
+ local ref={["num"]=num,["fit"]=fit}
+ if fit then
+ if fit<#num then
+ if func then
+ print("Warning: attempting to store a number that takes up more space than allotted! Using provided method!")
+ func(ref)
+ else
+ print("Warning: attempting to store a number that takes up more space than allotted!")
+ end
+ return ref.num:sub(1,ref.fit)
+ elseif fit==#num then
+ return string.reverse(num)
+ else
+ return string.reverse(string.rep("\0",fit-#num)..num)
+ end
+ else
+ return string.reverse(num)
+ end
+ end
+end
+function infinabits:conv(n)
+ local tab={}
+ local one=BigNum.new(1)
+ local n=BigNum.new(n)
+ while n>=one do
+ table.insert(tab,tonumber(tostring(n%2)))
+ n=n/2
+ end
+ local str=string.reverse(table.concat(tab))
+ if #str%8~=0 or #str==0 then
+ str=string.rep('0',8-#str%8)..str
+ end
+ return str
+end
+function infinabits:tonumber(s)
+ if s==0 then
+ return tonumber(self.data,2)
+ end
+ s=s or 1
+ return tonumber(tostring(base2to10(string.sub(self.data,(8*(s-1))+1,8*s)))) or error('Bounds!')
+end
+function infinabits:isover()
+ return #self.data>8
+end
+function infinabits:flipbits()
+ tab={}
+ local s=self.data
+ s=s:gsub("1","_")
+ s=s:gsub("0","1")
+ s=s:gsub("_","0")
+ self.data=s
+end
+function infinabits:tobytes()
+ local tab={}
+ for i=self:getbytes(),1,-1 do
+ table.insert(tab,string.char(self:tonumber(i)))
+ end
+ return bin.new(table.concat(tab))
+end
+function infinabits:toSbytes()
+ local tab={}
+ for i=self:getbytes(),1,-1 do
+ table.insert(tab,string.char(self:tonumber(i)))
+ end
+ return table.concat(tab)
+end
+function infinabits:getBin()
+ return self.data
+end
+function infinabits:getbytes()
+ return #self.data/8
+end
+return infinabits
diff --git a/examples/love2d Threading Example/bin/numbers/no_jit_bit.lua b/examples/love2d Threading Example/bin/numbers/no_jit_bit.lua
new file mode 100644
index 0000000..2cfdc1d
--- /dev/null
+++ b/examples/love2d Threading Example/bin/numbers/no_jit_bit.lua
@@ -0,0 +1,333 @@
+--[[
+LICENSE
+
+ (c) 2008-2011 David Manura. Licensed under the same terms as Lua (MIT).
+--]]
+
+local M = {_TYPE='module', _NAME='bit.numberlua', _VERSION='0.3.1.20120131'}
+
+local floor = math.floor
+
+local MOD = 2^32
+local MODM = MOD-1
+
+local function memoize(f)
+ local mt = {}
+ local t = setmetatable({}, mt)
+ function mt:__index(k)
+ local v = f(k); t[k] = v
+ return v
+ end
+ return t
+end
+
+local function make_bitop_uncached(t, m)
+ local function bitop(a, b)
+ local res,p = 0,1
+ while a ~= 0 and b ~= 0 do
+ local am, bm = a%m, b%m
+ res = res + t[am][bm]*p
+ a = (a - am) / m
+ b = (b - bm) / m
+ p = p*m
+ end
+ res = res + (a+b)*p
+ return res
+ end
+ return bitop
+end
+
+local function make_bitop(t)
+ local op1 = make_bitop_uncached(t,2^1)
+ local op2 = memoize(function(a)
+ return memoize(function(b)
+ return op1(a, b)
+ end)
+ end)
+ return make_bitop_uncached(op2, 2^(t.n or 1))
+end
+
+-- ok? probably not if running on a 32-bit int Lua number type platform
+function M.tobit(x)
+ return x % 2^32
+end
+
+M.bxor = make_bitop {[0]={[0]=0,[1]=1},[1]={[0]=1,[1]=0}, n=4}
+local bxor = M.bxor
+
+function M.bnot(a) return MODM - a end
+local bnot = M.bnot
+
+function M.band(a,b) return ((a+b) - bxor(a,b))/2 end
+local band = M.band
+
+function M.bor(a,b) return MODM - band(MODM - a, MODM - b) end
+local bor = M.bor
+
+local lshift, rshift -- forward declare
+
+function M.rshift(a,disp) -- Lua5.2 insipred
+ if disp < 0 then return lshift(a,-disp) end
+ return floor(a % 2^32 / 2^disp)
+end
+rshift = M.rshift
+
+function M.lshift(a,disp) -- Lua5.2 inspired
+ if disp < 0 then return rshift(a,-disp) end
+ return (a * 2^disp) % 2^32
+end
+lshift = M.lshift
+
+function M.tohex(x, n) -- BitOp style
+ n = n or 8
+ local up
+ if n <= 0 then
+ if n == 0 then return '' end
+ up = true
+ n = - n
+ end
+ x = band(x, 16^n-1)
+ return ('%0'..n..(up and 'X' or 'x')):format(x)
+end
+local tohex = M.tohex
+
+function M.extract(n, field, width) -- Lua5.2 inspired
+ width = width or 1
+ return band(rshift(n, field), 2^width-1)
+end
+local extract = M.extract
+
+function M.replace(n, v, field, width) -- Lua5.2 inspired
+ width = width or 1
+ local mask1 = 2^width-1
+ v = band(v, mask1) -- required by spec?
+ local mask = bnot(lshift(mask1, field))
+ return band(n, mask) + lshift(v, field)
+end
+local replace = M.replace
+
+function M.bswap(x) -- BitOp style
+ local a = band(x, 0xff); x = rshift(x, 8)
+ local b = band(x, 0xff); x = rshift(x, 8)
+ local c = band(x, 0xff); x = rshift(x, 8)
+ local d = band(x, 0xff)
+ return lshift(lshift(lshift(a, 8) + b, 8) + c, 8) + d
+end
+local bswap = M.bswap
+
+function M.rrotate(x, disp) -- Lua5.2 inspired
+ disp = disp % 32
+ local low = band(x, 2^disp-1)
+ return rshift(x, disp) + lshift(low, 32-disp)
+end
+local rrotate = M.rrotate
+
+function M.lrotate(x, disp) -- Lua5.2 inspired
+ return rrotate(x, -disp)
+end
+local lrotate = M.lrotate
+
+M.rol = M.lrotate -- LuaOp inspired
+M.ror = M.rrotate -- LuaOp insipred
+
+
+function M.arshift(x, disp) -- Lua5.2 inspired
+ local z = rshift(x, disp)
+ if x >= 0x80000000 then z = z + lshift(2^disp-1, 32-disp) end
+ return z
+end
+local arshift = M.arshift
+
+function M.btest(x, y) -- Lua5.2 inspired
+ return band(x, y) ~= 0
+end
+
+M.bit32 = {} -- Lua 5.2 'bit32' compatibility
+
+
+local function bit32_bnot(x)
+ return (-1 - x) % MOD
+end
+M.bit32.bnot = bit32_bnot
+
+local function bit32_bxor(a, b, c, ...)
+ local z
+ if b then
+ a = a % MOD
+ b = b % MOD
+ z = bxor(a, b)
+ if c then
+ z = bit32_bxor(z, c, ...)
+ end
+ return z
+ elseif a then
+ return a % MOD
+ else
+ return 0
+ end
+end
+M.bit32.bxor = bit32_bxor
+
+local function bit32_band(a, b, c, ...)
+ local z
+ if b then
+ a = a % MOD
+ b = b % MOD
+ z = ((a+b) - bxor(a,b)) / 2
+ if c then
+ z = bit32_band(z, c, ...)
+ end
+ return z
+ elseif a then
+ return a % MOD
+ else
+ return MODM
+ end
+end
+M.bit32.band = bit32_band
+
+local function bit32_bor(a, b, c, ...)
+ local z
+ if b then
+ a = a % MOD
+ b = b % MOD
+ z = MODM - band(MODM - a, MODM - b)
+ if c then
+ z = bit32_bor(z, c, ...)
+ end
+ return z
+ elseif a then
+ return a % MOD
+ else
+ return 0
+ end
+end
+M.bit32.bor = bit32_bor
+
+function M.bit32.btest(...)
+ return bit32_band(...) ~= 0
+end
+
+function M.bit32.lrotate(x, disp)
+ return lrotate(x % MOD, disp)
+end
+
+function M.bit32.rrotate(x, disp)
+ return rrotate(x % MOD, disp)
+end
+
+function M.bit32.lshift(x,disp)
+ if disp > 31 or disp < -31 then return 0 end
+ return lshift(x % MOD, disp)
+end
+
+function M.bit32.rshift(x,disp)
+ if disp > 31 or disp < -31 then return 0 end
+ return rshift(x % MOD, disp)
+end
+
+function M.bit32.arshift(x,disp)
+ x = x % MOD
+ if disp >= 0 then
+ if disp > 31 then
+ return (x >= 0x80000000) and MODM or 0
+ else
+ local z = rshift(x, disp)
+ if x >= 0x80000000 then z = z + lshift(2^disp-1, 32-disp) end
+ return z
+ end
+ else
+ return lshift(x, -disp)
+ end
+end
+
+function M.bit32.extract(x, field, ...)
+ local width = ... or 1
+ if field < 0 or field > 31 or width < 0 or field+width > 32 then error 'out of range' end
+ x = x % MOD
+ return extract(x, field, ...)
+end
+
+function M.bit32.replace(x, v, field, ...)
+ local width = ... or 1
+ if field < 0 or field > 31 or width < 0 or field+width > 32 then error 'out of range' end
+ x = x % MOD
+ v = v % MOD
+ return replace(x, v, field, ...)
+end
+
+M.bit = {} -- LuaBitOp "bit" compatibility
+
+function M.bit.tobit(x)
+ x = x % MOD
+ if x >= 0x80000000 then x = x - MOD end
+ return x
+end
+local bit_tobit = M.bit.tobit
+
+function M.bit.tohex(x, ...)
+ return tohex(x % MOD, ...)
+end
+
+function M.bit.bnot(x)
+ return bit_tobit(bnot(x % MOD))
+end
+
+local function bit_bor(a, b, c, ...)
+ if c then
+ return bit_bor(bit_bor(a, b), c, ...)
+ elseif b then
+ return bit_tobit(bor(a % MOD, b % MOD))
+ else
+ return bit_tobit(a)
+ end
+end
+M.bit.bor = bit_bor
+
+local function bit_band(a, b, c, ...)
+ if c then
+ return bit_band(bit_band(a, b), c, ...)
+ elseif b then
+ return bit_tobit(band(a % MOD, b % MOD))
+ else
+ return bit_tobit(a)
+ end
+end
+M.bit.band = bit_band
+
+local function bit_bxor(a, b, c, ...)
+ if c then
+ return bit_bxor(bit_bxor(a, b), c, ...)
+ elseif b then
+ return bit_tobit(bxor(a % MOD, b % MOD))
+ else
+ return bit_tobit(a)
+ end
+end
+M.bit.bxor = bit_bxor
+
+function M.bit.lshift(x, n)
+ return bit_tobit(lshift(x % MOD, n % 32))
+end
+
+function M.bit.rshift(x, n)
+ return bit_tobit(rshift(x % MOD, n % 32))
+end
+
+function M.bit.arshift(x, n)
+ return bit_tobit(arshift(x % MOD, n % 32))
+end
+
+function M.bit.rol(x, n)
+ return bit_tobit(lrotate(x % MOD, n % 32))
+end
+
+function M.bit.ror(x, n)
+ return bit_tobit(rrotate(x % MOD, n % 32))
+end
+
+function M.bit.bswap(x)
+ return bit_tobit(bswap(x % MOD))
+end
+
+return M
diff --git a/examples/love2d Threading Example/bin/numbers/random.lua b/examples/love2d Threading Example/bin/numbers/random.lua
new file mode 100644
index 0000000..091a7f5
--- /dev/null
+++ b/examples/love2d Threading Example/bin/numbers/random.lua
@@ -0,0 +1,232 @@
+--[[----------------------------------------
+Random
+Not all of this is mine
+------------------------------------------]]
+--[[------------------------------------
+RandomLua v0.3.1
+Pure Lua Pseudo-Random Numbers Generator
+Under the MIT license.
+copyright(c) 2011 linux-man
+--]]------------------------------------
+
+local math_floor = math.floor
+
+local function normalize(n)
+ return n % 0x80000000
+end
+
+local function bit_and(a, b)
+ local r = 0
+ local m = 0
+ for m = 0, 31 do
+ if (a % 2 == 1) and (b % 2 == 1) then r = r + 2^m end
+ if a % 2 ~= 0 then a = a - 1 end
+ if b % 2 ~= 0 then b = b - 1 end
+ a = a / 2 b = b / 2
+ end
+ return normalize(r)
+end
+
+local function bit_or(a, b)
+ local r = 0
+ local m = 0
+ for m = 0, 31 do
+ if (a % 2 == 1) or (b % 2 == 1) then r = r + 2^m end
+ if a % 2 ~= 0 then a = a - 1 end
+ if b % 2 ~= 0 then b = b - 1 end
+ a = a / 2 b = b / 2
+ end
+ return normalize(r)
+end
+
+local function bit_xor(a, b)
+ local r = 0
+ local m = 0
+ for m = 0, 31 do
+ if a % 2 ~= b % 2 then r = r + 2^m end
+ if a % 2 ~= 0 then a = a - 1 end
+ if b % 2 ~= 0 then b = b - 1 end
+ a = a / 2 b = b / 2
+ end
+ return normalize(r)
+end
+
+local function seed()
+ return normalize(os.time())
+end
+
+--Mersenne twister
+local mersenne_twister = {}
+mersenne_twister.__index = mersenne_twister
+
+function mersenne_twister:randomseed(s)
+ if not s then s = seed() end
+ self.mt[0] = normalize(s)
+ for i = 1, 623 do
+ self.mt[i] = normalize(0x6c078965 * bit_xor(self.mt[i-1], math_floor(self.mt[i-1] / 0x40000000)) + i)
+ end
+end
+
+function mersenne_twister:random(a, b)
+ local y
+ if self.index == 0 then
+ for i = 0, 623 do
+ y = self.mt[(i + 1) % 624] % 0x80000000
+ self.mt[i] = bit_xor(self.mt[(i + 397) % 624], math_floor(y / 2))
+ if y % 2 ~= 0 then self.mt[i] = bit_xor(self.mt[i], 0x9908b0df) end
+ end
+ end
+ y = self.mt[self.index]
+ y = bit_xor(y, math_floor(y / 0x800))
+ y = bit_xor(y, bit_and(normalize(y * 0x80), 0x9d2c5680))
+ y = bit_xor(y, bit_and(normalize(y * 0x8000), 0xefc60000))
+ y = bit_xor(y, math_floor(y / 0x40000))
+ self.index = (self.index + 1) % 624
+ if not a then return y / 0x80000000
+ elseif not b then
+ if a == 0 then return y
+ else return 1 + (y % a)
+ end
+ else
+ return a + (y % (b - a + 1))
+ end
+end
+
+local function twister(s)
+ local temp = {}
+ setmetatable(temp, mersenne_twister)
+ temp.mt = {}
+ temp.index = 0
+ temp:randomseed(s)
+ return temp
+end
+
+--Linear Congruential Generator
+local linear_congruential_generator = {}
+linear_congruential_generator.__index = linear_congruential_generator
+
+function linear_congruential_generator:random(a, b)
+ local y = (self.a * self.x + self.c) % self.m
+ self.x = y
+ if not a then return y / 0x10000
+ elseif not b then
+ if a == 0 then return y
+ else return 1 + (y % a) end
+ else
+ return a + (y % (b - a + 1))
+ end
+end
+
+function linear_congruential_generator:randomseed(s)
+ if not s then s = seed() end
+ self.x = normalize(s)
+end
+
+local function lcg(s, r)
+ local temp = {}
+ setmetatable(temp, linear_congruential_generator)
+ temp.a, temp.c, temp.m = 1103515245, 12345, 0x10000 --from Ansi C
+ if r then
+ if r == 'nr' then temp.a, temp.c, temp.m = 1664525, 1013904223, 0x10000 --from Numerical Recipes.
+ elseif r == 'mvc' then temp.a, temp.c, temp.m = 214013, 2531011, 0x10000 end--from MVC
+ end
+ temp:randomseed(s)
+ return temp
+end
+
+-- Multiply-with-carry
+local multiply_with_carry = {}
+multiply_with_carry.__index = multiply_with_carry
+
+function multiply_with_carry:random(a, b)
+ local m = self.m
+ local t = self.a * self.x + self.c
+ local y = t % m
+ self.x = y
+ self.c = math_floor(t / m)
+ if not a then return y / 0x10000
+ elseif not b then
+ if a == 0 then return y
+ else return 1 + (y % a) end
+ else
+ return a + (y % (b - a + 1))
+ end
+end
+
+function multiply_with_carry:randomseed(s)
+ if not s then s = seed() end
+ self.c = self.ic
+ self.x = normalize(s)
+end
+
+local function mwc(s, r)
+ local temp = {}
+ setmetatable(temp, multiply_with_carry)
+ temp.a, temp.c, temp.m = 1103515245, 12345, 0x10000 --from Ansi C
+ if r then
+ if r == 'nr' then temp.a, temp.c, temp.m = 1664525, 1013904223, 0x10000 --from Numerical Recipes.
+ elseif r == 'mvc' then temp.a, temp.c, temp.m = 214013, 2531011, 0x10000 end--from MVC
+ end
+ temp.ic = temp.c
+ temp:randomseed(s)
+ return temp
+end
+-- Little bind for the methods: My code starts
+local randomGen={}
+randomGen.__index=randomGen
+function randomGen:new(s)
+ local temp={}
+ setmetatable(temp,randomGen)
+ temp[1]=twister()
+ temp[2]=lcg()
+ temp[3]=mwc()
+ temp.pos=1
+ for i=1,3 do
+ temp[i]:randomseed(s)
+ end
+ return temp
+end
+function randomGen:randomseed(s)
+ self.pos=1
+ self[1]:randomseed(s)
+ self[2]:randomseed(s)
+ self[3]:randomseed(s)
+end
+function randomGen:randomInt(a,b)
+ local t=self[self.pos]:random(a,b)
+ self.pos=self.pos+1
+ if self.pos>3 then
+ self.pos=1
+ end
+ return t
+end
+function randomGen:newND(a,b,s)
+ if not(a) or not(b) then error('You must include a range!') end
+ local temp=randomGen:new(s)
+ temp.a=a
+ temp.b=b
+ temp.range=b-a+1
+ temp.dups={no=0}
+ function temp:nextInt()
+ local t=self:randomInt(self.a,self.b)
+ if self.dups[t]==nil then
+ self.dups[t]=true
+ self.dups.no=self.dups.no+1
+ else
+ return self:nextInt()
+ end
+ if self.dups.no==self.range then
+ function self:nextInt()
+ return 1,true
+ end
+ return t
+ else
+ return t
+ end
+ end
+ function temp:nextIInt()
+ return function() return self:nextInt() end
+ end
+ return temp
+end
+return randomGen
diff --git a/examples/love2d Threading Example/bin/support/extraBlocks.lua b/examples/love2d Threading Example/bin/support/extraBlocks.lua
new file mode 100644
index 0000000..2b8aa3b
--- /dev/null
+++ b/examples/love2d Threading Example/bin/support/extraBlocks.lua
@@ -0,0 +1,146 @@
+local __CURRENTVERSION=2
+bin.registerBlock("t",function(SIZE_OR_NIL,ref)
+ local header=ref:read(3)
+ if not header:match("(LT.)") then error("Not a valid table struct!") end
+ if bin.defualtBit.new(header:sub(3,3)):tonumber(1)>__CURRENTVERSION then error("Incompatible Version of LuaTable!") end
+ local len=ref:getBlock("n",4) -- hehe lets make life easier
+ local tab={}
+ local ind
+ local n=0
+ while true do
+ local _dat=ref:read(2)
+ if _dat==nil then break end
+ local it,dt=_dat:match("(.)(.)")
+ n=n+2
+ if it=="N" then -- get the index stuff out of the way first
+ ind=ref:getBlock("n",4)
+ n=n+4
+ else
+ indL=ref:getBlock("n",1)
+ n=n+1+indL
+ ind=ref:read(indL)
+ end
+ if dt=="N" then
+ tab[ind]=ref:getBlock("d")
+ n=n+8
+ elseif dt=="I" then
+ tab[ind]=math.huge
+ ref:getBlock("n",4)
+ n=n+4
+ elseif dt=="i" then
+ tab[ind]=-math.huge
+ ref:getBlock("n",4)
+ n=n+4
+ elseif dt=="S" then
+ local nn=ref:getBlock("n",4)
+ tab[ind]=ref:read(nn)
+ n=n+4+nn
+ elseif dt=="B" then
+ tab[ind]=({["\255"]=true,["\0"]=false})[ref:read(1)]
+ n=n+1
+ elseif dt=="F" then
+ local nn=ref:getBlock("n",4)
+ tab[ind]=loadstring(ref:read(nn))
+ n=n+4+nn
+ elseif dt=="T" then
+ local cur=ref:getSeek()
+ local size=ref:getBlock("n",4)
+ ref:setSeek(cur)
+ ref:read(4)
+ if size==7 then
+ tab[ind]={}
+ ref:read(7)
+ n=n+11
+ else
+ local data=bin.new(ref:read(size))
+ local dat=data:getBlock("t")
+ if dat.__RECURSIVE then
+ tab[ind]=tab
+ else
+ tab[ind]=dat
+ end
+ n=n+data:getSize()+4
+ end
+ end
+ if n==len then break end
+ end
+ return bin.resolveType(tab)
+end,function(d,fit,fmt,self,rec,tabsaw)
+ -- INGORE FIT WE ARE CREATING A STRUCT!!!
+ -- fmt will apply to all numbers
+ local __rem=nil
+ if not tabsaw then rem=true end
+ local tabsaw=tabsaw or {}
+ if rem then
+ table.insert(tabsaw,d)
+ end
+ local bData={}
+ for i,v in pairs(d) do -- this is for tables, all but userdata is fine. Depending on where you are using lua functions may or may not work
+ local tp=type(v):sub(1,1):upper() -- uppercase of datatype
+ if type(i)=="number" then -- Lets handle indexies
+ if v==math.huge then
+ tp="I"
+ v=0
+ elseif v==-math.huge then
+ tp="i"
+ v=0
+ end
+ table.insert(bData,"N"..tp..bin.defualtBit.numToBytes(i,4)) -- number index?
+ elseif type(i)=="string" then
+ if #i>255 then error("A string index cannot be larger than 255 bytes!") end
+ table.insert(bData,"S"..tp..bin.defualtBit.numToBytes(#i,1)..i) -- string index?
+ else
+ error("Only numbers and strings can be a table index!") -- throw error?
+ end
+ if type(v)=="number" then
+ -- How do we handle number data
+ local temp=bin.new()
+ temp:addBlock(v,nil,"d")
+ table.insert(bData,temp.data)
+ elseif type(v)=="string" then
+ -- Lets work on strings
+ table.insert(bData,bin.defualtBit.numToBytes(#v,4)) -- add length of string
+ table.insert(bData,v) -- add string
+ elseif type(v)=="boolean" then -- bools are easy :D
+ table.insert(bData,({[true]="\255",[false]="\0"})[v])
+ elseif type(v)=="function" then -- should we allow this? why not...
+ local dump=string.dump(v)
+ table.insert(bData,bin.defualtBit.numToBytes(#dump,4)) -- add length of dumped string
+ table.insert(bData,dump) -- add it
+ elseif type(v)=="table" then -- tables...
+ if tabsaw[1]==v then
+ v={__RECURSIVE=i}
+ else
+ tabsaw[i]=v
+ end
+ local data=rec(v,nil,"t",self,rec,tabsaw)
+ table.insert(bData,bin.defualtBit.numToBytes(#data,4)) -- add length of string
+ table.insert(bData,data) -- add string
+ end
+ end
+ local data=table.concat(bData)
+ return "LT"..string.char(__CURRENTVERSION)..bin.defualtBit.numToBytes(#data,4)..data
+end)
+bin.registerBlock("b",function(SIZE_OR_NIL,ref)
+ return ({["\255"]=true,["\0"]=false})[ref:read(1)]
+end,function(d)
+ return ({[true]="\255",[false]="\0"})[d]
+end)
+bin.registerBlock("f",function(SIZE_OR_NIL,ref)
+ local nn=ref:getBlock("n",4)
+ return loadstring(ref:read(nn))
+end,function(d)
+ local dump=string.dump(d)
+ return bin.defualtBit.numToBytes(#dump,4)..dump
+end)
+bin.registerBlock("d",function(SIZE_OR_NIL,ref)
+ local w,p=ref:getBlock("n",4),ref:getBlock("n",4)
+ p=tonumber("0."..tostring(p))
+ return w+p
+end,function(d,fit,fmt,self,rec,tabsaw)
+ local w,p = toFraction(d)
+ local temp=bin.new()
+ temp:addBlock(w,4)
+ temp:addBlock(p,4)
+ return temp.data
+end)
\ No newline at end of file
diff --git a/examples/love2d Threading Example/bin/support/utils.lua b/examples/love2d Threading Example/bin/support/utils.lua
new file mode 100644
index 0000000..cc6efa6
--- /dev/null
+++ b/examples/love2d Threading Example/bin/support/utils.lua
@@ -0,0 +1,127 @@
+function table.print(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)
+ table.print(v, indent+1)
+ elseif type(v) == 'boolean' then
+ print(formatting .. tostring(v))
+ else
+ print(formatting .. tostring(v))
+ end
+ end
+end
+function table.flip(t)
+ local tt={}
+ for i,v in pairs(t) do
+ tt[v]=i
+ end
+ return tt
+end
+function toFraction(n)
+ local w,p=math.modf(n)
+ if p~=0 then
+ p=tonumber(tostring(p):sub(3))
+ end
+ return w,p
+end
+function io.cleanName(name)
+ name=name:gsub("\\","")
+ name=name:gsub("/","")
+ name=name:gsub(":","")
+ name=name:gsub("*","")
+ name=name:gsub("%?","")
+ name=name:gsub("\"","''")
+ name=name:gsub("<","")
+ name=name:gsub(">","")
+ name=name:gsub("|","")
+ return name
+end
+function math.numfix(n,x)
+ local str=tostring(n)
+ if #str",a,b)
+ test[a]=b
+end,1)
\ No newline at end of file
diff --git a/examples/love2d Threading Example/main7.lua b/examples/love2d Threading Example/main7.lua
new file mode 100644
index 0000000..8ed71a6
--- /dev/null
+++ b/examples/love2d Threading Example/main7.lua
@@ -0,0 +1,69 @@
+require("core.Library")
+GLOBAL,sThread=require("multi.integration.loveManager").init() -- load the love2d version of the lanesManager and requires the entire multi library
+--IMPORTANT
+-- Do not make the above local, this is the one difference that the lanesManager does not have
+-- If these are local the functions will have the upvalues put into them that do not exist on the threaded side
+-- You will need to ensure that the function does not refer to any upvalues in its code. It will print an error if it does though
+-- Also each thread has a .1 second delay! This is used to generate a random values for each thread!
+require("core.GuiManager") -- allows the use of graphics in the program.
+gui.ff.Color=Color.Black
+function comma_value(amount)
+ local formatted = amount
+ while true do
+ formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
+ if (k==0) then
+ break
+ end
+ end
+ return formatted
+end
+multi:newSystemThread("test1",function() -- Another difference is that the multi library is already loaded in the threaded enviroment as well as a call to multi:mainloop()
+ multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 1"):OnBench(function(self,c) GLOBAL["T1"]=c multi:Stop() end)
+end)
+multi:newSystemThread("test2",function() -- spawns a thread in another lua process
+ multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 2"):OnBench(function(self,c) GLOBAL["T2"]=c multi:Stop() end)
+end)
+multi:newSystemThread("test3",function() -- spawns a thread in another lua process
+ multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 3"):OnBench(function(self,c) GLOBAL["T3"]=c multi:Stop() end)
+end)
+multi:newSystemThread("test4",function() -- spawns a thread in another lua process
+ multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 4"):OnBench(function(self,c) GLOBAL["T4"]=c multi:Stop() end)
+end)
+multi:newSystemThread("test5",function() -- spawns a thread in another lua process
+ multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 5"):OnBench(function(self,c) GLOBAL["T5"]=c multi:Stop() end)
+end)
+multi:newSystemThread("test6",function() -- spawns a thread in another lua process
+ multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 6"):OnBench(function(self,c) GLOBAL["T6"]=c multi:Stop() end)
+end)
+multi:newSystemThread("Combiner",function() -- spawns a thread in another lua process
+ function comma_value(amount)
+ local formatted = amount
+ while true do
+ formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
+ if (k==0) then
+ break
+ end
+ end
+ return formatted
+ end
+ local b=comma_value(tostring(sThread.waitFor("T1")+sThread.waitFor("T2")+sThread.waitFor("T3")+sThread.waitFor("T4")+sThread.waitFor("T5")+sThread.waitFor("T6")))
+ GLOBAL["DONE"]=b
+end)
+multi:newThread("test0",function()
+ -- sThread.waitFor("DONE") -- lets hold the main thread completely so we don't eat up cpu
+ -- os.exit()
+ -- when the main thread is holding there is a chance that error handling on the system threads may not work!
+ -- instead we can do this
+ while true do
+ thread.skip(1) -- allow error handling to take place... Otherwise lets keep the main thread running on the low
+ -- Before we held just because we could... But this is a game and we need to have logic continue
+ --sThreadM.sleep(.001) -- Sleeping for .001 is a greeat way to keep cpu usage down. Make sure if you aren't doing work to rest. Abuse the hell out of GLOBAL if you need to :P
+ if GLOBAL["DONE"] then
+ t.text="Bench: "..GLOBAL["DONE"]
+ end
+ end
+end)
+GLOBAL["Bench"]=3
+t=gui:newTextLabel("no done yet!",0,0,300,100)
+t:centerX()
+t:centerY()
diff --git a/examples/love2d Threading Example/multi/compat/love2d.lua b/examples/love2d Threading Example/multi/compat/love2d.lua
index cc4015d..33451c0 100644
--- a/examples/love2d Threading Example/multi/compat/love2d.lua
+++ b/examples/love2d Threading Example/multi/compat/love2d.lua
@@ -33,43 +33,50 @@ function love.run()
if love.load then love.load(arg) end
if love.timer then love.timer.step() end
local dt = 0
- while true do
- -- Process events.
- if love.event then
- love.event.pump()
- for e,a,b,c,d in love.event.poll() do
- if e == "quit" then
- if not love.quit or not love.quit() then
- if love.audio then
- love.audio.stop()
+ local breakme=false
+ multi:newThread("MAIN-RUN",function()
+ while true do
+ -- Process events.
+ if love.event then
+ love.event.pump()
+ for name, a,b,c,d,e,f in love.event.poll() do
+ if name == "quit" then
+ if not love.quit or not love.quit() then
+ breakme=true
+ thread.kill()
+ break
end
- return
end
+ love.handlers[name](a,b,c,d,e,f)
end
- love.handlers[e](a,b,c,d)
end
+ if love.timer then
+ love.timer.step()
+ dt = love.timer.getDelta()
+ end
+ if love.update then love.update(dt) end
+ if love.window and love.graphics and love.window.isCreated() then
+ love.graphics.clear()
+ love.graphics.origin()
+ if love.draw then love.draw() end
+ multi.dManager()
+ love.graphics.setColor(255,255,255,255)
+ if multi.draw then multi.draw() end
+ love.graphics.present()
+ end
+ thread.sleep()
end
- if love.timer then
- love.timer.step()
- dt = love.timer.getDelta()
- end
- if love.update then love.update(dt) end
+ end)
+ while not breakme do
+ love.timer.sleep(.005)
+ multi:uManager(dt)
if multi.boost then
for i=1,multi.boost-1 do
multi:uManager(dt)
end
end
- multi:uManager(dt)
- if love.window and love.graphics and love.window.isCreated() then
- love.graphics.clear()
- love.graphics.origin()
- if love.draw then love.draw() end
- multi.dManager()
- love.graphics.setColor(255,255,255,255)
- if multi.draw then multi.draw() end
- love.graphics.present()
- end
end
+ return
end
multi.drawF={}
function multi:dManager()
@@ -81,34 +88,34 @@ function multi:onDraw(func,i)
i=i or 1
table.insert(self.drawF,i,func)
end
-function multi:lManager()
- if love.event then
- love.event.pump()
- for e,a,b,c,d in love.event.poll() do
- if e == "quit" then
- if not love.quit or not love.quit() then
- if love.audio then
- love.audio.stop()
- end
- return nil
- end
- end
- love.handlers[e](a,b,c,d)
- end
- end
- if love.timer then
- love.timer.step()
- dt = love.timer.getDelta()
- end
- if love.update then love.update(dt) end
- multi:uManager(dt)
- if love.window and love.graphics and love.window.isCreated() then
- love.graphics.clear()
- love.graphics.origin()
- if love.draw then love.draw() end
- multi.dManager()
- love.graphics.setColor(255,255,255,255)
- if multi.draw then multi.draw() end
- love.graphics.present()
- end
-end
+--~ function multi:lManager()
+--~ if love.event then
+--~ love.event.pump()
+--~ for e,a,b,c,d in love.event.poll() do
+--~ if e == "quit" then
+--~ if not love.quit or not love.quit() then
+--~ if love.audio then
+--~ love.audio.stop()
+--~ end
+--~ return nil
+--~ end
+--~ end
+--~ love.handlers[e](a,b,c,d)
+--~ end
+--~ end
+--~ if love.timer then
+--~ love.timer.step()
+--~ dt = love.timer.getDelta()
+--~ end
+--~ if love.update then love.update(dt) end
+--~ multi:uManager(dt)
+--~ if love.window and love.graphics and love.window.isCreated() then
+--~ love.graphics.clear()
+--~ love.graphics.origin()
+--~ if love.draw then love.draw() end
+--~ multi.dManager()
+--~ love.graphics.setColor(255,255,255,255)
+--~ if multi.draw then multi.draw() end
+--~ love.graphics.present()
+--~ end
+--~ end
diff --git a/examples/love2d Threading Example/multi/init.lua b/examples/love2d Threading Example/multi/init.lua
index 1c98dfa..e7c44a3 100644
--- a/examples/love2d Threading Example/multi/init.lua
+++ b/examples/love2d Threading Example/multi/init.lua
@@ -21,6 +21,63 @@ 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.
]]
+require("bin")
+multi = {}
+multi.Version = "1.9.2"
+multi._VERSION = "1.9.2"
+multi.stage = "mostly-stable"
+multi.__index = multi
+multi.Mainloop = {}
+multi.Tasks = {}
+multi.Tasks2 = {}
+multi.Garbage = {}
+multi.ender = {}
+multi.Children = {}
+multi.Paused = {}
+multi.Active = true
+multi.fps = 60
+multi.Id = -1
+multi.Type = "mainprocess"
+multi.Rest = 0
+multi._type = type
+multi.Jobs = {}
+multi.queue = {}
+multi.jobUS = 2
+multi.clock = os.clock
+multi.time = os.time
+multi.LinkedPath = multi
+multi.isRunning = false
+--Do not change these ever...Any other number will not work (Unless you are using enablePriority2())
+multi.Priority_Core = 1
+multi.Priority_High = 4
+multi.Priority_Above_Normal = 16
+multi.Priority_Normal = 64
+multi.Priority_Below_Normal = 256
+multi.Priority_Low = 1024
+multi.Priority_Idle = 4096
+multi.PStep = 1
+multi.PList={multi.Priority_Core,multi.Priority_High,multi.Priority_Above_Normal,multi.Priority_Normal,multi.Priority_Below_Normal,multi.Priority_Low,multi.Priority_Idle}
+--^^^^
+multi.PriorityTick=1 -- Between 1, 2 and 4
+multi.Priority=multi.Priority_Core
+multi.threshold=256
+multi.threstimed=.001
+function multi.queuefinal(self)
+ self:Destroy()
+ if self.Parent.Mainloop[#self.Parent.Mainloop] then
+ if self.Parent.Mainloop[#self.Parent.Mainloop].Type=="alarm" then
+ self.Parent.Mainloop[#self.Parent.Mainloop]:Reset()
+ self.Parent.Mainloop[#self.Parent.Mainloop].Active=true
+ else
+ self.Parent.Mainloop[#self.Parent.Mainloop]:Resume()
+ end
+ else
+ for i=1,#self.Parent.funcE do
+ self.Parent.funcE[i](self)
+ end
+ self.Parent:Remove()
+ end
+end
if table.unpack then
unpack=table.unpack
end
@@ -44,83 +101,19 @@ function print(...)
_print(...)
end
end
-multi = {}
-multi.Version="1.8.6"
-multi._VERSION="1.8.6"
-multi.stage='mostly-stable'
-multi.__index = multi
-multi.Mainloop={}
-multi.Tasks={}
-multi.Tasks2={}
-multi.Garbage={}
-multi.ender={}
-multi.Children={}
-multi.Paused={}
-multi.Active=true
-multi.fps=60
-multi.Id=-1
-multi.Type='mainprocess'
-multi.Rest=0
-multi._type=type
-multi.Jobs={}
-multi.queue={}
-multi.jobUS=2
-multi.clock=os.clock
-multi.time=os.time
-multi.LinkedPath=multi
-multi.isRunning=false
-multi.queuefinal=function(self)
- self:Destroy()
- if self.Parent.Mainloop[#self.Parent.Mainloop] then
- if self.Parent.Mainloop[#self.Parent.Mainloop].Type=="alarm" then
- self.Parent.Mainloop[#self.Parent.Mainloop]:Reset()
- self.Parent.Mainloop[#self.Parent.Mainloop].Active=true
- else
- self.Parent.Mainloop[#self.Parent.Mainloop]:Resume()
- end
- else
- for i=1,#self.Parent.funcE do
- self.Parent.funcE[i](self)
- end
- self.Parent:Remove()
+_write=io.write
+function io.write(...)
+ if not __SUPPRESSWRITES then
+ _write(...)
end
end
---Do not change these ever...Any other number will not work (Unless you are using enablePriority2() then change can be made. Just ensure that Priority_Idle is the greatest and Priority_Core is 1!)
-multi.Priority_Core=1
-multi.Priority_High=4
-multi.Priority_Above_Normal=16
-multi.Priority_Normal=64
-multi.Priority_Below_Normal=256
-multi.Priority_Low=1024
-multi.Priority_Idle=4096
-multi.PList={multi.Priority_Core,multi.Priority_High,multi.Priority_Above_Normal,multi.Priority_Normal,multi.Priority_Below_Normal,multi.Priority_Low,multi.Priority_Idle}
-multi.PStep=1
---^^^^
-multi.PriorityTick=1 -- Between 1 and 4 any greater and problems arise
-multi.Priority=multi.Priority_Core
-multi.threshold=256
-multi.threstimed=.001
-function multi:setThreshold(n)
- self.threshold=n or 120
-end
function multi:setThrestimed(n)
- self.threstimed=n or .001
+ self.deltaTarget=n or .1
end
function multi:getLoad()
- return multi:newFunction(function(self)
- multi.scheduler:Pause()
- local sample=#multi.Mainloop
- local FFloadtest=0
- multi:benchMark(multi.threstimed):OnBench(function(_,l3) FFloadtest=l3*(1/multi.threstimed) end)
- self:hold(function() return FFloadtest~=0 end)
- local val=FFloadtest/sample
- multi.scheduler:Resume()
- if val>multi.threshold then
- return 0
- else
- return 100-((val/multi.threshold)*100)
- end
- end)()
+ if multi.load_updater:isPaused() then multi.load_updater:Resume() return 0 end
+ local val = math.abs(self.dStepA-self.dStepB)/multi.deltaTarget*100
+ if val > 100 then return 100 else return val end
end
function multi:setDomainName(name)
self[name]={}
@@ -169,6 +162,14 @@ else
os.execute('sleep ' .. tonumber(n))
end
end
+function multi.randomString(n)
+ local str = ''
+ local strings = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','1','2','3','4','5','6','7','8','9','0','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'}
+ for i=1,n do
+ str = str..''..strings[math.random(1,#strings)]
+ end
+ return str
+end
function multi:getParentProcess()
return self.Mainloop[self.CID]
end
@@ -194,6 +195,7 @@ multi.Condition=multi.condition
function multi:isHeld()
return self.held
end
+multi.important={}
multi.IsHeld=multi.isHeld
function multi.executeFunction(name,...)
if type(_G[name])=='function' then
@@ -213,19 +215,44 @@ end
multi.WaitFor=multi.waitFor
function multi:reboot(r)
local before=collectgarbage('count')
- self.Mainloop={}
- self.Tasks={}
- self.Tasks2={}
- self.Garbage={}
- self.Children={}
- self.Paused={}
- self.Active=true
- self.Id=-1
+ multi.Mainloop={}
+ multi.Tasks={}
+ multi.Tasks2={}
+ multi.Garbage={}
+ multi.ender={}
+ multi.Children={}
+ multi.Paused={}
+ multi.Active=true
+ multi.fps=60
+ multi.Id=-1
+ multi.Type='mainprocess'
+ multi.Rest=0
+ multi._type=type
+ multi.Jobs={}
+ multi.queue={}
+ multi.jobUS=2
+ multi.clock=os.clock
+ multi.time=os.time
+ multi.LinkedPath=multi
+ multi.isRunning=false
+ multi.Priority_Core=1
+ multi.Priority_High=4
+ multi.Priority_Above_Normal=16
+ multi.Priority_Normal=64
+ multi.Priority_Below_Normal=256
+ multi.Priority_Low=1024
+ multi.Priority_Idle=4096
+ multi.PList={multi.Priority_Core,multi.Priority_High,multi.Priority_Above_Normal,multi.Priority_Normal,multi.Priority_Below_Normal,multi.Priority_Low,multi.Priority_Idle}
+ multi.PStep=1
+ multi.PriorityTick=1
+ multi.Priority=multi.Priority_Core
+ multi.threshold=256
+ multi.threstimed=.001
if r then
for i,v in pairs(_G) do
if type(i)=='table' then
if i.Parent and i.Id and i.Act then
- i={}
+ _G[i]={}
end
end
end
@@ -279,20 +306,18 @@ function multi:enablePriority()
_G.ID=0
local PS=self
for _D=#Loop,1,-1 do
- if Loop[_D] then
- if (PS.PList[PS.PStep])%Loop[_D].Priority==0 then
- if Loop[_D].Active then
- Loop[_D].Id=_D
- self.CID=_D
- Loop[_D]:Act()
+ for P=1,7 do
+ if Loop[_D] then
+ if (PS.PList[P])%Loop[_D].Priority==0 then
+ if Loop[_D].Active then
+ Loop[_D].Id=_D
+ self.CID=_D
+ Loop[_D]:Act()
+ end
end
end
end
end
- PS.PStep=PS.PStep+1
- if PS.PStep>7 then
- PS.PStep=1
- end
end
end
function multi:enablePriority2()
@@ -318,45 +343,6 @@ function multi:enablePriority2()
end
end
multi.disablePriority=multi.unProtect
-function multi:fromfile(path,int)
- int=int or self
- local test2={}
- local test=bin.load(path)
- local tp=test:getBlock('s')
- if tp=='event' then
- test2=int:newEvent(test:getBlock('f'))
- local t=test:getBlock('t')
- for i=1,#t do
- test2:OnEvent(t[i])
- end
- elseif tp=='alarm' then
- test2=int:newAlarm(test:getBlock('n'))
- elseif tp=='loop' then
- test2=int:newLoop(test:getBlock('t')[1])
- elseif tp=='step' or tp=='tstep' then
- local func=test:getBlock('t')
- local funcE=test:getBlock('t')
- local funcS=test:getBlock('t')
- local tab=test:getBlock('t')
- test2=int:newStep()
- table.merge(test2,tab)
- test2.funcE=funcE
- test2.funcS=funcS
- test2.func=func
- elseif tp=='trigger' then
- test2=int:newTrigger(test:getBlock('f'))
- elseif tp=='connector' then
- test2=int:newConnection()
- test2.func=test:getBlock('t')
- elseif tp=='timer' then
- test2=int:newTimer()
- test2.count=tonumber(test:getBlock('n'))
- else
- print('Error: The file you selected is not a valid multi file object!')
- return false
- end
- return test2
-end
function multi:benchMark(sec,p,pt)
local temp=self:newLoop(function(self,t)
if self.clock()-self.init>self.sec then
@@ -379,21 +365,6 @@ function multi:benchMark(sec,p,pt)
temp.c=0
return temp
end
-function multi:tofile(path)
- local items=self:getChildren()
- io.mkDir(io.getName(path))
- for i=1,#items do
- items[i]:tofile(io.getName(path)..'\\item'..item[i]..'.dat')
- end
- local int=bin.new()
- int:addBlock('process')
- int:addBlock(io.getName(path))
- int:addBlock(#self.Mainloop)
- int:addBlock(self.Active)
- int:addBlock(self.Rest)
- int:addBlock(self.Jobs)
- int:tofile()
-end
function multi.startFPSMonitior()
if not multi.runFPS then
multi.doFPS(s)
@@ -407,6 +378,12 @@ function multi.doFPS(s)
end
end
--Helpers
+function multi.timer(func,...)
+ local timer=multi:newTimer()
+ timer:Start()
+ args={func(...)}
+ return timer:Get(),unpack(args)
+end
function multi:IsAnActor()
return ({watcher=true,tstep=true,step=true,updater=true,loop=true,alarm=true,event=true})[self.Type]
end
@@ -685,6 +662,7 @@ function multi:newBase(ins)
c.funcTM={}
c.funcTMR={}
c.ender={}
+ c.important={}
c.Id=0
c.PId=0
c.Act=function() end
@@ -747,6 +725,7 @@ function multi:newProcess(file)
loadstring('local process=multi.Cself '..io.open(file,'rb'):read('*all'))()
end
self:create(c)
+--~ c:IngoreObject()
return c
end
function multi:newQueuer(file)
@@ -795,7 +774,7 @@ function multi:newTimer()
return (os.clock()-self.time)+self.count
end
function c:isPaused()
- return c.paused
+ return self.paused
end
c.Reset=c.Start
function c:Pause()
@@ -818,7 +797,8 @@ function multi:newTimer()
end
function multi:newConnection(protect)
local c={}
- setmetatable(c,{__call=function(self,...) self:connect(...) end})
+ c.Parent=self
+ setmetatable(c,{__call=function(self,...) return self:connect(...) end})
c.Type='connector'
c.func={}
c.ID=0
@@ -826,15 +806,32 @@ function multi:newConnection(protect)
c.connections={}
c.fconnections={}
c.FC=0
+ function c:holdUT(n)
+ local n=n or 0
+ self.waiting=true
+ local count=0
+ local id=self:connect(function()
+ count = count + 1
+ if n<=count then
+ self.waiting=false
+ end
+ end)
+ repeat
+ self.Parent:uManager()
+ until self.waiting==false
+ id:Destroy()
+ end
+ c.HoldUT=c.holdUT
function c:fConnect(func)
local temp=self:connect(func)
table.insert(self.fconnections,temp)
self.FC=self.FC+1
end
+ c.FConnect=c.fConnect
function c:getConnection(name,ingore)
if ingore then
return self.connections[name] or {
- Fire=function() end -- if the connection doesn't exist lets call all of them or silently ingore
+ Fire=function() end -- if the connection doesn't exist lets call all of them or silently ignore
}
else
return self.connections[name] or self
@@ -898,14 +895,16 @@ function multi:newConnection(protect)
end
end
end
- end
+ end,
}
+ temp.Destroy=temp.Remove
if name then
self.connections[name]=temp
end
return temp
end
c.Connect=c.connect
+ c.GetConnection=c.getConnection
function c:tofile(path)
local m=bin.new()
m:addBlock(self.Type)
@@ -1152,14 +1151,6 @@ function multi:newEvent(task)
function c:OnEvent(func)
table.insert(self.func,func)
end
- function c:tofile(path)
- local m=bin.new()
- m:addBlock(self.Type)
- m:addBlock(self.Task)
- m:addBlock(self.func)
- m:addBlock(self.Active)
- m:tofile(path)
- end
self:create(c)
return c
end
@@ -1177,7 +1168,7 @@ function multi:newUpdater(skip)
end
self.pos=self.pos+1
end
- function c:setSkip(n)
+ function c:SetSkip(n)
self.skip=n
end
c.OnUpdate=self.OnMainConnect
@@ -1190,13 +1181,6 @@ function multi:newAlarm(set)
c.Priority=self.Priority_Low
c.timer=self:newTimer()
c.set=set or 0
- function c:tofile(path)
- local m=bin.new()
- m:addBlock(self.Type)
- m:addBlock(self.set)
- m:addBlock(self.Active)
- m:tofile(path)
- end
function c:Act()
if self.timer:Get()>=self.set then
self:Pause()
@@ -1232,13 +1216,6 @@ function multi:newLoop(func)
if func then
c.func={func}
end
- function c:tofile(path)
- local m=bin.new()
- m:addBlock(self.Type)
- m:addBlock(self.func)
- m:addBlock(self.Active)
- m:tofile(path)
- end
function c:Act()
for i=1,#self.func do
self.func[i](self,self.Parent.clock()-self.Start)
@@ -1285,16 +1262,6 @@ function multi:newStep(start,reset,count,skip)
think=-1
end
end
- function c:tofile(path)
- local m=bin.new()
- m:addBlock(self.Type)
- m:addBlock(self.func)
- m:addBlock(self.funcE)
- m:addBlock(self.funcS)
- m:addBlock({pos=self.pos,endAt=self.endAt,skip=self.skip,spos=self.spos,count=self.count,start=self.start})
- m:addBlock(self.Active)
- m:tofile(path)
- end
function c:Act()
if self~=nil then
if self.spos==0 then
@@ -1356,13 +1323,6 @@ function multi:newTLoop(func,set)
if func then
c.func={func}
end
- function c:tofile(path)
- local m=bin.new()
- m:addBlock(self.Type)
- m:addBlock(self.func)
- m:addBlock(self.Active)
- m:tofile(path)
- end
function c:Act()
if self.timer:Get()>=self.set then
self.life=self.life+1
@@ -1393,12 +1353,6 @@ function multi:newTrigger(func)
function c:Fire(...)
self:trigfunc(...)
end
- function c:tofile(path)
- local m=bin.new()
- m:addBlock(self.Type)
- m:addBlock(self.trigfunc)
- m:tofile(path)
- end
self:create(c)
return c
end
@@ -1426,16 +1380,6 @@ function multi:newTStep(start,reset,count,set)
self.timer=self.clock()
self:Resume()
end
- function c:tofile(path)
- local m=bin.new()
- m:addBlock(self.Type)
- m:addBlock(self.func)
- m:addBlock(self.funcE)
- m:addBlock(self.funcS)
- m:addBlock({pos=self.pos,endAt=self.endAt,skip=self.skip,timer=self.timer,count=self.count,start=self.start,set=self.set})
- m:addBlock(self.Active)
- m:tofile(path)
- end
function c:Act()
if self.clock()-self.timer>=self.set then
self:Reset()
@@ -1477,6 +1421,112 @@ function multi:newTStep(start,reset,count,set)
self:create(c)
return c
end
+function multi:newTimeStamper()
+ local c=self:newBase()
+ c.Type='timestamper'
+ c.Priority=self.Priority_Idle
+ c.hour = {}
+ c.minute = {}
+ c.second = {}
+ c.time = {}
+ c.day = {}
+ c.month = {}
+ c.year = {}
+ function c:Act()
+ for i=1,#self.hour do
+ if self.hour[i][1]==os.date("%H") and self.hour[i][3] then
+ self.hour[i][2](self)
+ self.hour[i][3]=false
+ elseif self.hour[i][1]~=os.date("%H") and not self.hour[i][3] then
+ self.hour[i][3]=true
+ end
+ end
+ for i=1,#self.minute do
+ if self.minute[i][1]==os.date("%M") and self.minute[i][3] then
+ self.minute[i][2](self)
+ self.minute[i][3]=false
+ elseif self.minute[i][1]~=os.date("%M") and not self.minute[i][3] then
+ self.minute[i][3]=true
+ end
+ end
+ for i=1,#self.second do
+ if self.second[i][1]==os.date("%S") and self.second[i][3] then
+ self.second[i][2](self)
+ self.second[i][3]=false
+ elseif self.second[i][1]~=os.date("%S") and not self.second[i][3] then
+ self.second[i][3]=true
+ end
+ end
+ for i=1,#self.day do
+ if type(self.day[i][1])=="string" then
+ if self.day[i][1]==os.date("%a") and self.day[i][3] then
+ self.day[i][2](self)
+ self.day[i][3]=false
+ elseif self.day[i][1]~=os.date("%a") and not self.day[i][3] then
+ self.day[i][3]=true
+ end
+ else
+ if string.format("%02d",self.day[i][1])==os.date("%d") and self.day[i][3] then
+ self.day[i][2](self)
+ self.day[i][3]=false
+ elseif string.format("%02d",self.day[i][1])~=os.date("%d") and not self.day[i][3] then
+ self.day[i][3]=true
+ end
+ end
+ end
+ for i=1,#self.month do
+ if self.month[i][1]==os.date("%m") and self.month[i][3] then
+ self.month[i][2](self)
+ self.month[i][3]=false
+ elseif self.month[i][1]~=os.date("%m") and not self.month[i][3] then
+ self.month[i][3]=true
+ end
+ end
+ for i=1,#self.time do
+ if self.time[i][1]==os.date("%X") and self.time[i][3] then
+ self.time[i][2](self)
+ self.time[i][3]=false
+ elseif self.time[i][1]~=os.date("%X") and not self.time[i][3] then
+ self.time[i][3]=true
+ end
+ end
+ for i=1,#self.year do
+ if self.year[i][1]==os.date("%y") and self.year[i][3] then
+ self.year[i][2](self)
+ self.year[i][3]=false
+ elseif self.year[i][1]~=os.date("%y") and not self.year[i][3] then
+ self.year[i][3]=true
+ end
+ end
+ end
+ function c:OnTime(hour,minute,second,func)
+ if type(hour)=="number" then
+ self.time[#self.time+1]={string.format("%02d:%02d:%02d",hour,minute,second),func,true}
+ else
+ self.time[#self.time+1]={hour,minute,true}
+ end
+ end
+ function c:OnHour(hour,func)
+ self.hour[#self.hour+1]={string.format("%02d",hour),func,true}
+ end
+ function c:OnMinute(minute,func)
+ self.minute[#self.minute+1]={string.format("%02d",minute),func,true}
+ end
+ function c:OnSecond(second,func)
+ self.second[#self.second+1]={string.format("%02d",second),func,true}
+ end
+ function c:OnDay(day,func)
+ self.day[#self.day+1]={day,func,true}
+ end
+ function c:OnMonth(month,func)
+ self.month[#self.month+1]={string.format("%02d",month),func,true}
+ end
+ function c:OnYear(year,func)
+ self.year[#self.year+1]={string.format("%02d",year),func,true}
+ end
+ self:create(c)
+ return c
+end
function multi:newWatcher(namespace,name)
local function WatcherObj(ns,n)
if self.Type=='queue' then
@@ -1551,15 +1601,19 @@ function thread.testFor(name,val,sym)
thread.hold(function() return thread.get(name)~=nil end)
return thread.get(name)
end
-function multi:newTBase(ins)
+function multi:newTBase(name)
local c = {}
+ c.name=name
c.Active=true
c.func={}
c.ender={}
c.Id=0
c.PId=0
c.Parent=self
+ c.important={}
c.held=false
+ c.ToString=multi.ToString
+ c.ToFile=multi.ToFile
return c
end
function multi:newThread(name,func)
@@ -1673,17 +1727,10 @@ end)
multi.scheduler:Pause()
multi.OnError=multi:newConnection()
function multi:newThreadedAlarm(name,set)
- local c=self:newTBase()
+ local c=self:newTBase(name)
c.Type='alarmThread'
c.timer=self:newTimer()
c.set=set or 0
- function c:tofile(path)
- local m=bin.new()
- m:addBlock(self.Type)
- m:addBlock(self.set)
- m:addBlock(self.Active)
- m:tofile(path)
- end
function c:Resume()
self.rest=false
self.timer:Resume()
@@ -1722,7 +1769,7 @@ function multi:newThreadedAlarm(name,set)
return c
end
function multi:newThreadedUpdater(name,skip)
- local c=self:newTBase()
+ local c=self:newTBase(name)
c.Type='updaterThread'
c.pos=1
c.skip=skip or 1
@@ -1753,10 +1800,9 @@ function multi:newThreadedUpdater(name,skip)
return c
end
function multi:newThreadedTStep(name,start,reset,count,set)
- local c=self:newTBase()
+ local c=self:newTBase(name)
local think=1
c.Type='tstepThread'
- c.Priority=self.Priority_Low
c.start=start or 1
local reset = reset or math.huge
c.endAt=reset
@@ -1776,16 +1822,6 @@ function multi:newThreadedTStep(name,start,reset,count,set)
self.timer=os.clock()
self:Resume()
end
- function c:tofile(path)
- local m=bin.new()
- m:addBlock(self.Type)
- m:addBlock(self.func)
- m:addBlock(self.funcE)
- m:addBlock(self.funcS)
- m:addBlock({pos=self.pos,endAt=self.endAt,skip=self.skip,timer=self.timer,count=self.count,start=self.start,set=self.set})
- m:addBlock(self.Active)
- m:tofile(path)
- end
function c:Resume()
self.rest=false
end
@@ -1824,7 +1860,7 @@ function multi:newThreadedTStep(name,start,reset,count,set)
end
end
for i=1,#c.func do
- c.func[i](c.pos,c)
+ c.func[i](c,c.pos)
end
c.pos=c.pos+c.count
if c.pos-c.count==c.endAt then
@@ -1843,19 +1879,12 @@ function multi:newThreadedTStep(name,start,reset,count,set)
return c
end
function multi:newThreadedTLoop(name,func,n)
- local c=self:newTBase()
+ local c=self:newTBase(name)
c.Type='tloopThread'
c.restN=n or 1
if func then
c.func={func}
end
- function c:tofile(path)
- local m=bin.new()
- m:addBlock(self.Type)
- m:addBlock(self.func)
- m:addBlock(self.Active)
- m:tofile(path)
- end
function c:Resume()
self.rest=false
end
@@ -1884,7 +1913,7 @@ function multi:newThreadedTLoop(name,func,n)
return c
end
function multi:newThreadedStep(name,start,reset,count,skip)
- local c=self:newTBase()
+ local c=self:newTBase(name)
local think=1
c.Type='stepThread'
c.pos=start or 1
@@ -1900,16 +1929,6 @@ function multi:newThreadedStep(name,start,reset,count,skip)
think=-1
end
end
- function c:tofile(path)
- local m=bin.new()
- m:addBlock(self.Type)
- m:addBlock(self.func)
- m:addBlock(self.funcE)
- m:addBlock(self.funcS)
- m:addBlock({pos=self.pos,endAt=self.endAt,skip=self.skip,spos=self.spos,count=self.count,start=self.start})
- m:addBlock(self.Active)
- m:tofile(path)
- end
function c:Resume()
self.rest=false
end
@@ -1951,7 +1970,7 @@ function multi:newThreadedStep(name,start,reset,count,skip)
end
end
for i=1,#c.func do
- c.func[i](c.pos,c)
+ c.func[i](c,c.pos)
end
c.pos=c.pos+c.count
if c.pos-c.count==c.endAt then
@@ -2057,19 +2076,12 @@ function multi:newThreadedProcess(name)
return c
end
function multi:newThreadedLoop(name,func)
- local c=self:newTBase()
+ local c=self:newTBase(name)
c.Type='loopThread'
c.Start=os.clock()
if func then
c.func={func}
end
- function c:tofile(path)
- local m=bin.new()
- m:addBlock(self.Type)
- m:addBlock(self.func)
- m:addBlock(self.Active)
- m:tofile(path)
- end
function c:Resume()
self.rest=false
end
@@ -2098,20 +2110,12 @@ function multi:newThreadedLoop(name,func)
return c
end
function multi:newThreadedEvent(name,task)
- local c=self:newTBase()
+ local c=self:newTBase(name)
c.Type='eventThread'
c.Task=task or function() end
function c:OnEvent(func)
table.insert(self.func,func)
end
- function c:tofile(path)
- local m=bin.new()
- m:addBlock(self.Type)
- m:addBlock(self.Task)
- m:addBlock(self.func)
- m:addBlock(self.Active)
- m:tofile(path)
- end
function c:Resume()
self.rest=false
end
@@ -2139,3 +2143,259 @@ function multi:newThreadedEvent(name,task)
self:create(c)
return c
end
+-- State Saving Stuff
+function multi:IngoreObject()
+ self.Ingore=true
+end
+multi.scheduler:IngoreObject()
+function multi:ToString()
+ if self.Ingore then return end
+ local t=self.Type
+ local data;
+ print(t)
+ if t:sub(-6)=="Thread" then
+ data={
+ Type=t,
+ rest=self.rest,
+ updaterate=self.updaterest,
+ restrate=self.restrate,
+ name=self.name,
+ func=self.func,
+ important=self.important,
+ Active=self.Active,
+ ender=self.ender,
+ -- IDK if these need to be present...
+ -- Id=self.Id,
+ -- PId=self.PId,
+ held=self.held,
+ }
+ else
+ data={
+ Type=t,
+ func=self.func,
+ funcTM=self.funcTM,
+ funcTMR=self.funcTMR,
+ important=self.important,
+ ender=self.ender,
+ -- IDK if these need to be present...
+ -- Id=self.Id,
+ -- PId=self.PId,
+ held=self.held,
+ }
+ end
+ if t=="eventThread" or t=="event" then
+ table.merge(data,{
+ Task=self.Task,
+ })
+ elseif t=="loopThread" or t=="loop" then
+ table.merge(data,{
+ Start=self.Start,
+ })
+ elseif t=="stepThread" or t=="step" then
+ table.merge(data,{
+ funcE=self.funcE,
+ funcS=self.funcS,
+ pos=self.pos,
+ endAt=self.endAt,
+ start=self.start,
+ spos=self.spos,
+ skip=self.skip,
+ count=self.count,
+ })
+ elseif t=="tloopThread" then
+ table.merge(data,{
+ restN=self.restN,
+ })
+ elseif t=="tloop" then
+ table.merge(data,{
+ set=self.set,
+ life=self.life,
+ })
+ elseif t=="tstepThread" or t=="tstep" then
+ table.merge(data,{
+ funcE=self.funcE,
+ funcS=self.funcS,
+ pos=self.pos,
+ endAt=self.endAt,
+ start=self.start,
+ spos=self.spos,
+ skip=self.skip,
+ count=self.count,
+ timer=self.timer,
+ set=self.set,
+ reset=self.reset,
+ })
+ elseif t=="updaterThread" or t=="updater" then
+ table.merge(data,{
+ pos=self.pos,
+ skip=self.skip,
+ })
+ elseif t=="alarmThread" or t=="alarm" then
+ table.merge(data,{
+ set=self.set,
+ })
+ elseif t=="watcher" then
+ print("Currently cannot sterilize a watcher object!")
+ -- needs testing
+ -- table.merge(data,{
+ -- ns=self.ns,
+ -- n=self.n,
+ -- cv=self.cv,
+ -- })
+ elseif t=="timemaster" then
+ -- Weird stuff is going on here!
+ -- Need to do some testing
+ table.merge(data,{
+ timer=self.timer,
+ _timer=self._timer,
+ set=self.set,
+ link=self.link,
+ })
+ elseif t=="process" or t=="mainprocess" then
+ local loop=self.Mainloop
+ local dat={}
+ for i=1,#loop do
+ local ins=loop[i]:ToString()
+ if ins~=nil then
+ table.insert(dat,ins)
+ end
+ end
+ local str=bin.new()
+ str:addBlock({Type=t})
+ str:addBlock(#dat,4,"n")
+ for i=1,#dat do
+ str:addBlock(#dat[i],4,"n")
+ str:addBlock(dat[i])
+ end
+ return str.data
+ end
+ for i,v in pairs(self.important) do
+ data[v]=self[v]
+ end
+ local str=bin.new()
+ str:addBlock(data)
+ return str.data
+end
+function multi:newFromString(str)
+ if type(str)=="table" then
+ if str.Type=="bin" then
+ str=str.data
+ end
+ end
+ local handle=bin.new(str)
+ local data=handle:getBlock("t")
+ local t=data.Type
+ if t=="mainprocess" then
+ local objs=handle:getBlock("n",4)
+ for i=1,objs do
+ self:newFromString(handle:getBlock("s",(handle:getBlock("n",4))))
+ end
+ return self
+ elseif t=="process" then
+ local temp=multi:newProcess()
+ local objs=handle:getBlock("n",4)
+ for i=1,objs do
+ temp:newFromString(handle:getBlock("s",(handle:getBlock("n",4))))
+ end
+ return temp
+ elseif t=="step" then -- GOOD
+ local item=self:newStep()
+ table.merge(item,data)
+ return item
+ elseif t=="tstep" then -- GOOD
+ local item=self:newTStep()
+ table.merge(item,data)
+ return item
+ elseif t=="tloop" then -- GOOD
+ local item=self:newTLoop()
+ table.merge(item,data)
+ return item
+ elseif t=="event" then -- GOOD
+ local item=self:newEvent(data.task)
+ table.merge(item,data)
+ return item
+ elseif t=="alarm" then -- GOOD
+ local item=self:newAlarm()
+ table.merge(item,data)
+ return item
+ elseif t=="watcher" then -- NEEDS TESTING
+ local item=self:newWatcher()
+ table.merge(item,data)
+ return item
+ elseif t=="updater" then -- GOOD
+ local item=self:newUpdater()
+ table.merge(item,data)
+ return item
+ elseif t=="loop" then -- GOOD
+ local item=self:newLoop()
+ table.merge(item,data)
+ return item
+ elseif t=="eventThread" then -- GOOD
+ local item=self:newThreadedEvent(data.name)
+ table.merge(item,data)
+ return item
+ elseif t=="loopThread" then -- GOOD
+ local item=self:newThreadedLoop(data.name)
+ table.merge(item,data)
+ return item
+ elseif t=="stepThread" then -- GOOD
+ local item=self:newThreadedStep(data.name)
+ table.merge(item,data)
+ return item
+ elseif t=="tloopThread" then -- GOOD
+ local item=self:newThreadedTLoop(data.name,nil,data.restN)
+ table.merge(item,data)
+ return item
+ elseif t=="tstepThread" then -- GOOD
+ local item=self:newThreadedTStep(data.name)
+ table.merge(item,data)
+ return item
+ elseif t=="updaterThread" then -- GOOD
+ local item=self:newThreadedUpdater(data.name)
+ table.merge(item,data)
+ return item
+ elseif t=="alarmThread" then -- GOOD
+ local item=self:newThreadedAlarm(data.name)
+ table.merge(item,data)
+ return item
+ end
+end
+function multi:Important(varname)
+ table.insert(important,varname)
+end
+function multi:ToFile(path)
+ bin.new(self:ToString()):tofile(path)
+end
+function multi:fromFile(path)
+ self:newFromString(bin.load(path))
+end
+function multi:SetStateFlag(opt)
+ --
+end
+function multi:quickStateSave(b)
+ --
+end
+function multi:saveState(path,opt)
+ --
+end
+function multi:loadState(path)
+ --
+end
+function multi:setDefualtStateFlag(opt)
+ --
+end
+multi.dStepA = 0
+multi.dStepB = 0
+multi.dSwap = 0
+multi.deltaTarget = .05
+multi.load_updater = multi:newUpdater(2)
+multi.load_updater:Pause()
+multi.load_updater:OnUpdate(function(self)
+ if self.Parent.dSwap == 0 then
+ self.Parent.dStepA = os.clock()
+ self.Parent.dSwap = 1
+ else
+ self.Parent.dSwap = 0
+ self.Parent.dStepB = os.clock()
+ end
+end)
diff --git a/examples/love2d Threading Example/multi/integration/lanesManager.lua b/examples/love2d Threading Example/multi/integration/lanesManager.lua
index a490ce8..023dd81 100644
--- a/examples/love2d Threading Example/multi/integration/lanesManager.lua
+++ b/examples/love2d Threading Example/multi/integration/lanesManager.lua
@@ -21,6 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]]
+package.path="?/init.lua;?.lua;"..package.path
function os.getOS()
if package.config:sub(1,1)=='\\' then
return 'windows'
@@ -32,9 +33,13 @@ end
lanes=require("lanes").configure()
--~ package.path="lua/?/init.lua;lua/?.lua;"..package.path
require("multi") -- get it all and have it on all lanes
+isMainThread=true
function multi:canSystemThread()
return true
end
+function multi:getPlatform()
+ return "lanes"
+end
local multi=multi
-- Step 2 set up the linda objects
local __GlobalLinda = lanes.linda() -- handles global stuff
@@ -85,7 +90,10 @@ else
THREAD.__CORES=tonumber(io.popen("nproc --all"):read("*n"))
end
function THREAD.kill() -- trigger the lane destruction
- -- coroutine.yield({"_kill_",":)"})
+ error("Thread was killed!")
+end
+function THREAD.getName()
+ return THREAD_NAME
end
--[[ Step 4 We need to get sleeping working to handle timing... We want idle wait, not busy wait
Idle wait keeps the CPU running better where busy wait wastes CPU cycles... Lanes does not have a sleep method
@@ -102,12 +110,17 @@ function THREAD.hold(n)
repeat wait() until n()
end
-- Step 5 Basic Threads!
-function multi:newSystemThread(name,func)
+function multi:newSystemThread(name,func,...)
local c={}
local __self=c
c.name=name
c.Type="sthread"
- c.thread=lanes.gen("*", func)()
+ local THREAD_NAME=name
+ local function func2(...)
+ _G["THREAD_NAME"]=THREAD_NAME
+ func()
+ end
+ c.thread=lanes.gen("*", func2)(...)
function c:kill()
--self.status:Destroy()
self.thread:cancel()
diff --git a/examples/love2d Threading Example/multi/integration/loveManager.lua b/examples/love2d Threading Example/multi/integration/loveManager.lua
index 56260c7..172f8e9 100644
--- a/examples/love2d Threading Example/multi/integration/loveManager.lua
+++ b/examples/love2d Threading Example/multi/integration/loveManager.lua
@@ -2,17 +2,22 @@ require("multi.compat.love2d")
function multi:canSystemThread()
return true
end
+function multi:getPlatform()
+ return "love2d"
+end
multi.integration={}
multi.integration.love2d={}
multi.integration.love2d.ThreadBase=[[
tab={...}
-__THREADNAME__=tab[2]
-__THREADID__=tab[1]
+__THREADID__=table.remove(tab,1)
+__THREADNAME__=table.remove(tab,1)
require("love.filesystem")
require("love.system")
require("love.timer")
+require("love.image")
require("multi")
GLOBAL={}
+isMainThread=false
setmetatable(GLOBAL,{
__index=function(t,k)
__sync__()
@@ -31,7 +36,7 @@ setmetatable(GLOBAL,{
function __sync__()
local data=__mythread__:pop()
while data do
- love.timer.sleep(.001)
+ love.timer.sleep(.01)
if type(data)=="string" then
local cmd,tp,name,d=data:match("(%S-) (%S-) (%S-) (.+)")
if name=="__DIEPLZ"..__THREADID__.."__" then
@@ -136,6 +141,12 @@ function dump(func)
end
return table.concat(code)
end
+function sThread.getName()
+ return __THREADNAME__
+end
+function sThread.kill()
+ error("Thread was killed!")
+end
function sThread.set(name,val)
GLOBAL[name]=val
end
@@ -155,17 +166,9 @@ end
function sThread.hold(n)
repeat __sync__() until n()
end
-multi:newLoop(function(self)
- self:Pause()
- local ld=multi:getLoad()
- self:Resume()
- if ld<80 then
- love.timer.sleep(.01)
- end
-end)
updater=multi:newUpdater()
updater:OnUpdate(__sync__)
-func=loadDump([=[INSERT_USER_CODE]=])()
+func=loadDump([=[INSERT_USER_CODE]=])(unpack(tab))
multi:mainloop()
]]
GLOBAL={} -- Allow main thread to interact with these objects as well
@@ -187,6 +190,10 @@ setmetatable(GLOBAL,{
})
THREAD={} -- Allow main thread to interact with these objects as well
multi.integration.love2d.mainChannel=love.thread.getChannel("__MainChan__")
+isMainThread=true
+function THREAD.getName()
+ return __THREADNAME__
+end
function ToStr(val, name, skipnewlines, depth)
skipnewlines = skipnewlines or false
depth = depth or 0
@@ -265,12 +272,12 @@ local function randomString(n)
end
return str
end
-function multi:newSystemThread(name,func) -- the main method
+function multi:newSystemThread(name,func,...) -- the main method
local c={}
c.name=name
c.ID=c.name..""
c.thread=love.thread.newThread(multi.integration.love2d.ThreadBase:gsub("INSERT_USER_CODE",dump(func)))
- c.thread:start(c.ID,c.name)
+ c.thread:start(c.ID,c.name,...)
function c:kill()
multi.integration.GLOBAL["__DIEPLZ"..self.ID.."__"]="__DIEPLZ"..self.ID.."__"
end
@@ -288,9 +295,7 @@ function THREAD.get(name)
return GLOBAL[name]
end
function THREAD.waitFor(name)
- multi.OBJ_REF:Pause()
- repeat multi:lManager() until GLOBAL[name]
- multi.OBJ_REF:Resume()
+ repeat multi:uManager() until GLOBAL[name]
return GLOBAL[name]
end
function THREAD.getCores()
@@ -300,9 +305,7 @@ function THREAD.sleep(n)
love.timer.sleep(n)
end
function THREAD.hold(n)
- multi.OBJ_REF:Pause()
- repeat multi:lManager() until n()
- multi.OBJ_REF:Resume()
+ repeat multi:uManager() until n()
end
__channels__={}
multi.integration.GLOBAL=GLOBAL
@@ -311,7 +314,6 @@ updater=multi:newUpdater()
updater:OnUpdate(function(self)
local data=multi.integration.love2d.mainChannel:pop()
while data do
- --print("MAIN:",data)
if type(data)=="string" then
local cmd,tp,name,d=data:match("(%S-) (%S-) (%S-) (.+)")
if cmd=="SYNC" then
diff --git a/examples/love2d Threading Example/multi/integration/luvitManager.lua b/examples/love2d Threading Example/multi/integration/luvitManager.lua
new file mode 100644
index 0000000..ba1e653
--- /dev/null
+++ b/examples/love2d Threading Example/multi/integration/luvitManager.lua
@@ -0,0 +1,127 @@
+--[[
+MIT License
+
+Copyright (c) 2017 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, sublicense, 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.
+]]
+
+-- I DEMAND USAGE FOR LUVIT
+-- Cannot use discordia without my multitasking library (Which I love more that the luvit platform... then again i'm partial :P)
+package.path="?/init.lua;?.lua;"..package.path
+local function _INIT(luvitThread,timer)
+ -- lots of this stuff should be able to stay the same
+ function os.getOS()
+ if package.config:sub(1,1)=='\\' then
+ return 'windows'
+ else
+ return 'unix'
+ end
+ end
+ -- Step 1 get setup threads on luvit... Sigh how do i even...
+ require("multi")
+ isMainThread=true
+ function multi:canSystemThread()
+ return true
+ end
+ function multi:getPlatform()
+ return "luvit"
+ end
+ local multi=multi
+ -- Step 2 set up the Global table... is this possible?
+ local GLOBAL={}
+ setmetatable(GLOBAL,{
+ __index=function(t,k)
+ --print("No Global table when using luvit integration!")
+ return nil
+ end,
+ __newindex=function(t,k,v)
+ --print("No Global table when using luvit integration!")
+ end,
+ })
+ local THREAD={}
+ function THREAD.set(name,val)
+ --print("No Global table when using luvit integration!")
+ end
+ function THREAD.get(name)
+ --print("No Global table when using luvit integration!")
+ end
+ local function randomString(n)
+ local str = ''
+ local strings = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','1','2','3','4','5','6','7','8','9','0','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'}
+ for i=1,n do
+ str = str..''..strings[math.random(1,#strings)]
+ end
+ return str
+ end
+ function THREAD.waitFor(name)
+ --print("No Global table when using luvit integration!")
+ end
+ function THREAD.testFor(name,val,sym)
+ --print("No Global table when using luvit integration!")
+ end
+ function THREAD.getCores()
+ return THREAD.__CORES
+ end
+ if os.getOS()=="windows" then
+ THREAD.__CORES=tonumber(os.getenv("NUMBER_OF_PROCESSORS"))
+ else
+ THREAD.__CORES=tonumber(io.popen("nproc --all"):read("*n"))
+ end
+ function THREAD.kill() -- trigger the thread destruction
+ error("Thread was Killed!")
+ end
+ -- hmmm if im cleaver I can get this to work... but since data passing isn't going to be a thing its probably not important
+ function THREAD.sleep(n)
+ --print("No Global table when using luvit integration!")
+ end
+ function THREAD.hold(n)
+ --print("No Global table when using luvit integration!")
+ end
+ -- Step 5 Basic Threads!
+ local function entry(path,name,func,...)
+ local timer = require'timer'
+ local luvitThread = require'thread'
+ package.path=path
+ loadstring(func)(...)
+ end
+ function multi:newSystemThread(name,func,...)
+ local c={}
+ local __self=c
+ c.name=name
+ c.Type="sthread"
+ c.thread={}
+ c.func=string.dump(func)
+ function c:kill()
+ -- print("No Global table when using luvit integration!")
+ end
+ luvitThread.start(entry,package.path,name,c.func,...)
+ return c
+ end
+ print("Integrated Luvit!")
+ multi.integration={} -- for module creators
+ multi.integration.GLOBAL=GLOBAL
+ multi.integration.THREAD=THREAD
+ require("multi.integration.shared")
+ -- Start the main mainloop... This allows you to process your multi objects, but the engine on the main thread will be limited to .001 or 1 millisecond sigh...
+ local interval = timer.setInterval(1, function ()
+ multi:uManager()
+ end)
+end
+return {init=function(threadHandle,timerHandle) _INIT(threadHandle,timerHandle) return GLOBAL,THREAD end}
diff --git a/examples/love2d Threading Example/multi/integration/shared.lua b/examples/love2d Threading Example/multi/integration/shared.lua
index 63ff000..33c1458 100644
--- a/examples/love2d Threading Example/multi/integration/shared.lua
+++ b/examples/love2d Threading Example/multi/integration/shared.lua
@@ -21,35 +21,70 @@ 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.
]]
-function multi.randomString(n)
- local str = ''
- local strings = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','1','2','3','4','5','6','7','8','9','0','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'}
- for i=1,n do
- str = str..''..strings[math.random(1,#strings)]
- end
- return str
-end
function multi:newSystemThreadedQueue(name) -- in love2d this will spawn a channel on both ends
local c={} -- where we will store our object
c.name=name -- set the name this is important for the love2d side
if love then -- check love
if love.thread then -- make sure we can use the threading module
- function c:init() -- create an init function so we can mimic on bith love2d and lanes
+ function c:init() -- create an init function so we can mimic on both love2d and lanes
self.chan=love.thread.getChannel(self.name) -- create channel by the name self.name
function self:push(v) -- push to the channel
- self.chan:push({type(v),resolveData(v)})
+ local tab
+ if type(v)=="table" then
+ tab = {}
+ for i,c in pairs(v) do
+ if type(c)=="function" then
+ tab[i]="\1"..string.dump(c)
+ else
+ tab[i]=c
+ end
+ end
+ self.chan:push(tab)
+ else
+ self.chan:push(c)
+ end
end
function self:pop() -- pop from the channel
- local tab=self.chan:pop()
- --print(tab)
- if not tab then return end
- return resolveType(tab[1],tab[2])
+ local v=self.chan:pop()
+ if not v then return end
+ if type(v)=="table" then
+ tab = {}
+ for i,c in pairs(v) do
+ if type(c)=="string" then
+ if c:sub(1,1)=="\1" then
+ tab[i]=loadstring(c:sub(2,-1))
+ else
+ tab[i]=c
+ end
+ else
+ tab[i]=c
+ end
+ end
+ return tab
+ else
+ return self.chan:pop()
+ end
end
function self:peek()
- local tab=self.chan:peek()
- --print(tab)
- if not tab then return end
- return resolveType(tab[1],tab[2])
+ local v=self.chan:peek()
+ if not v then return end
+ if type(v)=="table" then
+ tab = {}
+ for i,c in pairs(v) do
+ if type(c)=="string" then
+ if c:sub(1,1)=="\1" then
+ tab[i]=loadstring(c:sub(2,-1))
+ else
+ tab[i]=c
+ end
+ else
+ tab[i]=c
+ end
+ end
+ return tab
+ else
+ return self.chan:pop()
+ end
end
GLOBAL[self.name]=self -- send the object to the thread through the global interface
return self -- return the object
@@ -59,7 +94,7 @@ function multi:newSystemThreadedQueue(name) -- in love2d this will spawn a chann
error("Make sure you required the love.thread module!") -- tell the user if he/she didn't require said module
end
else
- c.linda=lanes.linda() -- lanes is a bit eaiser, create the linda on the main thread
+ c.linda=lanes.linda() -- lanes is a bit easier, create the linda on the main thread
function c:push(v) -- push to the queue
self.linda:send("Q",v)
end
@@ -76,6 +111,100 @@ function multi:newSystemThreadedQueue(name) -- in love2d this will spawn a chann
end
return c
end
+function multi:newSystemThreadedConnection(name,protect)
+ local c={}
+ c.name = name
+ c.protect=protect
+ local sThread=multi.integration.THREAD
+ local GLOBAL=multi.integration.GLOBAL
+ function c:init()
+ require("multi")
+ if multi:getPlatform()=="love2d" then
+ GLOBAL=_G.GLOBAL
+ sThread=_G.sThread
+ end
+ local conn = {}
+ conn.name = self.name
+ conn.count = 0
+ if isMainThread then
+ if GLOBAL[self.name.."THREADED_CONNQ"] then -- if this thing exists then lets grab it, we are doing something different here. instead of cleaning things up, we will gave a dedicated queue to manage things
+ conn.queueCall = sThread.waitFor(self.name.."THREADED_CALLQ"):init()
+ else
+ conn.queueCall = multi:newSystemThreadedQueue(self.name.."THREADED_CALLQ"):init()
+ end
+ else
+ require("multi") -- so things don't break, but also allows bi-directional connections to work
+ conn.queueCall = sThread.waitFor(self.name.."THREADED_CALLQ"):init()
+ end
+ setmetatable(conn,{__call=function(self,...) return self:connect(...) end})
+ conn.obj=multi:newConnection(self.protect)
+ function conn:connect(func)
+ return self.obj(func)
+ end
+ function conn:fConnect(func)
+ return self.obj:fConnect(func)
+ end
+ function conn:holdUT(n)
+ self.obj:holdUT(n)
+ end
+ function conn:Bind(t)
+ self.obj:Bind(t)
+ end
+ function conn:Remove()
+ self.obj:Remove()
+ end
+ function conn:getConnection(name,ingore)
+ return self.obj:getConnection(name,ingore)
+ end
+ function conn:Fire(...)
+ local args = {...}
+ table.insert(args,1,multi.randomString(8))
+ table.insert(args,1,self.name)
+ table.insert(args,1,"F")
+ self.queueCall:push(args)
+ if self.trigger_self then
+ self.obj:Fire(...)
+ end
+ end
+ self.cleanup = .01
+ function conn:SetCleanUpRate(n)
+ self.cleanup=n or .01
+ end
+ conn.lastid=""
+ conn.looper = multi:newLoop(function(self)
+ local con = self.link
+ local data = con.queueCall:peek()
+ if not data then return end
+ local id = data[3]
+ if data[1]=="F" and data[2]==con.name and con.lastid~=id then
+ con.lastid=id
+ table.remove(data,1)-- Remove the first 3 elements
+ table.remove(data,1)-- Remove the first 3 elements
+ table.remove(data,1)-- Remove the first 3 elements
+ con.obj:Fire(unpack(data))
+ multi:newThread("Clean_UP",function()
+ thread.sleep(con.cleanup)
+ local dat = con.queueCall:peek()
+ if not dat then return end
+ table.remove(data,1)-- Remove the first 3 elements
+ table.remove(data,1)-- Remove the first 3 elements
+ table.remove(data,1)-- Remove the first 3 elements
+ if dat[3]==id then
+ con.queueCall:pop()
+ end
+ end)
+ end
+ end)
+ conn.HoldUT=conn.holdUT
+ conn.looper.link=conn
+ conn.Connect=conn.connect
+ conn.FConnect=conn.fConnect
+ conn.GetConnection=conn.getConnection
+ return conn
+ end
+ GLOBAL[name]=c
+ return c
+end
function multi:systemThreadedBenchmark(n,p)
n=n or 1
local cores=multi.integration.THREAD.getCores()
@@ -120,88 +249,43 @@ function multi:systemThreadedBenchmark(n,p)
end)
return c
end
-function multi:newSystemThreadedTable(name,n)
- local c={} -- where we will store our object
- c.name=name -- set the name this is important for the love2d side
- c.cores=n
- c.hasT={}
- if love then -- check love
- if love.thread then -- make sure we can use the threading module
- function c:init() -- create an init function so we can mimic on bith love2d and lanes
- self.tab={}
- self.chan=love.thread.getChannel(self.name) -- create channel by the name self.name
- function self:waitFor(name) -- pop from the channel
- repeat self:sync() until self[name]
- return self[name]
- end
- function self:sync()
- local data=self.chan:peek()
- if data then
- local cmd,tp,name,d=data:match("(%S-) (%S-) (%S-) (.+)")
- if not self.hasT[name] then
- if type(data)=="string" then
- if cmd=="SYNC" then
- self.tab[name]=resolveType(tp,d) -- this is defined in the loveManager.lua file
- self.hasT[name]=true
- end
- else
- self.tab[name]=data
- end
- self.chan:pop()
- end
- end
- end
- function self:reset(name)
- self.hasT[core]=nil
- end
- setmetatable(self,{
- __index=function(t,k)
- self:sync()
- return self.tab[k]
- end,
- __newindex=function(t,k,v)
- self:sync()
- self.tab[k]=v
- if type(v)=="userdata" then
- self.chan:push(v)
- else
- for i=1,self.cores do
- self.chan:push("SYNC "..type(v).." "..k.." "..resolveData(v)) -- this is defined in the loveManager.lua file
- end
- end
- end,
- })
- GLOBAL[self.name]=self -- send the object to the thread through the global interface
- return self -- return the object
- end
- return c
+function multi:newSystemThreadedTable(name)
+ local c={}
+ c.name=name -- set the name this is important for identifying what is what
+ local sThread=multi.integration.THREAD
+ local GLOBAL=multi.integration.GLOBAL
+ function c:init() -- create an init function so we can mimic on both love2d and lanes
+ if multi:getPlatform()=="love2d" then
+ GLOBAL=_G.GLOBAL
+ sThread=_G.sThread
+ end
+ local cc={}
+ cc.tab={}
+ if isMainThread then
+ cc.conn = multi:newSystemThreadedConnection(self.name.."_Tabled_Connection"):init()
else
- error("Make sure you required the love.thread module!") -- tell the user if he/she didn't require said module
+ cc.conn = sThread.waitFor(self.name.."_Tabled_Connection"):init()
end
- else
- c.linda=lanes.linda() -- lanes is a bit eaiser, create the linda on the main thread
- function c:waitFor(name)
- while self[name]==nil do
- -- Waiting
- end
- return self[name]
+ function cc:waitFor(name)
+ repeat multi:uManager() until tab[name]~=nil
+ return tab[name]
end
- function c:sync()
- return -- just so we match the love2d side
- end
- function c:init() -- set the metatable
- setmetatable(self,{
- __index=function(t,k)
- return self.linda:get(k)
- end,
- __newindex=function(t,k,v)
- self.linda:set(k,v)
- end,
- })
- return self
- end
- multi.integration.GLOBAL[name]=c -- send the object to the thread through the global interface
+ local link = cc
+ cc.conn(function(k,v)
+ link.tab[k]=v
+ end)
+ setmetatable(cc,{
+ __index=function(t,k)
+ return t.tab[k]
+ end,
+ __newindex=function(t,k,v)
+ t.tab[k]=v
+ t.conn:Fire(k,v)
+ end,
+ })
+ return cc
end
+ GLOBAL[c.name]=c
return c
end
function multi:newSystemThreadedJobQueue(numOfCores)
@@ -212,8 +296,7 @@ function multi:newSystemThreadedJobQueue(numOfCores)
c.queueOUT=multi:newSystemThreadedQueue("THREADED_JQO"):init()
c.queueALL=multi:newSystemThreadedQueue("THREADED_QALL"):init()
c.REG=multi:newSystemThreadedQueue("THREADED_JQ_F_REG"):init()
- -- registerJob(name,func)
- -- pushJob(...)
+ c.OnReady=multi:newConnection()
function c:registerJob(name,func)
for i=1,self.cores do
self.REG:push({name,func})
@@ -222,9 +305,10 @@ function multi:newSystemThreadedJobQueue(numOfCores)
function c:pushJob(name,...)
self.queueOUT:push({self.jobnum,name,...})
self.jobnum=self.jobnum+1
+ return self.jobnum-1
end
- local GLOBAL=multi.integration.GLOBAL -- set up locals incase we are using lanes
- local sThread=multi.integration.THREAD -- set up locals incase we are using lanes
+ local GLOBAL=multi.integration.GLOBAL -- set up locals in case we are using lanes
+ local sThread=multi.integration.THREAD -- set up locals in case we are using lanes
function c:doToAll(func)
local TaskName=multi.randomString(16)
for i=1,self.cores do
@@ -232,17 +316,26 @@ function multi:newSystemThreadedJobQueue(numOfCores)
end
end
function c:start()
- self:doToAll(function()
- _G["__started__"]=true
- SFunc()
+ multi:newEvent(function()
+ return self.ThreadsLoaded==true
+ end):OnEvent(function(evnt)
+ GLOBAL["THREADED_JQ"]=nil -- remove it
+ GLOBAL["THREADED_JQO"]=nil -- remove it
+ GLOBAL["THREADED_JQ_F_REG"]=nil -- remove it
+ self:doToAll(function()
+ _G["__started__"]=true
+ SFunc()
+ end)
+ evnt:Destroy()
end)
end
GLOBAL["__JQ_COUNT__"]=c.cores
for i=1,c.cores do
- multi:newSystemThread("System Threaded Job Queue Worker Thread #"..i,function()
+ multi:newSystemThread("System Threaded Job Queue Worker Thread #"..i,function(name,ind)
require("multi")
+ ThreadName=name
__sleep__=.001
- if love then -- lets make sure we don't reference upvalues if using love2d
+ if love then -- lets make sure we don't reference up-values if using love2d
GLOBAL=_G.GLOBAL
sThread=_G.sThread
__sleep__=.1
@@ -251,10 +344,6 @@ function multi:newSystemThreadedJobQueue(numOfCores)
JQO=sThread.waitFor("THREADED_JQ"):init() -- Grab it
REG=sThread.waitFor("THREADED_JQ_F_REG"):init() -- Grab it
QALL=sThread.waitFor("THREADED_QALL"):init() -- Grab it
- sThread.sleep(.1) -- lets wait for things to work out
- GLOBAL["THREADED_JQ"]=nil -- remove it
- GLOBAL["THREADED_JQO"]=nil -- remove it
- GLOBAL["THREADED_JQ_F_REG"]=nil -- remove it
QALLT={}
FUNCS={}
SFunc=multi:newFunction(function(self)
@@ -287,10 +376,12 @@ function multi:newSystemThreadedJobQueue(numOfCores)
return FUNCS[k]
end
})
+ lastjob=os.clock()
MainLoop=multi:newLoop(function(self)
if __started__ then
local job=JQI:pop()
if job then
+ lastjob=os.clock()
local d=QALL:peek()
if d then
if not QALLT[d[1]] then
@@ -311,17 +402,36 @@ function multi:newSystemThreadedJobQueue(numOfCores)
end
end
end)
+ multi:newThread("Idler",function()
+ while true do
+ if os.clock()-lastjob>1 then
+ sThread.sleep(.1)
+ end
+ thread.sleep(.001)
+ end
+ end)
+ JQO:push({"_THREADINIT_"})
if not love then
multi:mainloop()
end
- end)
+ end,"Thread<"..i..">",i)
end
c.OnJobCompleted=multi:newConnection()
+ c.threadsResponded = 0
c.updater=multi:newLoop(function(self)
local data=self.link.queueIN:pop()
while data do
if data then
- self.link.OnJobCompleted:Fire(unpack(data))
+ local a=unpack(data)
+ if a=="_THREADINIT_" then
+ self.link.threadsResponded=self.link.threadsResponded+1
+ if self.link.threadsResponded==self.link.cores then
+ self.link.ThreadsLoaded=true
+ self.link.OnReady:Fire()
+ end
+ else
+ self.link.OnJobCompleted:Fire(unpack(data))
+ end
end
data=self.link.queueIN:pop()
end
diff --git a/multi/init.lua b/multi/init.lua
index 751c585..a75f921 100644
--- a/multi/init.lua
+++ b/multi/init.lua
@@ -23,9 +23,9 @@ SOFTWARE.
]]
require("bin")
multi = {}
-multi.Version = "1.9.2"
-multi._VERSION = "1.9.2"
-multi.stage = "mostly-stable"
+multi.Version = "1.10.0"
+multi._VERSION = "1.10.0"
+multi.stage = "stable"
multi.__index = multi
multi.Mainloop = {}
multi.Tasks = {}
@@ -162,6 +162,14 @@ else
os.execute('sleep ' .. tonumber(n))
end
end
+function multi.randomString(n)
+ local str = ''
+ local strings = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','1','2','3','4','5','6','7','8','9','0','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'}
+ for i=1,n do
+ str = str..''..strings[math.random(1,#strings)]
+ end
+ return str
+end
function multi:getParentProcess()
return self.Mainloop[self.CID]
end
@@ -798,25 +806,32 @@ function multi:newConnection(protect)
c.connections={}
c.fconnections={}
c.FC=0
- function c:holdUT()
+ function c:holdUT(n)
+ local n=n or 0
self.waiting=true
+ local count=0
local id=self:connect(function()
- self.waiting=false
+ count = count + 1
+ if n<=count then
+ self.waiting=false
+ end
end)
repeat
self.Parent:uManager()
until self.waiting==false
id:Destroy()
end
+ c.HoldUT=c.holdUT
function c:fConnect(func)
local temp=self:connect(func)
table.insert(self.fconnections,temp)
self.FC=self.FC+1
end
+ c.FConnect=c.fConnect
function c:getConnection(name,ingore)
if ingore then
return self.connections[name] or {
- Fire=function() end -- if the connection doesn't exist lets call all of them or silently ingore
+ Fire=function() end -- if the connection doesn't exist lets call all of them or silently ignore
}
else
return self.connections[name] or self
@@ -889,6 +904,7 @@ function multi:newConnection(protect)
return temp
end
c.Connect=c.connect
+ c.GetConnection=c.getConnection
function c:tofile(path)
local m=bin.new()
m:addBlock(self.Type)
diff --git a/multi/integration/lanesManager.lua b/multi/integration/lanesManager.lua
index d71f252..023dd81 100644
--- a/multi/integration/lanesManager.lua
+++ b/multi/integration/lanesManager.lua
@@ -33,6 +33,7 @@ end
lanes=require("lanes").configure()
--~ package.path="lua/?/init.lua;lua/?.lua;"..package.path
require("multi") -- get it all and have it on all lanes
+isMainThread=true
function multi:canSystemThread()
return true
end
diff --git a/multi/integration/loveManager.lua b/multi/integration/loveManager.lua
index 04c09fa..172f8e9 100644
--- a/multi/integration/loveManager.lua
+++ b/multi/integration/loveManager.lua
@@ -17,6 +17,7 @@ require("love.timer")
require("love.image")
require("multi")
GLOBAL={}
+isMainThread=false
setmetatable(GLOBAL,{
__index=function(t,k)
__sync__()
@@ -189,6 +190,7 @@ setmetatable(GLOBAL,{
})
THREAD={} -- Allow main thread to interact with these objects as well
multi.integration.love2d.mainChannel=love.thread.getChannel("__MainChan__")
+isMainThread=true
function THREAD.getName()
return __THREADNAME__
end
@@ -293,9 +295,7 @@ function THREAD.get(name)
return GLOBAL[name]
end
function THREAD.waitFor(name)
- multi.OBJ_REF:Pause()
- repeat multi:lManager() until GLOBAL[name]
- multi.OBJ_REF:Resume()
+ repeat multi:uManager() until GLOBAL[name]
return GLOBAL[name]
end
function THREAD.getCores()
@@ -305,9 +305,7 @@ function THREAD.sleep(n)
love.timer.sleep(n)
end
function THREAD.hold(n)
- multi.OBJ_REF:Pause()
- repeat multi:lManager() until n()
- multi.OBJ_REF:Resume()
+ repeat multi:uManager() until n()
end
__channels__={}
multi.integration.GLOBAL=GLOBAL
@@ -316,7 +314,6 @@ updater=multi:newUpdater()
updater:OnUpdate(function(self)
local data=multi.integration.love2d.mainChannel:pop()
while data do
- --print("MAIN:",data)
if type(data)=="string" then
local cmd,tp,name,d=data:match("(%S-) (%S-) (%S-) (.+)")
if cmd=="SYNC" then
diff --git a/multi/integration/luvitManager.lua b/multi/integration/luvitManager.lua
index 04af6c9..ba1e653 100644
--- a/multi/integration/luvitManager.lua
+++ b/multi/integration/luvitManager.lua
@@ -36,6 +36,7 @@ local function _INIT(luvitThread,timer)
end
-- Step 1 get setup threads on luvit... Sigh how do i even...
require("multi")
+ isMainThread=true
function multi:canSystemThread()
return true
end
diff --git a/multi/integration/shared.lua b/multi/integration/shared.lua
index 37aeaa4..33c1458 100644
--- a/multi/integration/shared.lua
+++ b/multi/integration/shared.lua
@@ -21,20 +21,12 @@ 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.
]]
-function multi.randomString(n)
- local str = ''
- local strings = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','1','2','3','4','5','6','7','8','9','0','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'}
- for i=1,n do
- str = str..''..strings[math.random(1,#strings)]
- end
- return str
-end
function multi:newSystemThreadedQueue(name) -- in love2d this will spawn a channel on both ends
local c={} -- where we will store our object
c.name=name -- set the name this is important for the love2d side
if love then -- check love
if love.thread then -- make sure we can use the threading module
- function c:init() -- create an init function so we can mimic on bith love2d and lanes
+ function c:init() -- create an init function so we can mimic on both love2d and lanes
self.chan=love.thread.getChannel(self.name) -- create channel by the name self.name
function self:push(v) -- push to the channel
local tab
@@ -102,7 +94,7 @@ function multi:newSystemThreadedQueue(name) -- in love2d this will spawn a chann
error("Make sure you required the love.thread module!") -- tell the user if he/she didn't require said module
end
else
- c.linda=lanes.linda() -- lanes is a bit eaiser, create the linda on the main thread
+ c.linda=lanes.linda() -- lanes is a bit easier, create the linda on the main thread
function c:push(v) -- push to the queue
self.linda:send("Q",v)
end
@@ -119,6 +111,100 @@ function multi:newSystemThreadedQueue(name) -- in love2d this will spawn a chann
end
return c
end
+function multi:newSystemThreadedConnection(name,protect)
+ local c={}
+ c.name = name
+ c.protect=protect
+ local sThread=multi.integration.THREAD
+ local GLOBAL=multi.integration.GLOBAL
+ function c:init()
+ require("multi")
+ if multi:getPlatform()=="love2d" then
+ GLOBAL=_G.GLOBAL
+ sThread=_G.sThread
+ end
+ local conn = {}
+ conn.name = self.name
+ conn.count = 0
+ if isMainThread then
+ if GLOBAL[self.name.."THREADED_CONNQ"] then -- if this thing exists then lets grab it, we are doing something different here. instead of cleaning things up, we will gave a dedicated queue to manage things
+ conn.queueCall = sThread.waitFor(self.name.."THREADED_CALLQ"):init()
+ else
+ conn.queueCall = multi:newSystemThreadedQueue(self.name.."THREADED_CALLQ"):init()
+ end
+ else
+ require("multi") -- so things don't break, but also allows bi-directional connections to work
+ conn.queueCall = sThread.waitFor(self.name.."THREADED_CALLQ"):init()
+ end
+ setmetatable(conn,{__call=function(self,...) return self:connect(...) end})
+ conn.obj=multi:newConnection(self.protect)
+ function conn:connect(func)
+ return self.obj(func)
+ end
+ function conn:fConnect(func)
+ return self.obj:fConnect(func)
+ end
+ function conn:holdUT(n)
+ self.obj:holdUT(n)
+ end
+ function conn:Bind(t)
+ self.obj:Bind(t)
+ end
+ function conn:Remove()
+ self.obj:Remove()
+ end
+ function conn:getConnection(name,ingore)
+ return self.obj:getConnection(name,ingore)
+ end
+ function conn:Fire(...)
+ local args = {...}
+ table.insert(args,1,multi.randomString(8))
+ table.insert(args,1,self.name)
+ table.insert(args,1,"F")
+ self.queueCall:push(args)
+ if self.trigger_self then
+ self.obj:Fire(...)
+ end
+ end
+ self.cleanup = .01
+ function conn:SetCleanUpRate(n)
+ self.cleanup=n or .01
+ end
+ conn.lastid=""
+ conn.looper = multi:newLoop(function(self)
+ local con = self.link
+ local data = con.queueCall:peek()
+ if not data then return end
+ local id = data[3]
+ if data[1]=="F" and data[2]==con.name and con.lastid~=id then
+ con.lastid=id
+ table.remove(data,1)-- Remove the first 3 elements
+ table.remove(data,1)-- Remove the first 3 elements
+ table.remove(data,1)-- Remove the first 3 elements
+ con.obj:Fire(unpack(data))
+ multi:newThread("Clean_UP",function()
+ thread.sleep(con.cleanup)
+ local dat = con.queueCall:peek()
+ if not dat then return end
+ table.remove(data,1)-- Remove the first 3 elements
+ table.remove(data,1)-- Remove the first 3 elements
+ table.remove(data,1)-- Remove the first 3 elements
+ if dat[3]==id then
+ con.queueCall:pop()
+ end
+ end)
+ end
+ end)
+ conn.HoldUT=conn.holdUT
+ conn.looper.link=conn
+ conn.Connect=conn.connect
+ conn.FConnect=conn.fConnect
+ conn.GetConnection=conn.getConnection
+ return conn
+ end
+ GLOBAL[name]=c
+ return c
+end
function multi:systemThreadedBenchmark(n,p)
n=n or 1
local cores=multi.integration.THREAD.getCores()
@@ -163,88 +249,43 @@ function multi:systemThreadedBenchmark(n,p)
end)
return c
end
-function multi:newSystemThreadedTable(name,n) -- NEDS FIXING SING SO MUCH WORK!!!
- local c={} -- where we will store our object
- c.name=name -- set the name this is important for the love2d side
- c.cores=n
- c.hasT={}
- if love then -- check love
- if love.thread then -- make sure we can use the threading module
- function c:init() -- create an init function so we can mimic on bith love2d and lanes
- self.tab={}
- self.chan=love.thread.getChannel(self.name) -- create channel by the name self.name
- function self:waitFor(name) -- pop from the channel
- repeat self:sync() until self[name]
- return self[name]
- end
- function self:sync()
- local data=self.chan:peek()
- if data then
- local cmd,tp,name,d=data:match("(%S-) (%S-) (%S-) (.+)")
- if not self.hasT[name] then
- if type(data)=="string" then
- if cmd=="SYNC" then
- self.tab[name]=resolveType(tp,d) -- this is defined in the loveManager.lua file
- self.hasT[name]=true
- end
- else
- self.tab[name]=data
- end
- self.chan:pop()
- end
- end
- end
- function self:reset(name)
- self.hasT[core]=nil
- end
- setmetatable(self,{
- __index=function(t,k)
- self:sync()
- return self.tab[k]
- end,
- __newindex=function(t,k,v)
- self:sync()
- self.tab[k]=v
- if type(v)=="userdata" then
- self.chan:push(v)
- else
- for i=1,self.cores do
- self.chan:push("SYNC "..type(v).." "..k.." "..resolveData(v)) -- this is defined in the loveManager.lua file
- end
- end
- end,
- })
- GLOBAL[self.name]=self -- send the object to the thread through the global interface
- return self -- return the object
- end
- return c
+function multi:newSystemThreadedTable(name)
+ local c={}
+ c.name=name -- set the name this is important for identifying what is what
+ local sThread=multi.integration.THREAD
+ local GLOBAL=multi.integration.GLOBAL
+ function c:init() -- create an init function so we can mimic on both love2d and lanes
+ if multi:getPlatform()=="love2d" then
+ GLOBAL=_G.GLOBAL
+ sThread=_G.sThread
+ end
+ local cc={}
+ cc.tab={}
+ if isMainThread then
+ cc.conn = multi:newSystemThreadedConnection(self.name.."_Tabled_Connection"):init()
else
- error("Make sure you required the love.thread module!") -- tell the user if he/she didn't require said module
+ cc.conn = sThread.waitFor(self.name.."_Tabled_Connection"):init()
end
- else
- c.linda=lanes.linda() -- lanes is a bit eaiser, create the linda on the main thread
- function c:waitFor(name)
- while self[name]==nil do
- -- Waiting
- end
- return self[name]
+ function cc:waitFor(name)
+ repeat multi:uManager() until tab[name]~=nil
+ return tab[name]
end
- function c:sync()
- return -- just so we match the love2d side
- end
- function c:init() -- set the metatable
- setmetatable(self,{
- __index=function(t,k)
- return self.linda:get(k)
- end,
- __newindex=function(t,k,v)
- self.linda:set(k,v)
- end,
- })
- return self
- end
- multi.integration.GLOBAL[name]=c -- send the object to the thread through the global interface
+ local link = cc
+ cc.conn(function(k,v)
+ link.tab[k]=v
+ end)
+ setmetatable(cc,{
+ __index=function(t,k)
+ return t.tab[k]
+ end,
+ __newindex=function(t,k,v)
+ t.tab[k]=v
+ t.conn:Fire(k,v)
+ end,
+ })
+ return cc
end
+ GLOBAL[c.name]=c
return c
end
function multi:newSystemThreadedJobQueue(numOfCores)
@@ -266,8 +307,8 @@ function multi:newSystemThreadedJobQueue(numOfCores)
self.jobnum=self.jobnum+1
return self.jobnum-1
end
- local GLOBAL=multi.integration.GLOBAL -- set up locals incase we are using lanes
- local sThread=multi.integration.THREAD -- set up locals incase we are using lanes
+ local GLOBAL=multi.integration.GLOBAL -- set up locals in case we are using lanes
+ local sThread=multi.integration.THREAD -- set up locals in case we are using lanes
function c:doToAll(func)
local TaskName=multi.randomString(16)
for i=1,self.cores do
@@ -294,7 +335,7 @@ function multi:newSystemThreadedJobQueue(numOfCores)
require("multi")
ThreadName=name
__sleep__=.001
- if love then -- lets make sure we don't reference upvalues if using love2d
+ if love then -- lets make sure we don't reference up-values if using love2d
GLOBAL=_G.GLOBAL
sThread=_G.sThread
__sleep__=.1
@@ -369,20 +410,22 @@ function multi:newSystemThreadedJobQueue(numOfCores)
thread.sleep(.001)
end
end)
- JQO:push({"_THREADINIT_",ind})
+ JQO:push({"_THREADINIT_"})
if not love then
multi:mainloop()
end
end,"Thread<"..i..">",i)
end
c.OnJobCompleted=multi:newConnection()
+ c.threadsResponded = 0
c.updater=multi:newLoop(function(self)
local data=self.link.queueIN:pop()
while data do
if data then
- local a,b=unpack(data)
+ local a=unpack(data)
if a=="_THREADINIT_" then
- if b==self.link.cores then
+ self.link.threadsResponded=self.link.threadsResponded+1
+ if self.link.threadsResponded==self.link.cores then
self.link.ThreadsLoaded=true
self.link.OnReady:Fire()
end