Skip to content

Commit 4c9fbc6

Browse files
truemediansqueek502
authored andcommitted
feat: add binding for semaphore to test synchronization
fix/thread: fix luv_getmtname trying to index a non-present metatable.
1 parent b6f3e44 commit 4c9fbc6

File tree

7 files changed

+275
-7
lines changed

7 files changed

+275
-7
lines changed

docs/docs.lua

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,7 @@ local types = {
365365
luv_dir_t = cls('userdata'),
366366
luv_work_ctx_t = cls('userdata'),
367367
luv_thread_t = cls('userdata'),
368+
luv_sem_t = cls('userdata'),
368369

369370
threadargs = union('number', 'boolean', 'string', 'userdata'),
370371

@@ -4182,6 +4183,63 @@ local doc = {
41824183
{ name = 'msec', type = 'integer' },
41834184
},
41844185
},
4186+
{
4187+
name = 'new_sem',
4188+
desc = [[
4189+
Creates a new semaphore with the specified initial value. A semaphore is safe to
4190+
share across threads. It represents an unsigned integer value that can incremented
4191+
and decremented atomically but any attempt to make it negative will "wait" until
4192+
the value can be decremented by another thread incrementing it.
4193+
4194+
The initial value must be a non-negative integer.
4195+
]],
4196+
params = {
4197+
{ name = 'value', type = opt_int },
4198+
},
4199+
returns = ret_or_fail('luv_sem_t', 'sem'),
4200+
notes = {
4201+
[[A semaphore must be shared between threads, any `uv.sem_wait()` on a single thread that blocks will deadlock.]],
4202+
},
4203+
},
4204+
{
4205+
name = 'sem_post',
4206+
method_form = 'sem:post()',
4207+
desc = [[
4208+
Increments (unlocks) a semaphore, if the semaphore's value consequently becomes
4209+
greater than zero then another thread blocked in a sem_wait call will be woken
4210+
and proceed to decrement the semaphore.
4211+
]],
4212+
params = {
4213+
{ name = 'sem', type = 'luv_sem_t' },
4214+
},
4215+
},
4216+
{
4217+
name = 'sem_wait',
4218+
method_form = 'sem:wait()',
4219+
desc = [[
4220+
Decrements (locks) a semaphore, if the semaphore's value is greater than zero
4221+
then the value is decremented and the call returns immediately. If the semaphore's
4222+
value is zero then the call blocks until the semaphore's value rises above zero or
4223+
the call is interrupted by a signal.
4224+
]],
4225+
params = {
4226+
{ name = 'sem', type = 'luv_sem_t' },
4227+
},
4228+
},
4229+
{
4230+
name = 'sem_trywait',
4231+
method_form = 'sem:trywait()',
4232+
desc = [[
4233+
The same as `uv.sem_wait()` but returns immediately if the semaphore is not available.
4234+
4235+
If the semaphore's value was decremented then `true` is returned, otherwise the semaphore
4236+
has a value of zero and `false` is returned.
4237+
]],
4238+
params = {
4239+
{ name = 'sem', type = 'luv_sem_t' },
4240+
},
4241+
returns = 'boolean',
4242+
},
41854243
},
41864244
},
41874245
{

docs/docs.md

Lines changed: 57 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/meta.lua

Lines changed: 56 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/luv.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
#include "signal.c"
4444
#include "stream.c"
4545
#include "tcp.c"
46+
#include "synch.c"
4647
#include "thread.c"
4748
#include "timer.c"
4849
#include "tty.c"
@@ -408,6 +409,12 @@ static const luaL_Reg luv_functions[] = {
408409
{"thread_setname", luv_thread_setname},
409410
#endif
410411

412+
// synch.c
413+
{"new_sem", luv_new_sem},
414+
{"sem_post", luv_sem_post},
415+
{"sem_wait", luv_sem_wait},
416+
{"sem_trywait", luv_sem_trywait},
417+
411418
#if LUV_UV_VERSION_GEQ(1, 49, 0)
412419
{"utf16_length_as_wtf8", luv_utf16_length_as_wtf8},
413420
{"utf16_to_wtf8", luv_utf16_to_wtf8},
@@ -920,6 +927,7 @@ LUALIB_API int luaopen_luv (lua_State* L) {
920927
luv_dir_init(L);
921928
#endif
922929
luv_thread_init(L);
930+
luv_synch_init(L);
923931
luv_work_init(L);
924932

925933
luv_constants(L);

src/synch.c

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* Copyright 2014 The Luvit Authors. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
#include "private.h"
18+
19+
static uv_sem_t* luv_check_sem(lua_State* L, int index) {
20+
return (uv_sem_t*) luv_checkudata(L, index, "uv_sem");
21+
}
22+
23+
static int luv_new_sem(lua_State* L) {
24+
int value = luaL_optinteger(L, 1, 0);
25+
if (value < 0) {
26+
return luaL_argerror(L, 1, "value must be >= 0");
27+
}
28+
29+
uv_sem_t *sem = (uv_sem_t*) luv_newuserdata(L, sizeof(uv_sem_t));
30+
luaL_getmetatable(L, "uv_sem");
31+
lua_setmetatable(L, -2);
32+
33+
int ret = uv_sem_init(sem, value);
34+
if (ret < 0) {
35+
lua_pop(L, 1);
36+
return luv_error(L, ret);
37+
}
38+
return 1;
39+
}
40+
41+
static int luv_sem_gc(lua_State* L) {
42+
uv_sem_t* sem = luv_check_sem(L, 1);
43+
44+
uv_sem_destroy(sem);
45+
free(sem);
46+
return 0;
47+
}
48+
49+
static int luv_sem_post(lua_State* L) {
50+
uv_sem_t* sem = luv_check_sem(L, 1);
51+
uv_sem_post(sem);
52+
return 0;
53+
}
54+
55+
static int luv_sem_wait(lua_State* L) {
56+
uv_sem_t* sem = luv_check_sem(L, 1);
57+
uv_sem_wait(sem);
58+
return 0;
59+
}
60+
61+
static int luv_sem_trywait(lua_State* L) {
62+
uv_sem_t* sem = luv_check_sem(L, 1);
63+
int ret = uv_sem_trywait(sem);
64+
lua_pushboolean(L, ret == 0);
65+
return 1;
66+
}
67+
68+
static const luaL_Reg luv_sem_methods[] = {
69+
{"post", luv_sem_post},
70+
{"wait", luv_sem_wait},
71+
{"trywait", luv_sem_trywait},
72+
{NULL, NULL}
73+
};
74+
75+
static void luv_synch_init(lua_State* L) {
76+
luaL_newmetatable(L, "uv_sem");
77+
lua_pushcfunction(L, luv_sem_gc);
78+
lua_setfield(L, -2, "__gc");
79+
lua_newtable(L);
80+
luaL_setfuncs(L, luv_sem_methods, 0);
81+
lua_setfield(L, -2, "__index");
82+
lua_pop(L, 1);
83+
}

src/thread.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,11 @@ static void luv_thread_release_vm(lua_State* L) {
5454

5555
static const char* luv_getmtname(lua_State *L, int idx) {
5656
const char* name;
57-
lua_getmetatable(L, idx);
58-
lua_pushstring(L, "__name");
59-
lua_rawget(L, -2);
60-
name = lua_tostring(L, -1);
61-
lua_pop(L, 2);
57+
if (lua_getmetatable(L, idx)) {
58+
lua_getfield(L, -1, "__name");
59+
name = lua_tostring(L, -1);
60+
lua_pop(L, 2);
61+
}
6262
return name;
6363
}
6464

tests/test-thread.lua

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,19 +167,25 @@ return require('lib/tap')(function (test)
167167
assert(type(uv.constants.THREAD_PRIORITY_BELOW_NORMAL)=='number')
168168
assert(type(uv.constants.THREAD_PRIORITY_LOWEST)=='number')
169169

170-
local thread = uv.new_thread(function()
170+
local sem = uv.new_sem(0)
171+
local thread = uv.new_thread(function(sem)
171172
local _uv = require('luv')
172173
local self = _uv.thread_self()
174+
_uv.sem_wait(sem)
173175
local priority = assert(self:getpriority())
174176
print('priority in thread', priority)
175-
end)
177+
end, sem)
178+
179+
uv.sleep(100)
176180

177181
local priority = assert(thread:getpriority())
178182
print('default priority', priority)
179183

180184
assert(thread:setpriority(uv.constants.THREAD_PRIORITY_LOWEST))
181185
priority = assert(thread:getpriority())
182186
print('priority after change', priority)
187+
188+
sem:post()
183189
thread:join()
184190
end, "1.48.0")
185191

0 commit comments

Comments
 (0)