Last active
January 23, 2022 16:18
-
-
Save truemedian/a7b6d288a89e9f3f5cf61f1ddc1a8531 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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