Lua Thread Mutex Implementation

Hello Developers! :wave:

This module is essentially a Mutex Lock implementation written in Lua.
This is a sub-module developed for the Infinity ECS Framework; All Sub-Modules are built to be used inside of Infinity.

Example Of The Mutex
local MutexModule = require("Mutex")
local MutexObject = MutexModule.new(function()
    print("Clean!")
end)

Mutex:Timeout(1)
-- -> "Clean!" (In locking our thread, it will invoke the cleaning callback!)
-- -> This will allow us to reset variables, clean variables etc when our thread is locked.

print(Mutex:IsLocked()) --> true
-- A second later..
print(Mutex:IsLocked()) --> false
Module Source
--[[
    Mutex.lua
    @Author: AsynchronousMatrix
    @Licence: ...
]]--

-- // Variables
local MutexModule = { }
local MutexObject = { Name = "Mutex" }

MutexObject.__index = MutexObject

-- // MutexObject Functions
function MutexObject:Lock()
	self._Locked = true
	self._Thread = coroutine.running()

	if self.Callback then 
		self.Callback() 
	end
end

function MutexObject:Unlock()
	if self._Thread then
		assert(self._Thread == coroutine.running(), "Thread Exception: Attempted to call Mutex.Unlock")
	end
	
	self._Thread = nil
	self._Locked = false
end

function MutexObject:Timeout(Int)
	self._Locked = true
	self._Timeout = {
		T = os.time(), Int = Int
	}

	if self.Callback then 
		self.Callback(true, Int) 
	end
end

function MutexObject:IsLocked()
	if self._Timeout then
		if os.time() - self._Timeout.T >= self._Timeout.Int then
			self._Timeout = false
			self._Locked = false

			return false
		end
	end

	return self._Locked
end

-- // MutexModule Functions
function MutexModule.new(Callback)
	local Mutex = setmetatable({ Callback = Callback, _Locked = false }, MutexObject)

	return Mutex
end

-- // Module
return MutexModule

GitHub Link [ LuaModules / sources / Mutex.lua ];

If anyone has any suggestions, feel free to leave a comment below; I’ll respond when I am able too! :wave:

7 Likes

Hello, I have suggestion to make tick() instead of os.clock, some people would use float numbers as wait time. Also maybe I am not right, but mutex is only for one thread owner, so you should lock/unlock only if locked from current thread, and maybe add FreeWait(), CallbackWait() methods (yield untill this mutex unlocked and call callback when mutex unlocked)

1 Like

Ah, Thank you for your awesome suggestion!

to make tick() instead of os.clock()

I lean towards using os.clock based on some weird things people have said over the years, tick is somewhat unreliable, tick maybe getting deprecated? There’s been a few, so I looked for an alternative that not only works in Roblox, but in many version of Lua & Other engines like Love!

Overall, I used os.clock because it’s suggested to use this for benchmarking, meaning it’s somewhat more stable? Not 100% though!

but mutex is only for one thread owner, so you should lock/unlock only if locked from current thread, and maybe add FreeWait(), CallbackWait() methods

This seems like an awesome addition! I’ll see what I can do with it. (I’m using no generic yielding like wait or task.wait, mainly because I am trying to get this code to be ran in a multitude of environments, not just Roblox’s engine. )

Tick is probably getting deprecated as stated by a Roblox admin.

os.clock returns the CPU time in seconds with microsecond resolution making it more useful for benchmarking purposes although more heavy performance wise than other methods.

There’s also other methods like os.time and time;
time returns the amount of seconds in a millisecond resolution since the instance started (local time)
os.time returns the amount of seconds in second resolution since the Unix epoch

Do not use tick.

2 Likes

Mutex locks can be very helpful in certain situations. Very cool to see it as a module!

Oh, I think you can’t make this for native lua + luau, because you can resume threads only with task.spawn method in roblox. Maybe I can make roblox version, but I need some examples where it might be need. I never had to use mutex, because roblox launch only 1 thread at once, but maybe for native lua this is great thing

I’ve found some purposes using this; For example when a system is forced to use something such as an Heartbeat Method to update all Sub-Objects of a Base Object, then a MutexLock could define weather or not that thread will accept the Heartbeat or not.

It’s one of those things you’ll say; “Oh I dont need it haha” until it comes to a point where you do need it and you then say “Oh, now I understand the use of this, this seems pretty interesting”

At least in my head it works out somewhat like that ^ :smiley:

In this case it is more like cooldown, Isn’t it?

Yeah, like a super advanced debounce.
This super adbamced debounce has logicwhich extends the use more than just a normal variable however. (I mean, only if it’s applicable)

Hey, concurrency enthusiast here.

Love this, really do! Well done on this module! Was just curious, is this optimised as a Mutex or a RwLock? Or can we get a separate function/export optimised for reading lots?

1 Like

This module was initially designed to be a fully-fledged Mutex lock, however looking into the usage of a RwLock, I believe it’s leaning more towards that.

It’s designed to be unwriteable when locked, however be readable in any state.

I never knew RwLocks existed, else I would of done something more lenient to that, most likely with alot more API functions to go with it. Thanks :smiley: :wave:

1 Like