Skip to content

Instantly share code, notes, and snippets.

@truemedian
Last active January 23, 2022 16:18
Show Gist options
  • Save truemedian/a7b6d288a89e9f3f5cf61f1ddc1a8531 to your computer and use it in GitHub Desktop.
Save truemedian/a7b6d288a89e9f3f5cf61f1ddc1a8531 to your computer and use it in GitHub Desktop.
local ffi = require 'ffi'
local Mutex = {}
Mutex.__index = Mutex
if ffi.abi('win') then
-- trust me on these, we treat CRITICAL_SECTION like an opaque pointer.
local sizeof_critical_section = ffi.abi('32bit') and 24 or 40;
local libc = ffi.load('msvcrt')
ffi.cdef [[
void* malloc(size_t size);
void free(void* mem);
void InitializeCriticalSection(void* lpCriticalSection);
void EnterCriticalSection(void* lpCriticalSection);
int TryEnterCriticalSection(void* lpCriticalSection);
void LeaveCriticalSection(void* lpCriticalSection);
void DeleteCriticalSection(void* lpCriticalSection);
]]
function Mutex.new()
local ptr = libc.malloc(sizeof_critical_section)
ffi.C.InitializeCriticalSection(ptr)
local self = setmetatable({}, Mutex)
self.inner = ptr
ffi.gc(self.inner, Mutex.destroy)
return self
end
function Mutex:destroy()
ffi.C.DeleteCriticalSection(self.inner)
libc.free(ffi.gc(self.inner, nil))
end
function Mutex:lock()
ffi.C.EnterCriticalSection(self.inner)
end
function Mutex:trylock()
return ffi.C.TryEnterCriticalSection(self.inner) ~= 0
end
function Mutex:unlock()
ffi.C.LeaveCriticalSection(self.inner)
end
else
-- trust me on these, we treat pthread_mutex_t like an opaque pointer.
local sizeof_pthread_mutex_t = ffi.abi('32bit') and 24 or 40;
ffi.cdef [[
char* strerror(int errnum);
void abort(void);
void* malloc(size_t size);
void free(void* mem);
int pthread_mutex_init(void* mutex);
int pthread_mutex_lock(void* mutex);
int pthread_mutex_trylock(void* mutex);
int pthread_mutex_unlock(void* mutex);
int pthread_mutex_destroy(void* mutex);
]]
function Mutex.new()
local ptr = ffi.C.malloc(sizeof_pthread_mutex_t)
local self = setmetatable({}, Mutex)
self.inner = ptr
local ret = ffi.C.pthread_mutex_init(ptr)
if ret ~= 0 then
ffi.C.free(ptr);
error('pthread_mutex_init failed: ' .. ffi.string(ffi.C.strerror(ret)))
end
ffi.gc(self.inner, Mutex.destroy)
return self
end
function Mutex:destroy()
if ffi.C.pthread_mutex_destroy(self.inner) ~= 0 then
ffi.C.abort()
end
libc.free(ffi.gc(self.inner, nil))
end
function Mutex:lock()
if ffi.C.pthread_mutex_lock(self.inner) ~= 0 then
ffi.C.abort()
end
end
function Mutex:trylock()
local ret = ffi.C.pthread_mutex_trylock(self.inner)
if ret ~= 0 then
local err = ffi.string(ffi.C.strerror(ret))
if err == 'EBUSY' or err == 'EAGAIN' then
return false
else
ffi.C.abort()
end
end
return true
end
function Mutex:unlock()
if ffi.C.pthread_mutex_unlock(self.inner) ~= 0 then
ffi.C.abort()
end
end
end
return Mutex
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment