• multi v15.1.x Stable

    rayaman released this 2021-11-30 21:28:18 -05:00 | 152 commits to master since this release

    Update 15.1.0 - Hold the thread!

    Full Update Showcase

    package.path = "./?/init.lua;"..package.path
    multi,thread = require("multi"):init()
    
    func = thread:newFunction(function(count)
        local a = 0
        while true do
            a = a + 1
            thread.sleep(.1)
            thread.pushStatus(a,count)
            if a == count then break end
        end
        return "Done"
    end)
    
    multi:newThread("Function Status Test",function()
        local ret = func(10)
        local ret2 = func(15)
        local ret3 = func(20)
        ret.OnStatus(function(part,whole)
            print("Ret1: ",math.ceil((part/whole)*1000)/10 .."%")
        end)
        ret2.OnStatus(function(part,whole)
            print("Ret2: ",math.ceil((part/whole)*1000)/10 .."%")
        end)
        ret3.OnStatus(function(part,whole)
            print("Ret3: ",math.ceil((part/whole)*1000)/10 .."%")
        end)
    	-- Connections can now be added together, if you had multiple holds and one finished before others and wasn't consumed it would lock forever! This is now fixed
        thread.hold(ret2.OnReturn + ret.OnReturn + ret3.OnReturn)
        print("Function Done!")
        os.exit()
    end)
    
    test = thread:newFunction(function()
        return 1,2,nil,3,4,5,6,7,8,9
    end,true)
    print(test())
    multi:newThread("testing",function()
        print("#Test = ",test())
        print(thread.hold(function()
            print("Hello!")
            return false
        end,{
            interval = 2,
            cycles = 3
        })) -- End result, 3 attempts within 6 seconds. If still false then timeout
        print("held")
    end).OnError(function(...)
        print(...)
    end)
    
    sandbox = multi:newProcessor()
    sandbox:newTLoop(function()
        print("testing...")
    end,1)
    
    test2 = multi:newTLoop(function()
        print("testing2...")
    end,1)
    
    sandbox:newThread("Test Thread",function()
        local a = 0
        while true do
            thread.sleep(1)
            a = a + 1
            print("Thread Test: ".. multi.getCurrentProcess().Name)
            if a == 10 then
                sandbox.Stop()
            end
        end
    end).OnError(function(...)
        print(...)
    end)
    multi:newThread("Test Thread",function()
        while true do
            thread.sleep(1)
            print("Thread Test: ".. multi.getCurrentProcess().Name)
        end
    end).OnError(function(...)
        print(...)
    end)
    
    sandbox.Start()
    
    multi:mainloop()
    

    Added:

    multi:newSystemThreadedJobQueue(n) isEmpty()

    • returns true if the queue is empty, false if there are items in the queue.

    Note: a queue might be empty, but the job may still be running and not finished yet! Also if a registered function is called directly instead of pushed, it will not reflect inside the queue until the next cycle!

    Example:

    package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path
    package.cpath = [[C:\Program Files (x86)\Lua\5.1\systree\lib\lua\5.1\?.dll;C:\Program Files (x86)\Lua\5.1\systree\lib\lua\5.1\?\core.dll;]] ..package.cpath
    multi,thread = require("multi"):init()
    GLOBAL,THREAD = require("multi.integration.threading"):init() -- Auto detects your enviroment and uses what's available
    
    jq = multi:newSystemThreadedJobQueue(5) -- Job queue with 4 worker threads
    func = jq:newFunction("test",function(a,b)
        THREAD.sleep(2)
        return a+b
    end)
    for i = 1,10 do
        func(i,i*3).connect(function(data)
            print(data)
        end)
    end
    
    local a = true
    b = false
    
    multi:newThread("Standard Thread 1",function()
        while true do
            thread.sleep(.1)
            print("Empty:",jq:isEmpty())
        end
    end).OnError(function(self,msg)
        print(msg)
    end)
    multi:mainloop()
    

    multi.TIMEOUT

    multi.TIMEOUT is equal to "TIMEOUT", it is reccomended to use this incase things change later on. There are plans to change the timeout value to become a custom object instead of a string.

    new connections on threaded functions

    • func.OnStatus(...)

      Allows you to connect to the status of a function see thread.pushStatus()

    • func.OnReturn(...)

      Allows you to connect to the functions return event and capture its returns see Example for an example of it in use.

    multi:newProcessor(name)

    package.path = "./?/init.lua;"..package.path
    multi,thread = require("multi"):init()
    
    -- Create a processor object, it works a lot like the multi object
    sandbox = multi:newProcessor()
    
    -- On our processor object create a TLoop that prints "testing..." every second
    sandbox:newTLoop(function()
    	print("testing...")
    end,1)
    
    -- Create a thread on the processor object
    sandbox:newThread("Test Thread",function()
    	-- Create a counter named 'a'
    	local a = 0
    	-- Start of the while loop that ends when a = 10
    	while true do
    		-- pause execution of the thread for 1 second
    		thread.sleep(1)
    		-- increment a by 1
    		a = a + 1
    		-- display the name of the current process
    		print("Thread Test: ".. multi.getCurrentProcess().Name)
    		if a == 10 then
    			-- Stopping the processor stops all objects created inside that process including threads. In the backend threads use a regular multiobject to handle the scheduler and all of the holding functions. These all stop when a processor is stopped. This can be really useful to sandbox processes that might need to turned on and off with ease and not having to think about it.
    			sandbox.Stop()
    		end
    	end
    	-- Catch any errors that may come up
    end).OnError(function(...)
    	print(...)
    end)
    
    sandbox.Start() -- Start the process
    
    multi:mainloop() -- The main loop that allows all processes to continue
    

    Note: Processor objects have been added and removed many times in the past, but will remain with this update.

    Attribute Type Returns Description
    Start Method() self Starts the process
    Stop Method() self Stops the process
    OnError Connection connection Allows connection to the process error handler
    Type Member:string "process" Contains the type of object
    Active Member:boolean variable If false the process is not active
    Name Member:string variable The name set at process creation
    process Thread thread A handle to a multi thread object

    Note: All tasks/threads created on a process are linked to that process. If a process is stopped all tasks/threads will be halted until the process is started back up.

    Connection can now be added together

    Very useful when using thread.hold for multiple connections to trigger.

    Iif you had multiple holds and one finished before others and wasn't consumed it would lock forever! This is now fixed

    print(conn + conn2 + conn3 + connN)

    Can be chained as long as you want! See example below

    Status added to threaded functions

    • thread.pushStatus(...)

      Allows a developer to push a status from a function.

    • tFunc.OnStatus(func(...))

      A connection that can be used on a function to view the status of the threaded function

    Example:

    package.path = "./?/init.lua;"..package.path
    multi,thread = require("multi"):init()
    
    func = thread:newFunction(function(count)
        local a = 0
        while true do
            a = a + 1
            thread.sleep(.1)
            thread.pushStatus(a,count)
            if a == count then break end
        end
        return "Done"
    end)
    
    multi:newThread("Function Status Test",function()
        local ret = func(10)
        local ret2 = func(15)
        local ret3 = func(20)
        ret.OnStatus(function(part,whole)
            --[[ Print out the current status. In this case every second it will update with:
    		10%
    		20%
    		30%
    		...
    		100%
    
    		Function Done!
    		]]
            print(math.ceil((part/whole)*1000)/10 .."%")
        end)
        ret2.OnStatus(function(part,whole)
            print("Ret2: ",math.ceil((part/whole)*1000)/10 .."%")
        end)
        ret3.OnStatus(function(part,whole)
            print("Ret3: ",math.ceil((part/whole)*1000)/10 .."%")
        end)
    	-- Connections can now be added together, if you had multiple holds and one finished before others and wasn't consumed it would lock forever! This is now fixed
        thread.hold(ret2.OnReturn + ret.OnReturn + ret3.OnReturn)
        print("Function Done!")
        os.exit()
    end)
    

    Changed:

    • f = thread:newFunction(func,holdme)

      • Nothing changed that will affect how the object functions by default. The returned function is now a table that is callable and 3 new methods have been added:
      Method Description
      Pause() Pauses the function, Will cause the function to return nil, Function is paused
      Resume() Resumes the function
      holdMe(set) Sets the holdme argument that existed at function creation
      package.path = "./?/init.lua;"..package.path
      multi, thread = require("multi"):init()
      
      test = thread:newFunction(function(a,b)
      	thread.sleep(1)
      	return a,b
      end, true)
      
      print(test(1,2))
      
      test:Pause()
      
      print(test(1,2))
      
      test:Resume()
      
      print(test(1,2))
      
      --[[ -- If you left holdme nil/false
      
      print(test(1,2).connect(function(...)
      	print(...)
      end))
      
      test:Pause()
      
      print(test(1,2).connect(function(...)
      	print(...)
      end))
      
      test:Resume()
      
      print(test(1,2).connect(function(...)
      	print(...)
      end))
      
      ]]
      
      multi:mainloop()
      

      Output:

      1       2
      nil     Function is paused
      1       2
      

      If holdme is nil/false:

      nil     Function is paused
      
      
      1       2       nil...
      1       2       nil...
      
    • thread.hold(n,opt) Ref. Issue

      • Added option table to thread.hold

        Option Description
        interval Time between each poll
        cycles Number of cycles before timing out
        sleep Number of seconds before timing out
        skip Number of cycles before testing again, does not cause a timeout!

        Note: cycles and sleep options cannot both be used at the same time. Interval and skip cannot be used at the same time either. Cycles take priority over sleep if both are present! HoldFor and HoldWithin can be emulated using the new features. Old functions will remain for backward compatibility.

        Using cycles, sleep or interval will cause a timeout; returning nil, multi.TIMEOUT

      • n can be a number and thread.hold will act like thread.sleep. When n is a number the option table will be ignored!

    Removed:

    • N/A

    Fixed:

    • Threaded functions not returning multiple values Ref. Issue
    • Priority Lists not containing Very_High and Very_Low from previous update
    • All functions that should have chaining now do, reminder all functions that don't return any data return a reference to itself to allow chaining of method calls.

    ToDo

    • Work on network parallelism (I really want to make this, but time and getting it right is proving much more difficult)
    • Work on QOL changes to allow cleaner code like this
    Downloads