Compare commits

..

110 Commits

Author SHA1 Message Date
涵曦
38e5b1ca1c fix wrong date (#1501) 2021-11-09 13:55:55 +08:00
Cloud Wu
a0a138c002 Release v1.5.0 2021-11-09 13:37:17 +08:00
子熏
e35cad3053 fix #1494 socket read阻塞问题 (#1495)
* fix #1494 socket read阻塞问题

* 优化readfunc

Co-authored-by: zixun <lb151450@alibaba-inc.com>
2021-10-29 13:55:54 +08:00
yxt945
8f7e19310c add json support (#1493) 2021-10-27 21:00:25 +08:00
云风
c008476417 Support string address, see #1490 (#1491) 2021-10-22 17:43:07 +08:00
Cloud Wu
599181ceaf bugfix 2021-10-22 10:14:33 +08:00
Cloud Wu
432b0c07f9 array may hash part only 2021-10-13 23:02:40 +08:00
Cloud Wu
58da8a1a22 Use lua_isinteger instead 2021-10-13 15:41:30 +08:00
Cloud Wu
0e11c78575 avoid string number 2021-10-13 15:36:19 +08:00
Cloud Wu
f80e2cd9b1 bugfix: See #1484 2021-10-13 12:57:55 +08:00
Cloud Wu
0ae839b2dd Check req size , See #1481 2021-09-30 11:24:36 +08:00
云风
0ebecb4e6d add ispurewhite : is white and not shared object, see #1479 (#1480) 2021-09-27 11:26:28 +08:00
Cloud Wu
9cbab6f5ec avoid warnings 2021-09-27 08:52:23 +08:00
Cloud Wu
edbed9f980 bugfix: Don't need inc sending since dw_buffer is not NULL 2021-09-26 11:57:32 +08:00
zhuilang
987bb3df47 modify from http folder (#1470)
* delete unused var and add used arg

* use interface from stream
2021-09-01 07:12:27 +08:00
云风
54e733a76f Request stream (#1463)
* split request/response

* stream api

* fix: global variable (#1464)

* fix global variable

* fix global variable

* Add httpc.head

Co-authored-by: JTrancender <jie-email@jie-trancender.org>
2021-08-30 10:00:57 +08:00
colin
6ee8d23ac4 ssl request support sni(Server Name Indication) (#1460)
* ssl support sni(Server Name Indication)

* ssl support sni(Server Name Indication)
2021-08-23 16:54:01 +08:00
JTrancender
fdc4b35281 feat: add mongo self batch insert (#1456)
* feat: add mongo self batch insert

* perf: add safe batch insert test

* perf: modify some words
2021-08-18 15:11:40 +08:00
Cloud Wu
740b91533d Fix #1452 2021-08-09 19:31:32 +08:00
coder
0207b9210a Update service_logger.c (#1451)
格式化时间改为通用的结构,修复windows上时间显示不正确问题
2021-08-05 16:36:49 +08:00
mrCerberus
0e9c0c34a9 'http请求C没有大写' (#1444)
Co-authored-by: 王羽平.Cerberus <wangyuping.cerberus@snda.com>
2021-07-21 20:33:22 +08:00
noname
d9762b8f38 sharedata 去掉多余的value字段 (#1442) 2021-07-15 20:14:05 +08:00
云风
72a6182601 bugfix #1434 (#1439) 2021-07-15 09:35:31 +08:00
涵曦
39e0aaa27e Fix typo (#1436) 2021-07-10 07:24:07 +08:00
colin
338f53fbe0 use _mm_pause (#1433) 2021-07-01 19:29:48 +08:00
colin
5c154d34dc improve spinlock (#1431)
* improve spinlock

* improve spinlock

* typo

Co-authored-by: colin <linzhengqun@163.com>
2021-06-30 12:42:58 +08:00
子熏
2bfecfceae fix lua-seri rb_read (#1427)
Co-authored-by: zixun <lvzxiun@gmail.com>
2021-06-23 15:52:46 +08:00
cm
3900067026 bugfix:params changed when cluster sender is connecting (#1420)
Co-authored-by: LiuYang <cm.yang@alibaba-inc.com>
2021-06-08 17:03:20 +08:00
zhuilang
c90e288601 Update skynet.lua (#1416)
update skynet.wait
2021-06-05 00:35:28 +08:00
colin
c2c585f73a freebsd compile error (#1414) 2021-06-03 19:13:30 +08:00
zhuilang
219b15e9ec Update skynet.lua (#1415)
update skynet.ret
2021-06-03 19:11:54 +08:00
Cloud Wu
30b534b558 ignore break sessions, See issue #1413 2021-06-03 08:13:22 +08:00
喜欢兰花山丘
f5eee7dcbb Update socket_server.c (#1410)
1. 去除冗余代码
2. fix 保存第一现场的错误码
2021-05-30 06:52:02 +08:00
Cloud Wu
ef4af820d5 Do not resume the fd after closing, see #1408 2021-05-21 10:15:21 +08:00
Cloud Wu
71317d8502 Add socket.resolve, see #1398 2021-05-17 11:16:24 +08:00
t0350
6ad8da0993 when skynet.init require the loading module caused circular dependency (#1403) 2021-05-16 22:26:39 +08:00
蒋祥龙
28d47d96b9 1.修复前一个ssl错误没清,导致后面的正常ssl调用操作 获取到这个错误 (#1401) 2021-05-14 21:07:27 +08:00
t0350
1e6781bd41 add coroutine close for skynetco (#1392)
Co-authored-by: jietao.cjt <jietao.cjt@alibaba-inc.com>
2021-04-29 14:40:37 +08:00
Cloud Wu
f61a27ac6b fix #1388 2021-04-19 18:45:56 +08:00
Cloud Wu
1020c403dc revert wakeup_response 2021-04-15 08:58:29 +00:00
云风
f0d24ac718 Bugfix #1385 (#1387) 2021-04-15 10:52:57 +08:00
云风
52d65c5dcb Redis ACL (#1381)
* Support redis ACL
See #1380
2021-04-10 07:02:38 +08:00
busymage
2b71c2755a Fix boostrap only support 1 param. (#1377) 2021-04-07 19:08:29 +08:00
Cloud Wu
df2803469e Add report_error(), this may fix issue #1376 2021-04-07 07:26:02 +00:00
Cloud Wu
bc71164a99 close may be better than shutdown 2021-04-07 03:47:19 +00:00
Cloud Wu
e69f876867 raise error on start, fix #1374 2021-04-07 03:39:22 +00:00
Cloud Wu
f9dc6a3756 fix SOCKET_CLOSE event after socket.close() See #1373 2021-04-07 03:25:55 +00:00
Cloud Wu
0f5867534c remove unused socket.lock/socket.unlock 2021-04-07 02:54:09 +00:00
Cloud Wu
ceb278adf6 Add shutdown to client_socket for testing 2021-04-06 16:46:14 +08:00
Cloud Wu
85dde52107 minor bugfix : socket_server_poll never returns -1 2021-04-06 16:45:51 +08:00
Cloud Wu
8ad6622936 halfclose_read should call before force_close 2021-04-06 15:47:32 +08:00
Cloud Wu
ba26c8a432 Do not close on error, see #1373 2021-04-06 10:46:41 +08:00
Cloud Wu
96c702bb3c bugfix 2021-04-02 10:33:23 +08:00
Cloud Wu
811ef52b3d fix build 2021-03-30 14:55:02 +08:00
Cloud Wu
e0d4fda24e Update lua 5.4.3 2021-03-30 10:09:43 +08:00
Cloud Wu
11165ce727 fix #1368 2021-03-29 14:27:37 +08:00
Cloud Wu
22b5fbf3b8 fix #1369 2021-03-29 14:23:40 +08:00
云风
9228b05dfe socket.onclose() (#1364)
* Add socket.onclose() and close socket immediately when socket channel closed by peer

* Revert read_socket(), Add SOCKET_MORE, See #1358

* remove warning log , because it's not a rare case any more. See #1358
2021-03-19 18:52:09 +08:00
风---自由
a39c9b8b10 clean时增加对macosx下的编译目录生效 (#1361)
* fix: macosx build error

* 🐎ci(makefile): clean时增加macosx下的编译目录

Co-authored-by: xiaojin <xiaojin@onemt.com.cn>
2021-03-11 15:18:13 +08:00
Cloud Wu
037c3a5c48 do not close fd if no handler.disconnect, #1358 2021-03-11 12:31:37 +08:00
Cloud Wu
094b20aae7 fix half close issue, #1358 2021-03-11 11:32:27 +08:00
风---自由
bf5eacfe80 fix: macosx build error (#1360)
Co-authored-by: xiaojin <xiaojin@onemt.com.cn>
2021-03-10 17:35:17 +08:00
Cloud Wu
fd12f2ea8e Merge branch 'master' of github.com:cloudwu/skynet 2021-03-10 15:25:14 +08:00
Cloud Wu
7c0c13b07b close unmanaged socket, see issue #1358 2021-03-10 15:24:33 +08:00
EfveZombie
b0dda0483d fix websocket bugs (#1359)
* fix websocket bugs

* revert write_frame

Co-authored-by: 郑非凡 <efve.zff@alibaba-inc.com>
2021-03-10 12:35:56 +08:00
Cloud Wu
6f2866e5c4 avoid warnings 2021-03-10 12:11:02 +08:00
Cloud Wu
213c9966e4 fix build 2021-03-10 12:01:15 +08:00
Cloud Wu
072d782651 Update lua 5.4.3 rc1 2021-03-10 11:53:37 +08:00
Cloud Wu
600a64327e fix halfclose bug, See detail #1358 2021-03-09 17:45:03 +08:00
Cloud Wu
0d9bfcb721 Atomic variable should be volatile, #1356 #1317 2021-03-09 10:54:49 +08:00
Cloud Wu
8d895ab2f0 See #1317 2021-03-05 21:36:52 +08:00
Cloud Wu
c12a7d1b99 Remove unused include 2021-03-04 11:40:15 +08:00
Cloud Wu
77c965e9e8 Don't support old MacOSX 2021-03-03 17:08:12 +08:00
Cloud Wu
0d949678b9 shutdown write when write error, See #1346 2021-03-01 11:18:56 +08:00
Cloud Wu
29a212db8e Shutdown SHUT_RD after read 0, See #1346 for detail 2021-02-25 22:36:36 +08:00
Cloud Wu
ccac1d4526 fix #1348 2021-02-25 19:59:12 +08:00
Cloud Wu
4bb883b989 report write error 2021-02-24 17:02:23 +08:00
Cloud Wu
6a8ca29d99 set result for SOCKET_CLOSE, see #1347 2021-02-24 11:54:47 +08:00
Cloud Wu
6a4a2482fc expand buffer faster 2021-02-24 11:33:35 +08:00
Cloud Wu
04dd921c5f Fix socket half close issues, see #1346 for details 2021-02-24 10:57:54 +08:00
Cloud Wu
8e32cc5283 Merge branch 'master' of github.com:cloudwu/skynet 2021-02-23 16:19:15 +08:00
Cloud Wu
8e45679d3d bugfix : coroutine.wrap for snlua 2021-02-23 16:19:01 +08:00
风---自由
b626198592 fix global var (#1342)
Co-authored-by: xiaojin <xiaojin@onemt.com.cn>
2021-02-22 12:24:00 +08:00
Cloud Wu
eebd44da9d fix #1341 2021-02-22 12:00:32 +08:00
Cloud Wu
51a942d095 bugfix: disable fd reading flag after shutdown read 2021-02-22 09:41:59 +08:00
Cloud Wu
6d941a6de1 support nil in array, fix #1340 2021-02-22 08:40:16 +08:00
云风
1a0461a9af Halfclose (#1338)
* post SOCKET_CLOSE when HALFCLOSE_READ, see #1331

* Add closing status

* Add closing stat in info, See #1333
2021-02-03 16:49:40 +08:00
Cloud Wu
e23ad8fc6b fix type 2021-02-03 16:04:01 +08:00
Cloud Wu
4534fb5269 pcall execute_module, #1331 2021-01-25 20:31:04 +08:00
Cloud Wu
41cc6d226c remove debug print 2021-01-25 10:47:06 +08:00
Cloud Wu
4532d4f146 Fix skynet.require recursion issue, See #1331 2021-01-25 10:34:01 +08:00
Cloud Wu
30279f2f8d Add gameserver embed mode, see #1326 2021-01-18 16:56:46 +08:00
风---自由
4c9c9ce6bd debug_console add dbgcmd command (#1325) 2021-01-18 10:41:30 +08:00
hong
cfb638243f skynet.exit (#1324) 2021-01-14 11:03:32 +08:00
云风
4cb84ac881 skynet.init rework, See #1322 (#1323)
* skynet.init rework, See #1322

* restore init_list #1322

* xpcall init_all, See #1322
2021-01-14 10:23:58 +08:00
lsg2020
53a74ca029 websocket upgrade (#1279) 2021-01-12 10:43:42 +08:00
子熏
2e06706323 fix atomic of macosx (#1320) See #1317
Co-authored-by: zixun <lvzxiun@gmail.com>
2021-01-11 17:41:03 +08:00
Cloud Wu
649aee1a23 use unsigned long instead of uint32_t to match atomic ulong 2021-01-11 17:32:37 +08:00
Cloud Wu
933dbbd570 lua-bson atomic #1317 2021-01-11 17:30:04 +08:00
Cloud Wu
1e5e8354d7 stm_copy.reference is _Atomic type, #1317 2021-01-11 17:20:16 +08:00
Cloud Wu
bc2fba560f CAS expected should not be _Atomic type, #1317 2021-01-11 17:10:26 +08:00
Cloud Wu
1ed4fdf94d Use _Atomic type for socket.sending and socket.udpconnecting. See #1317 2021-01-11 16:57:25 +08:00
子熏
c0e3421462 bugfix ltls init #1314 (#1318)
* bugfix ltls init #1314

* 修改init失败信息

* fix destructor

Co-authored-by: zixun <lvzxiun@gmail.com>
2021-01-11 15:15:25 +08:00
云风
b164e3a8a9 use stdatomic (#1317) 2021-01-11 10:54:34 +08:00
coder
284df5430b service_logger.c localtime崩溃,换成线程安全的localtime_r (#1311) 2020-12-28 15:42:16 +08:00
Cloud Wu
4d88f96ed6 bugfix: make proto shared 2020-12-18 19:20:15 +08:00
Cloud Wu
8022a53e50 O(1) fork_queue pop, See #1277 2020-12-16 23:05:52 +08:00
t0350
698d032a9b fix #1275 (#1276)
Co-authored-by: jietao.cjt <jietao.cjt@alibaba-inc.com>
2020-12-14 10:16:56 +08:00
Cloud Wu
8eac155b1b reset connection if node_sender exist, see issue #1273 2020-12-12 14:44:28 +08:00
Cloud Wu
e8a48483ab Check address changes before raise error, fix #1273 2020-12-11 12:02:32 +08:00
99 changed files with 2457 additions and 1337 deletions

View File

@@ -1,4 +1,4 @@
This is a modify version of lua 5.4.2 .
This is a modify version of lua 5.4.3 .
For detail ,
Shared Proto : http://lua-users.org/lists/lua-l/2014-03/msg00489.html

View File

@@ -39,7 +39,7 @@ const char lua_ident[] =
/*
** Test for a valid index.
** Test for a valid index (one that is not the 'nilvalue').
** '!ttisnil(o)' implies 'o != &G(L)->nilvalue', so it is not needed.
** However, it covers the most common cases in a faster way.
*/
@@ -74,7 +74,8 @@ static TValue *index2value (lua_State *L, int idx) {
return &G(L)->nilvalue; /* it has no upvalues */
else {
CClosure *func = clCvalue(s2v(ci->func));
return (idx <= func->nupvalues) ? &func->upvalue[idx-1] : &G(L)->nilvalue;
return (idx <= func->nupvalues) ? &func->upvalue[idx-1]
: &G(L)->nilvalue;
}
}
}
@@ -172,7 +173,7 @@ LUA_API int lua_gettop (lua_State *L) {
LUA_API void lua_settop (lua_State *L, int idx) {
CallInfo *ci;
StkId func;
StkId func, newtop;
ptrdiff_t diff; /* difference for new top */
lua_lock(L);
ci = L->ci;
@@ -187,9 +188,26 @@ LUA_API void lua_settop (lua_State *L, int idx) {
api_check(L, -(idx+1) <= (L->top - (func + 1)), "invalid new top");
diff = idx + 1; /* will "subtract" index (as it is negative) */
}
if (diff < 0 && hastocloseCfunc(ci->nresults))
luaF_close(L, L->top + diff, LUA_OK);
L->top += diff; /* correct top only after closing any upvalue */
api_check(L, L->tbclist < L->top, "previous pop of an unclosed slot");
newtop = L->top + diff;
if (diff < 0 && L->tbclist >= newtop) {
lua_assert(hastocloseCfunc(ci->nresults));
luaF_close(L, newtop, CLOSEKTOP, 0);
}
L->top = newtop; /* correct top only after closing any upvalue */
lua_unlock(L);
}
LUA_API void lua_closeslot (lua_State *L, int idx) {
StkId level;
lua_lock(L);
level = index2stack(L, idx);
api_check(L, hastocloseCfunc(L->ci->nresults) && L->tbclist == level,
"no variable to close at given level");
luaF_close(L, level, CLOSEKTOP, 0);
level = index2stack(L, idx); /* stack may be moved */
setnilvalue(s2v(level));
lua_unlock(L);
}
@@ -629,11 +647,21 @@ static int auxgetstr (lua_State *L, const TValue *t, const char *k) {
}
/*
** Get the global table in the registry. Since all predefined
** indices in the registry were inserted right when the registry
** was created and never removed, they must always be in the array
** part of the registry.
*/
#define getGtable(L) \
(&hvalue(&G(L)->l_registry)->array[LUA_RIDX_GLOBALS - 1])
LUA_API int lua_getglobal (lua_State *L, const char *name) {
Table *reg;
const TValue *G;
lua_lock(L);
reg = hvalue(&G(L)->l_registry);
return auxgetstr(L, luaH_getint(reg, LUA_RIDX_GLOBALS), name);
G = getGtable(L);
return auxgetstr(L, G, name);
}
@@ -811,10 +839,10 @@ static void auxsetstr (lua_State *L, const TValue *t, const char *k) {
LUA_API void lua_setglobal (lua_State *L, const char *name) {
Table *reg;
const TValue *G;
lua_lock(L); /* unlock done in 'auxsetstr' */
reg = hvalue(&G(L)->l_registry);
auxsetstr(L, luaH_getint(reg, LUA_RIDX_GLOBALS), name);
G = getGtable(L);
auxsetstr(L, G, name);
}
@@ -861,12 +889,10 @@ LUA_API void lua_seti (lua_State *L, int idx, lua_Integer n) {
static void aux_rawset (lua_State *L, int idx, TValue *key, int n) {
Table *t;
TValue *slot;
lua_lock(L);
api_checknelems(L, n);
t = gettable(L, idx);
slot = luaH_set(L, t, key);
setobj2t(L, slot, s2v(L->top - 1));
luaH_set(L, t, key, s2v(L->top - 1));
invalidateTMcache(t);
luaC_barrierback(L, obj2gco(t), s2v(L->top - 1));
L->top -= n;
@@ -912,7 +938,7 @@ LUA_API int lua_setmetatable (lua_State *L, int objindex) {
}
switch (ttype(obj)) {
case LUA_TTABLE: {
if (isshared(hvalue(obj)))
if (l_unlikely(isshared(hvalue(obj))))
luaG_runerror(L, "can't setmetatable to shared table");
hvalue(obj)->metatable = mt;
if (mt) {
@@ -1055,8 +1081,7 @@ LUA_API int lua_pcallk (lua_State *L, int nargs, int nresults, int errfunc,
static void set_env (lua_State *L, LClosure *f) {
if (f->nupvalues >= 1) { /* does it have an upvalue? */
/* get global table from registry */
Table *reg = hvalue(&G(L)->l_registry);
const TValue *gt = luaH_getint(reg, LUA_RIDX_GLOBALS);
const TValue *gt = getGtable(L);
/* set global table as 1st upvalue of 'f' (may be LUA_ENV) */
setobj(L, f->upvals[0]->v, gt);
luaC_barrier(L, f->upvals[0], gt);
@@ -1110,7 +1135,7 @@ LUA_API void lua_sharestring (lua_State *L, int index) {
LUA_API void lua_clonetable(lua_State *L, const void * tp) {
Table *t = cast(Table *, tp);
if (!isshared(t))
if (l_unlikely(!isshared(t)))
luaG_runerror(L, "Not a shared table");
lua_lock(L);
@@ -1284,8 +1309,7 @@ LUA_API void lua_toclose (lua_State *L, int idx) {
lua_lock(L);
o = index2stack(L, idx);
nresults = L->ci->nresults;
api_check(L, L->openupval == NULL || uplevel(L->openupval) <= o,
"marked index below or equal new one");
api_check(L, L->tbclist < o, "given index below or equal a marked one");
luaF_newtbcupval(L, o); /* create new to-be-closed upvalue */
if (!hastocloseCfunc(nresults)) /* function not marked yet? */
L->ci->nresults = codeNresults(nresults); /* mark it */

View File

@@ -42,6 +42,8 @@
#define hastocloseCfunc(n) ((n) < LUA_MULTRET)
/* Map [-1, inf) (range of 'nresults') into (-inf, -2] */
#define codeNresults(n) (-(n) - 3)
#define decodeNresults(n) (-(n) - 3)
#endif

View File

@@ -190,7 +190,7 @@ LUALIB_API int luaL_argerror (lua_State *L, int arg, const char *extramsg) {
}
int luaL_typeerror (lua_State *L, int arg, const char *tname) {
LUALIB_API int luaL_typeerror (lua_State *L, int arg, const char *tname) {
const char *msg;
const char *typearg; /* name for the type of the actual argument */
if (luaL_getmetafield(L, arg, "__name") == LUA_TSTRING)
@@ -378,7 +378,7 @@ LUALIB_API int luaL_checkoption (lua_State *L, int arg, const char *def,
** but without 'msg'.)
*/
LUALIB_API void luaL_checkstack (lua_State *L, int space, const char *msg) {
if (!lua_checkstack(L, space)) {
if (l_unlikely(!lua_checkstack(L, space))) {
if (msg)
luaL_error(L, "stack overflow (%s)", msg);
else
@@ -388,20 +388,20 @@ LUALIB_API void luaL_checkstack (lua_State *L, int space, const char *msg) {
LUALIB_API void luaL_checktype (lua_State *L, int arg, int t) {
if (lua_type(L, arg) != t)
if (l_unlikely(lua_type(L, arg) != t))
tag_error(L, arg, t);
}
LUALIB_API void luaL_checkany (lua_State *L, int arg) {
if (lua_type(L, arg) == LUA_TNONE)
if (l_unlikely(lua_type(L, arg) == LUA_TNONE))
luaL_argerror(L, arg, "value expected");
}
LUALIB_API const char *luaL_checklstring (lua_State *L, int arg, size_t *len) {
const char *s = lua_tolstring(L, arg, len);
if (!s) tag_error(L, arg, LUA_TSTRING);
if (l_unlikely(!s)) tag_error(L, arg, LUA_TSTRING);
return s;
}
@@ -420,7 +420,7 @@ LUALIB_API const char *luaL_optlstring (lua_State *L, int arg,
LUALIB_API lua_Number luaL_checknumber (lua_State *L, int arg) {
int isnum;
lua_Number d = lua_tonumberx(L, arg, &isnum);
if (!isnum)
if (l_unlikely(!isnum))
tag_error(L, arg, LUA_TNUMBER);
return d;
}
@@ -442,7 +442,7 @@ static void interror (lua_State *L, int arg) {
LUALIB_API lua_Integer luaL_checkinteger (lua_State *L, int arg) {
int isnum;
lua_Integer d = lua_tointegerx(L, arg, &isnum);
if (!isnum) {
if (l_unlikely(!isnum)) {
interror(L, arg);
}
return d;
@@ -475,7 +475,7 @@ static void *resizebox (lua_State *L, int idx, size_t newsize) {
lua_Alloc allocf = lua_getallocf(L, &ud);
UBox *box = (UBox *)lua_touserdata(L, idx);
void *temp = allocf(ud, box->box, box->bsize, newsize);
if (temp == NULL && newsize > 0) { /* allocation error? */
if (l_unlikely(temp == NULL && newsize > 0)) { /* allocation error? */
lua_pushliteral(L, "not enough memory");
lua_error(L); /* raise a memory error */
}
@@ -515,13 +515,22 @@ static void newbox (lua_State *L) {
#define buffonstack(B) ((B)->b != (B)->init.b)
/*
** Whenever buffer is accessed, slot 'idx' must either be a box (which
** cannot be NULL) or it is a placeholder for the buffer.
*/
#define checkbufferlevel(B,idx) \
lua_assert(buffonstack(B) ? lua_touserdata(B->L, idx) != NULL \
: lua_touserdata(B->L, idx) == (void*)B)
/*
** Compute new size for buffer 'B', enough to accommodate extra 'sz'
** bytes.
*/
static size_t newbuffsize (luaL_Buffer *B, size_t sz) {
size_t newsize = B->size * 2; /* double buffer size */
if (MAX_SIZET - sz < B->n) /* overflow in (B->n + sz)? */
if (l_unlikely(MAX_SIZET - sz < B->n)) /* overflow in (B->n + sz)? */
return luaL_error(B->L, "buffer too large");
if (newsize < B->n + sz) /* double is not big enough? */
newsize = B->n + sz;
@@ -531,10 +540,11 @@ static size_t newbuffsize (luaL_Buffer *B, size_t sz) {
/*
** Returns a pointer to a free area with at least 'sz' bytes in buffer
** 'B'. 'boxidx' is the relative position in the stack where the
** buffer's box is or should be.
** 'B'. 'boxidx' is the relative position in the stack where is the
** buffer's box or its placeholder.
*/
static char *prepbuffsize (luaL_Buffer *B, size_t sz, int boxidx) {
checkbufferlevel(B, boxidx);
if (B->size - B->n >= sz) /* enough space? */
return B->b + B->n;
else {
@@ -545,10 +555,9 @@ static char *prepbuffsize (luaL_Buffer *B, size_t sz, int boxidx) {
if (buffonstack(B)) /* buffer already has a box? */
newbuff = (char *)resizebox(L, boxidx, newsize); /* resize it */
else { /* no box yet */
lua_pushnil(L); /* reserve slot for final result */
lua_remove(L, boxidx); /* remove placeholder */
newbox(L); /* create a new box */
/* move box (and slot) to its intended position */
lua_rotate(L, boxidx - 1, 2);
lua_insert(L, boxidx); /* move box to its intended position */
lua_toclose(L, boxidx);
newbuff = (char *)resizebox(L, boxidx, newsize);
memcpy(newbuff, B->b, B->n * sizeof(char)); /* copy original content */
@@ -583,11 +592,11 @@ LUALIB_API void luaL_addstring (luaL_Buffer *B, const char *s) {
LUALIB_API void luaL_pushresult (luaL_Buffer *B) {
lua_State *L = B->L;
checkbufferlevel(B, -1);
lua_pushlstring(L, B->b, B->n);
if (buffonstack(B)) {
lua_copy(L, -1, -3); /* move string to reserved slot */
lua_pop(L, 2); /* pop string and box (closing the box) */
}
if (buffonstack(B))
lua_closeslot(L, -2); /* close the box */
lua_remove(L, -2); /* remove box or placeholder from the stack */
}
@@ -622,6 +631,7 @@ LUALIB_API void luaL_buffinit (lua_State *L, luaL_Buffer *B) {
B->b = B->init.b;
B->n = 0;
B->size = LUAL_BUFFERSIZE;
lua_pushlightuserdata(L, (void*)B); /* push placeholder */
}
@@ -639,10 +649,14 @@ LUALIB_API char *luaL_buffinitsize (lua_State *L, luaL_Buffer *B, size_t sz) {
** =======================================================
*/
/* index of free-list header */
#define freelist 0
/* index of free-list header (after the predefined values) */
#define freelist (LUA_RIDX_LAST + 1)
/*
** The previously freed references form a linked list:
** t[freelist] is the index of a first free index, or zero if list is
** empty; t[t[freelist]] is the index of the second element; etc.
*/
LUALIB_API int luaL_ref (lua_State *L, int t) {
int ref;
if (lua_isnil(L, -1)) {
@@ -650,9 +664,16 @@ LUALIB_API int luaL_ref (lua_State *L, int t) {
return LUA_REFNIL; /* 'nil' has a unique fixed reference */
}
t = lua_absindex(L, t);
lua_rawgeti(L, t, freelist); /* get first free element */
ref = (int)lua_tointeger(L, -1); /* ref = t[freelist] */
lua_pop(L, 1); /* remove it from stack */
if (lua_rawgeti(L, t, freelist) == LUA_TNIL) { /* first access? */
ref = 0; /* list is empty */
lua_pushinteger(L, 0); /* initialize as an empty list */
lua_rawseti(L, t, freelist); /* ref = t[freelist] = 0 */
}
else { /* already initialized */
lua_assert(lua_isinteger(L, -1));
ref = (int)lua_tointeger(L, -1); /* ref = t[freelist] */
}
lua_pop(L, 1); /* remove element from stack */
if (ref != 0) { /* any free element? */
lua_rawgeti(L, t, ref); /* remove it from list */
lua_rawseti(L, t, freelist); /* (t[freelist] = t[ref]) */
@@ -668,6 +689,7 @@ LUALIB_API void luaL_unref (lua_State *L, int t, int ref) {
if (ref >= 0) {
t = lua_absindex(L, t);
lua_rawgeti(L, t, freelist);
lua_assert(lua_isinteger(L, -1));
lua_rawseti(L, t, ref); /* t[ref] = t[freelist] */
lua_pushinteger(L, ref);
lua_rawseti(L, t, freelist); /* t[freelist] = ref */
@@ -851,7 +873,7 @@ LUALIB_API lua_Integer luaL_len (lua_State *L, int idx) {
int isnum;
lua_len(L, idx);
l = lua_tointegerx(L, -1, &isnum);
if (!isnum)
if (l_unlikely(!isnum))
luaL_error(L, "object length is not an integer");
lua_pop(L, 1); /* remove object */
return l;
@@ -1064,7 +1086,7 @@ static void warnfon (void *ud, const char *message, int tocont) {
LUALIB_API lua_State *luaL_newstate (void) {
lua_State *L = lua_newstate(l_alloc, NULL);
if (L) {
if (l_likely(L)) {
lua_atpanic(L, &panic);
lua_setwarnf(L, warnfoff, L); /* default is warnings off */
}
@@ -1113,7 +1135,7 @@ luaL_initcodecache(void) {
}
static const void *
load(const char *key) {
load_proto(const char *key) {
if (CC.L == NULL)
return NULL;
SPIN_LOCK(&CC)
@@ -1128,7 +1150,7 @@ load(const char *key) {
}
static const void *
save(const char *key, const void * proto) {
save_proto(const char *key, const void * proto) {
lua_State *L;
const void * result = NULL;
@@ -1201,7 +1223,7 @@ LUALIB_API int luaL_loadfilex (lua_State *L, const char *filename,
if (level == CACHE_OFF) {
return luaL_loadfilex_(L, filename, mode);
}
const void * proto = load(filename);
const void * proto = load_proto(filename);
if (proto) {
lua_clonefunction(L, proto);
return LUA_OK;
@@ -1224,7 +1246,7 @@ LUALIB_API int luaL_loadfilex (lua_State *L, const char *filename,
}
lua_sharefunction(eL, -1);
proto = lua_topointer(eL, -1);
const void * oldv = save(filename, proto);
const void * oldv = save_proto(filename, proto);
if (oldv) {
lua_close(eL);
lua_clonefunction(L, oldv);

View File

@@ -12,6 +12,7 @@
#include <stddef.h>
#include <stdio.h>
#include "luaconf.h"
#include "lua.h"
@@ -132,10 +133,10 @@ LUALIB_API void (luaL_requiref) (lua_State *L, const char *modname,
(luaL_checkversion(L), luaL_newlibtable(L,l), luaL_setfuncs(L,l,0))
#define luaL_argcheck(L, cond,arg,extramsg) \
((void)((cond) || luaL_argerror(L, (arg), (extramsg))))
((void)(luai_likely(cond) || luaL_argerror(L, (arg), (extramsg))))
#define luaL_argexpected(L,cond,arg,tname) \
((void)((cond) || luaL_typeerror(L, (arg), (tname))))
((void)(luai_likely(cond) || luaL_typeerror(L, (arg), (tname))))
#define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL))
#define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL))
@@ -159,6 +160,22 @@ LUALIB_API void (luaL_requiref) (lua_State *L, const char *modname,
#define luaL_pushfail(L) lua_pushnil(L)
/*
** Internal assertions for in-house debugging
*/
#if !defined(lua_assert)
#if defined LUAI_ASSERT
#include <assert.h>
#define lua_assert(c) assert(c)
#else
#define lua_assert(c) ((void)0)
#endif
#endif
/*
** {======================================================
** Generic Buffer manipulation

View File

@@ -138,7 +138,7 @@ static int luaB_setmetatable (lua_State *L) {
int t = lua_type(L, 2);
luaL_checktype(L, 1, LUA_TTABLE);
luaL_argexpected(L, t == LUA_TNIL || t == LUA_TTABLE, 2, "nil or table");
if (luaL_getmetafield(L, 1, "__metatable") != LUA_TNIL)
if (l_unlikely(luaL_getmetafield(L, 1, "__metatable") != LUA_TNIL))
return luaL_error(L, "cannot change a protected metatable");
lua_settop(L, 2);
lua_setmetatable(L, 1);
@@ -182,7 +182,8 @@ static int luaB_rawset (lua_State *L) {
static int pushmode (lua_State *L, int oldmode) {
lua_pushstring(L, (oldmode == LUA_GCINC) ? "incremental" : "generational");
lua_pushstring(L, (oldmode == LUA_GCINC) ? "incremental"
: "generational");
return 1;
}
@@ -299,7 +300,7 @@ static int luaB_ipairs (lua_State *L) {
static int load_aux (lua_State *L, int status, int envidx) {
if (status == LUA_OK) {
if (l_likely(status == LUA_OK)) {
if (envidx != 0) { /* 'env' parameter? */
lua_pushvalue(L, envidx); /* environment for loaded function */
if (!lua_setupvalue(L, -2, 1)) /* set it as 1st upvalue */
@@ -355,7 +356,7 @@ static const char *generic_reader (lua_State *L, void *ud, size_t *size) {
*size = 0;
return NULL;
}
else if (!lua_isstring(L, -1))
else if (l_unlikely(!lua_isstring(L, -1)))
luaL_error(L, "reader function must return a string");
lua_replace(L, RESERVEDSLOT); /* save string in reserved slot */
return lua_tolstring(L, RESERVEDSLOT, size);
@@ -393,7 +394,7 @@ static int dofilecont (lua_State *L, int d1, lua_KContext d2) {
static int luaB_dofile (lua_State *L) {
const char *fname = luaL_optstring(L, 1, NULL);
lua_settop(L, 1);
if (luaL_loadfile(L, fname) != LUA_OK)
if (l_unlikely(luaL_loadfile(L, fname) != LUA_OK))
return lua_error(L);
lua_callk(L, 0, LUA_MULTRET, 0, dofilecont);
return dofilecont(L, 0, 0);
@@ -401,7 +402,7 @@ static int luaB_dofile (lua_State *L) {
static int luaB_assert (lua_State *L) {
if (lua_toboolean(L, 1)) /* condition is true? */
if (l_likely(lua_toboolean(L, 1))) /* condition is true? */
return lua_gettop(L); /* return all arguments */
else { /* error */
luaL_checkany(L, 1); /* there must be a condition */
@@ -437,7 +438,7 @@ static int luaB_select (lua_State *L) {
** ignored).
*/
static int finishpcall (lua_State *L, int status, lua_KContext extra) {
if (status != LUA_OK && status != LUA_YIELD) { /* error? */
if (l_unlikely(status != LUA_OK && status != LUA_YIELD)) { /* error? */
lua_pushboolean(L, 0); /* first result (false) */
lua_pushvalue(L, -2); /* error message */
return 2; /* return false, msg */

View File

@@ -314,15 +314,6 @@ void luaK_patchtohere (FuncState *fs, int list) {
}
/*
** MAXimum number of successive Instructions WiTHout ABSolute line
** information.
*/
#if !defined(MAXIWTHABS)
#define MAXIWTHABS 120
#endif
/* limit for difference between lines in relative line info. */
#define LIMLINEDIFF 0x80
@@ -337,13 +328,13 @@ void luaK_patchtohere (FuncState *fs, int list) {
static void savelineinfo (FuncState *fs, Proto *f, int line) {
int linedif = line - fs->previousline;
int pc = fs->pc - 1; /* last instruction coded */
if (abs(linedif) >= LIMLINEDIFF || fs->iwthabs++ > MAXIWTHABS) {
if (abs(linedif) >= LIMLINEDIFF || fs->iwthabs++ >= MAXIWTHABS) {
luaM_growvector(fs->ls->L, f->abslineinfo, fs->nabslineinfo,
f->sizeabslineinfo, AbsLineInfo, MAX_INT, "lines");
f->abslineinfo[fs->nabslineinfo].pc = pc;
f->abslineinfo[fs->nabslineinfo++].line = line;
linedif = ABSLINEINFO; /* signal that there is absolute information */
fs->iwthabs = 0; /* restart counter */
fs->iwthabs = 1; /* restart counter */
}
luaM_growvector(fs->ls->L, f->lineinfo, pc, f->sizelineinfo, ls_byte,
MAX_INT, "opcodes");
@@ -545,11 +536,14 @@ static void freeexps (FuncState *fs, expdesc *e1, expdesc *e2) {
** and try to reuse constants. Because some values should not be used
** as keys (nil cannot be a key, integer keys can collapse with float
** keys), the caller must provide a useful 'key' for indexing the cache.
** Note that all functions share the same table, so entering or exiting
** a function can make some indices wrong.
*/
static int addk (FuncState *fs, TValue *key, TValue *v) {
TValue val;
lua_State *L = fs->ls->L;
Proto *f = fs->f;
TValue *idx = luaH_set(L, fs->ls->h, key); /* index scanner table */
const TValue *idx = luaH_get(fs->ls->h, key); /* query scanner table */
int k, oldsize;
if (ttisinteger(idx)) { /* is there an index there? */
k = cast_int(ivalue(idx));
@@ -563,7 +557,8 @@ static int addk (FuncState *fs, TValue *key, TValue *v) {
k = fs->nk;
/* numerical value does not need GC barrier;
table has no metatable, so it does not need to invalidate cache */
setivalue(idx, k);
setivalue(&val, k);
luaH_finishset(L, fs->ls->h, key, idx, &val);
luaM_growvector(L, f->k, k, f->sizek, TValue, MAXARG_Ax, "constants");
while (oldsize < f->sizek) setnilvalue(&f->k[oldsize++]);
setobj(L, &f->k[k], v);
@@ -763,7 +758,7 @@ void luaK_dischargevars (FuncState *fs, expdesc *e) {
break;
}
case VLOCAL: { /* already in a register */
e->u.info = e->u.var.sidx;
e->u.info = e->u.var.ridx;
e->k = VNONRELOC; /* becomes a non-relocatable value */
break;
}
@@ -1036,7 +1031,7 @@ void luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) {
switch (var->k) {
case VLOCAL: {
freeexp(fs, ex);
exp2reg(fs, ex, var->u.var.sidx); /* compute 'ex' into proper place */
exp2reg(fs, ex, var->u.var.ridx); /* compute 'ex' into proper place */
return;
}
case VUPVAL: {
@@ -1276,7 +1271,7 @@ void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) {
}
else {
/* register index of the table */
t->u.ind.t = (t->k == VLOCAL) ? t->u.var.sidx: t->u.info;
t->u.ind.t = (t->k == VLOCAL) ? t->u.var.ridx: t->u.info;
if (isKstr(fs, k)) {
t->u.ind.idx = k->u.info; /* literal string */
t->k = VINDEXSTR;
@@ -1303,7 +1298,8 @@ static int validop (int op, TValue *v1, TValue *v2) {
case LUA_OPBAND: case LUA_OPBOR: case LUA_OPBXOR:
case LUA_OPSHL: case LUA_OPSHR: case LUA_OPBNOT: { /* conversion errors */
lua_Integer i;
return (tointegerns(v1, &i) && tointegerns(v2, &i));
return (luaV_tointegerns(v1, &i, LUA_FLOORN2I) &&
luaV_tointegerns(v2, &i, LUA_FLOORN2I));
}
case LUA_OPDIV: case LUA_OPIDIV: case LUA_OPMOD: /* division by 0 */
return (nvalue(v2) != 0);

View File

@@ -31,14 +31,14 @@ static lua_State *getco (lua_State *L) {
*/
static int auxresume (lua_State *L, lua_State *co, int narg) {
int status, nres;
if (!lua_checkstack(co, narg)) {
if (l_unlikely(!lua_checkstack(co, narg))) {
lua_pushliteral(L, "too many arguments to resume");
return -1; /* error flag */
}
lua_xmove(L, co, narg);
status = lua_resume(co, L, narg, &nres);
if (status == LUA_OK || status == LUA_YIELD) {
if (!lua_checkstack(L, nres + 1)) {
if (l_likely(status == LUA_OK || status == LUA_YIELD)) {
if (l_unlikely(!lua_checkstack(L, nres + 1))) {
lua_pop(co, nres); /* remove results anyway */
lua_pushliteral(L, "too many results to resume");
return -1; /* error flag */
@@ -57,7 +57,7 @@ static int luaB_coresume (lua_State *L) {
lua_State *co = getco(L);
int r;
r = auxresume(L, co, lua_gettop(L) - 1);
if (r < 0) {
if (l_unlikely(r < 0)) {
lua_pushboolean(L, 0);
lua_insert(L, -2);
return 2; /* return false + error message */
@@ -73,10 +73,13 @@ static int luaB_coresume (lua_State *L) {
static int luaB_auxwrap (lua_State *L) {
lua_State *co = lua_tothread(L, lua_upvalueindex(1));
int r = auxresume(L, co, lua_gettop(L));
if (r < 0) { /* error? */
if (l_unlikely(r < 0)) { /* error? */
int stat = lua_status(co);
if (stat != LUA_OK && stat != LUA_YIELD) /* error in the coroutine? */
lua_resetthread(co); /* close its tbc variables */
if (stat != LUA_OK && stat != LUA_YIELD) { /* error in the coroutine? */
stat = lua_resetthread(co); /* close its tbc variables */
lua_assert(stat != LUA_OK);
lua_xmove(co, L, 1); /* copy error message */
}
if (stat != LUA_ERRMEM && /* not a memory error and ... */
lua_type(L, -1) == LUA_TSTRING) { /* ... error object is a string? */
luaL_where(L, 1); /* add extra info, if available */

View File

@@ -33,7 +33,7 @@ static const char *const HOOKKEY = "_HOOKKEY";
** checked.
*/
static void checkstack (lua_State *L, lua_State *L1, int n) {
if (L != L1 && !lua_checkstack(L1, n))
if (l_unlikely(L != L1 && !lua_checkstack(L1, n)))
luaL_error(L, "stack overflow");
}
@@ -152,6 +152,7 @@ static int db_getinfo (lua_State *L) {
lua_State *L1 = getthread(L, &arg);
const char *options = luaL_optstring(L, arg+2, "flnSrtu");
checkstack(L, L1, 3);
luaL_argcheck(L, options[0] != '>', arg + 2, "invalid option '>'");
if (lua_isfunction(L, arg + 1)) { /* info about a function? */
options = lua_pushfstring(L, ">%s", options); /* add '>' to 'options' */
lua_pushvalue(L, arg + 1); /* move function to 'L1' stack */
@@ -212,7 +213,7 @@ static int db_getlocal (lua_State *L) {
lua_Debug ar;
const char *name;
int level = (int)luaL_checkinteger(L, arg + 1);
if (!lua_getstack(L1, level, &ar)) /* out of range? */
if (l_unlikely(!lua_getstack(L1, level, &ar))) /* out of range? */
return luaL_argerror(L, arg+1, "level out of range");
checkstack(L, L1, 1);
name = lua_getlocal(L1, &ar, nvar);
@@ -237,7 +238,7 @@ static int db_setlocal (lua_State *L) {
lua_Debug ar;
int level = (int)luaL_checkinteger(L, arg + 1);
int nvar = (int)luaL_checkinteger(L, arg + 2);
if (!lua_getstack(L1, level, &ar)) /* out of range? */
if (l_unlikely(!lua_getstack(L1, level, &ar))) /* out of range? */
return luaL_argerror(L, arg+1, "level out of range");
luaL_checkany(L, arg+3);
lua_settop(L, arg+3);
@@ -377,7 +378,7 @@ static int db_sethook (lua_State *L) {
}
if (!luaL_getsubtable(L, LUA_REGISTRYINDEX, HOOKKEY)) {
/* table just created; initialize it */
lua_pushstring(L, "k");
lua_pushliteral(L, "k");
lua_setfield(L, -2, "__mode"); /** hooktable.__mode = "k" */
lua_pushvalue(L, -1);
lua_setmetatable(L, -2); /* metatable(hooktable) = hooktable */
@@ -420,7 +421,7 @@ static int db_debug (lua_State *L) {
for (;;) {
char buffer[250];
lua_writestringerror("%s", "lua_debug> ");
if (fgets(buffer, sizeof(buffer), stdin) == 0 ||
if (fgets(buffer, sizeof(buffer), stdin) == NULL ||
strcmp(buffer, "cont\n") == 0)
return 0;
if (luaL_loadbuffer(L, buffer, strlen(buffer), "=(debug command)") ||

View File

@@ -33,8 +33,6 @@
#define noLuaClosure(f) ((f) == NULL || (f)->c.tt == LUA_VCCL)
/* inverse of 'pcRel' */
#define invpcRel(pc, p) ((p)->code + (pc) + 1)
static const char *funcnamefromcode (lua_State *L, CallInfo *ci,
const char **name);
@@ -48,10 +46,16 @@ static int currentpc (CallInfo *ci) {
/*
** Get a "base line" to find the line corresponding to an instruction.
** For that, search the array of absolute line info for the largest saved
** instruction smaller or equal to the wanted instruction. A special
** case is when there is no absolute info or the instruction is before
** the first absolute one.
** Base lines are regularly placed at MAXIWTHABS intervals, so usually
** an integer division gets the right place. When the source file has
** large sequences of empty/comment lines, it may need extra entries,
** so the original estimate needs a correction.
** If the original estimate is -1, the initial 'if' ensures that the
** 'while' will run at least once.
** The assertion that the estimate is a lower bound for the correct base
** is valid as long as the debug info has been generated with the same
** value for MAXIWTHABS or smaller. (Previous releases use a little
** smaller value.)
*/
static int getbaseline (const Proto *f, int pc, int *basepc) {
if (f->sizeabslineinfo == 0 || pc < f->abslineinfo[0].pc) {
@@ -59,20 +63,12 @@ static int getbaseline (const Proto *f, int pc, int *basepc) {
return f->linedefined;
}
else {
unsigned int i;
if (pc >= f->abslineinfo[f->sizeabslineinfo - 1].pc)
i = f->sizeabslineinfo - 1; /* instruction is after last saved one */
else { /* binary search */
unsigned int j = f->sizeabslineinfo - 1; /* pc < anchorlines[j] */
i = 0; /* abslineinfo[i] <= pc */
while (i < j - 1) {
unsigned int m = (j + i) / 2;
if (pc >= f->abslineinfo[m].pc)
i = m;
else
j = m;
}
}
int i = cast_uint(pc) / MAXIWTHABS - 1; /* get an estimate */
/* estimate must be a lower bond of the correct base */
lua_assert(i < 0 ||
(i < f->sizeabslineinfo && f->abslineinfo[i].pc <= pc));
while (i + 1 < f->sizeabslineinfo && pc >= f->abslineinfo[i + 1].pc)
i++; /* low estimate; adjust it */
*basepc = f->abslineinfo[i].pc;
return f->abslineinfo[i].line;
}
@@ -305,8 +301,8 @@ static void collectvalidlines (lua_State *L, Closure *f) {
sethvalue2s(L, L->top, t); /* push it on stack */
api_incr_top(L);
setbtvalue(&v); /* boolean 'true' to be the value of all indices */
for (i = 0; i < p->sizelineinfo; i++) { /* for all lines with code */
currentline = nextline(p, currentline, i);
for (i = 0; i < p->sizelineinfo; i++) { /* for all instructions */
currentline = nextline(p, currentline, i); /* get its line */
luaH_setint(L, t, currentline, &v); /* table[line] = true */
}
}
@@ -629,12 +625,10 @@ static const char *funcnamefromcode (lua_State *L, CallInfo *ci,
case OP_LEN: tm = TM_LEN; break;
case OP_CONCAT: tm = TM_CONCAT; break;
case OP_EQ: tm = TM_EQ; break;
case OP_LT: case OP_LE: case OP_LTI: case OP_LEI:
*name = "order"; /* '<=' can call '__lt', etc. */
return "metamethod";
case OP_CLOSE: case OP_RETURN:
*name = "close";
return "metamethod";
/* no cases for OP_EQI and OP_EQK, as they don't call metamethods */
case OP_LT: case OP_LTI: case OP_GTI: tm = TM_LT; break;
case OP_LE: case OP_LEI: case OP_GEI: tm = TM_LE; break;
case OP_CLOSE: case OP_RETURN: tm = TM_CLOSE; break;
default:
return NULL; /* cannot find a reasonable name */
}
@@ -647,14 +641,18 @@ static const char *funcnamefromcode (lua_State *L, CallInfo *ci,
/*
** The subtraction of two potentially unrelated pointers is
** not ISO C, but it should not crash a program; the subsequent
** checks are ISO C and ensure a correct result.
** Check whether pointer 'o' points to some value in the stack
** frame of the current function. Because 'o' may not point to a
** value in this stack, we cannot compare it with the region
** boundaries (undefined behaviour in ISO C).
*/
static int isinstack (CallInfo *ci, const TValue *o) {
StkId base = ci->func + 1;
ptrdiff_t i = cast(StkId, o) - base;
return (0 <= i && i < (ci->top - base) && s2v(base + i) == o);
StkId pos;
for (pos = ci->func + 1; pos < ci->top; pos++) {
if (o == s2v(pos))
return 1;
}
return 0; /* not found */
}
@@ -697,6 +695,19 @@ l_noret luaG_typeerror (lua_State *L, const TValue *o, const char *op) {
}
l_noret luaG_callerror (lua_State *L, const TValue *o) {
CallInfo *ci = L->ci;
const char *name = NULL; /* to avoid warnings */
const char *what = (isLua(ci)) ? funcnamefromcode(L, ci, &name) : NULL;
if (what != NULL) {
const char *t = luaT_objtypename(L, o);
luaG_runerror(L, "%s '%s' is not callable (a %s value)", what, name, t);
}
else
luaG_typeerror(L, o, "call");
}
l_noret luaG_forerror (lua_State *L, const TValue *o, const char *what) {
luaG_runerror(L, "bad 'for' %s (number expected, got %s)",
what, luaT_objtypename(L, o));
@@ -722,7 +733,7 @@ l_noret luaG_opinterror (lua_State *L, const TValue *p1,
*/
l_noret luaG_tointerror (lua_State *L, const TValue *p1, const TValue *p2) {
lua_Integer temp;
if (!tointegerns(p1, &temp))
if (!luaV_tointegerns(p1, &temp, LUA_FLOORN2I))
p2 = p1;
luaG_runerror(L, "number%s has no integer representation", varinfo(L, p2));
}
@@ -780,16 +791,30 @@ l_noret luaG_runerror (lua_State *L, const char *fmt, ...) {
/*
** Check whether new instruction 'newpc' is in a different line from
** previous instruction 'oldpc'.
** previous instruction 'oldpc'. More often than not, 'newpc' is only
** one or a few instructions after 'oldpc' (it must be after, see
** caller), so try to avoid calling 'luaG_getfuncline'. If they are
** too far apart, there is a good chance of a ABSLINEINFO in the way,
** so it goes directly to 'luaG_getfuncline'.
*/
static int changedline (const Proto *p, int oldpc, int newpc) {
if (p->lineinfo == NULL) /* no debug information? */
return 0;
while (oldpc++ < newpc) {
if (p->lineinfo[oldpc] != 0)
return (luaG_getfuncline(p, oldpc - 1) != luaG_getfuncline(p, newpc));
if (newpc - oldpc < MAXIWTHABS / 2) { /* not too far apart? */
int delta = 0; /* line diference */
int pc = oldpc;
for (;;) {
int lineinfo = p->lineinfo[++pc];
if (lineinfo == ABSLINEINFO)
break; /* cannot compute delta; fall through */
delta += lineinfo;
if (pc == newpc)
return (delta != 0); /* delta computed successfully */
}
}
return 0; /* no line changes between positions */
/* either instructions are too far apart or there is an absolute line
info in the way; compute line difference explicitly */
return (luaG_getfuncline(p, oldpc) != luaG_getfuncline(p, newpc));
}
@@ -797,20 +822,19 @@ static int changedline (const Proto *p, int oldpc, int newpc) {
** Traces the execution of a Lua function. Called before the execution
** of each opcode, when debug is on. 'L->oldpc' stores the last
** instruction traced, to detect line changes. When entering a new
** function, 'npci' will be zero and will test as a new line without
** the need for 'oldpc'; so, 'oldpc' does not need to be initialized
** before. Some exceptional conditions may return to a function without
** updating 'oldpc'. In that case, 'oldpc' may be invalid; if so, it is
** reset to zero. (A wrong but valid 'oldpc' at most causes an extra
** call to a line hook.)
** function, 'npci' will be zero and will test as a new line whatever
** the value of 'oldpc'. Some exceptional conditions may return to
** a function without setting 'oldpc'. In that case, 'oldpc' may be
** invalid; if so, use zero as a valid value. (A wrong but valid 'oldpc'
** at most causes an extra call to a line hook.)
** This function is not "Protected" when called, so it should correct
** 'L->top' before calling anything that can run the GC.
*/
int luaG_traceexec (lua_State *L, const Instruction *pc) {
CallInfo *ci = L->ci;
lu_byte mask = L->hookmask;
const Proto *p = ci_func(ci)->p;
int counthook;
/* 'L->oldpc' may be invalid; reset it in this case */
int oldpc = (L->oldpc < p->sizecode) ? L->oldpc : 0;
if (!(mask & (LUA_MASKLINE | LUA_MASKCOUNT))) { /* no hooks? */
ci->u.l.trap = 0; /* don't need to stop again */
return 0; /* turn off 'trap' */
@@ -826,15 +850,16 @@ int luaG_traceexec (lua_State *L, const Instruction *pc) {
ci->callstatus &= ~CIST_HOOKYIELD; /* erase mark */
return 1; /* do not call hook again (VM yielded, so it did not move) */
}
if (!isIT(*(ci->u.l.savedpc - 1)))
L->top = ci->top; /* prepare top */
if (!isIT(*(ci->u.l.savedpc - 1))) /* top not being used? */
L->top = ci->top; /* correct top */
if (counthook)
luaD_hook(L, LUA_HOOKCOUNT, -1, 0, 0); /* call count hook */
if (mask & LUA_MASKLINE) {
/* 'L->oldpc' may be invalid; use zero in this case */
int oldpc = (L->oldpc < p->sizecode) ? L->oldpc : 0;
int npci = pcRel(pc, p);
if (npci == 0 || /* call linehook when enter a new function, */
pc <= invpcRel(oldpc, p) || /* when jump back (loop), or when */
changedline(p, oldpc, npci)) { /* enter new line */
if (npci <= oldpc || /* call hook when jump back (loop), */
changedline(p, oldpc, npci)) { /* or when enter new line */
int newline = luaG_getfuncline(p, npci);
luaD_hook(L, LUA_HOOKLINE, newline, 0, 0); /* call line hook */
}

View File

@@ -26,11 +26,22 @@
*/
#define ABSLINEINFO (-0x80)
/*
** MAXimum number of successive Instructions WiTHout ABSolute line
** information. (A power of two allows fast divisions.)
*/
#if !defined(MAXIWTHABS)
#define MAXIWTHABS 128
#endif
LUAI_FUNC int luaG_getfuncline (const Proto *f, int pc);
LUAI_FUNC const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n,
StkId *pos);
LUAI_FUNC l_noret luaG_typeerror (lua_State *L, const TValue *o,
const char *opname);
LUAI_FUNC l_noret luaG_callerror (lua_State *L, const TValue *o);
LUAI_FUNC l_noret luaG_forerror (lua_State *L, const TValue *o,
const char *what);
LUAI_FUNC l_noret luaG_concaterror (lua_State *L, const TValue *p1,

View File

@@ -98,11 +98,12 @@ void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop) {
setsvalue2s(L, oldtop, luaS_newliteral(L, "error in error handling"));
break;
}
case CLOSEPROTECT: {
case LUA_OK: { /* special case only for closing upvalues */
setnilvalue(s2v(oldtop)); /* no error message */
break;
}
default: {
lua_assert(errorstatus(errcode)); /* real error */
setobjs2s(L, oldtop, L->top - 1); /* error message on current top */
break;
}
@@ -118,17 +119,13 @@ l_noret luaD_throw (lua_State *L, int errcode) {
}
else { /* thread has no error handler */
global_State *g = G(L);
errcode = luaF_close(L, L->stack, errcode); /* close all upvalues */
L->status = cast_byte(errcode); /* mark it as dead */
errcode = luaE_resetthread(L, errcode); /* close all upvalues */
if (g->mainthread->errorJmp) { /* main thread has a handler? */
setobjs2s(L, g->mainthread->top++, L->top - 1); /* copy error obj. */
luaD_throw(g->mainthread, errcode); /* re-throw in main thread */
}
else { /* no handler at all; abort */
if (g->panic) { /* panic function? */
luaD_seterrorobj(L, errcode, L->top); /* assume EXTRA_STACK */
if (L->ci->top < L->top)
L->ci->top = L->top; /* pushing msg. can break this invariant */
lua_unlock(L);
g->panic(L); /* call panic function (last chance to jump out) */
}
@@ -163,9 +160,8 @@ int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
static void correctstack (lua_State *L, StkId oldstack, StkId newstack) {
CallInfo *ci;
UpVal *up;
if (oldstack == newstack)
return; /* stack address did not change */
L->top = (L->top - oldstack) + newstack;
L->tbclist = (L->tbclist - oldstack) + newstack;
for (up = L->openupval; up != NULL; up = up->u.open.next)
up->v = s2v((uplevel(up) - oldstack) + newstack);
for (ci = L->ci; ci != NULL; ci = ci->previous) {
@@ -181,19 +177,35 @@ static void correctstack (lua_State *L, StkId oldstack, StkId newstack) {
#define ERRORSTACKSIZE (LUAI_MAXSTACK + 200)
/*
** Reallocate the stack to a new size, correcting all pointers into
** it. (There are pointers to a stack from its upvalues, from its list
** of call infos, plus a few individual pointers.) The reallocation is
** done in two steps (allocation + free) because the correction must be
** done while both addresses (the old stack and the new one) are valid.
** (In ISO C, any pointer use after the pointer has been deallocated is
** undefined behavior.)
** In case of allocation error, raise an error or return false according
** to 'raiseerror'.
*/
int luaD_reallocstack (lua_State *L, int newsize, int raiseerror) {
int lim = stacksize(L);
StkId newstack = luaM_reallocvector(L, L->stack,
lim + EXTRA_STACK, newsize + EXTRA_STACK, StackValue);
int oldsize = stacksize(L);
int i;
StkId newstack = luaM_reallocvector(L, NULL, 0,
newsize + EXTRA_STACK, StackValue);
lua_assert(newsize <= LUAI_MAXSTACK || newsize == ERRORSTACKSIZE);
if (unlikely(newstack == NULL)) { /* reallocation failed? */
if (l_unlikely(newstack == NULL)) { /* reallocation failed? */
if (raiseerror)
luaM_error(L);
else return 0; /* do not raise an error */
}
for (; lim < newsize; lim++)
setnilvalue(s2v(newstack + lim + EXTRA_STACK)); /* erase new segment */
/* number of elements to be copied to the new stack */
i = ((oldsize <= newsize) ? oldsize : newsize) + EXTRA_STACK;
memcpy(newstack, L->stack, i * sizeof(StackValue));
for (; i < newsize + EXTRA_STACK; i++)
setnilvalue(s2v(newstack + i)); /* erase new segment */
correctstack(L, L->stack, newstack);
luaM_freearray(L, L->stack, oldsize + EXTRA_STACK);
L->stack = newstack;
L->stack_last = L->stack + newsize;
return 1;
@@ -206,7 +218,7 @@ int luaD_reallocstack (lua_State *L, int newsize, int raiseerror) {
*/
int luaD_growstack (lua_State *L, int n, int raiseerror) {
int size = stacksize(L);
if (unlikely(size > LUAI_MAXSTACK)) {
if (l_unlikely(size > LUAI_MAXSTACK)) {
/* if stack is larger than maximum, thread is already using the
extra space reserved for errors, that is, thread is handling
a stack error; cannot grow further than that. */
@@ -222,7 +234,7 @@ int luaD_growstack (lua_State *L, int n, int raiseerror) {
newsize = LUAI_MAXSTACK;
if (newsize < needed) /* but must respect what was asked for */
newsize = needed;
if (likely(newsize <= LUAI_MAXSTACK))
if (l_likely(newsize <= LUAI_MAXSTACK))
return luaD_reallocstack(L, newsize, raiseerror);
else { /* stack overflow */
/* add extra size to be able to handle the error message */
@@ -297,8 +309,8 @@ void luaD_hook (lua_State *L, int event, int line,
if (hook && L->allowhook) { /* make sure there is a hook */
int mask = CIST_HOOKED;
CallInfo *ci = L->ci;
ptrdiff_t top = savestack(L, L->top);
ptrdiff_t ci_top = savestack(L, ci->top);
ptrdiff_t top = savestack(L, L->top); /* preserve original 'top' */
ptrdiff_t ci_top = savestack(L, ci->top); /* idem for 'ci->top' */
lua_Debug ar;
ar.event = event;
ar.currentline = line;
@@ -308,8 +320,10 @@ void luaD_hook (lua_State *L, int event, int line,
ci->u2.transferinfo.ftransfer = ftransfer;
ci->u2.transferinfo.ntransfer = ntransfer;
}
if (isLua(ci) && L->top < ci->top)
L->top = ci->top; /* protect entire activation register */
luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */
if (L->top + LUA_MINSTACK > ci->top)
if (ci->top < L->top + LUA_MINSTACK)
ci->top = L->top + LUA_MINSTACK;
L->allowhook = 0; /* cannot call hooks inside a hook */
ci->callstatus |= mask;
@@ -331,38 +345,40 @@ void luaD_hook (lua_State *L, int event, int line,
** active.
*/
void luaD_hookcall (lua_State *L, CallInfo *ci) {
int hook = (ci->callstatus & CIST_TAIL) ? LUA_HOOKTAILCALL : LUA_HOOKCALL;
Proto *p;
if (!(L->hookmask & LUA_MASKCALL)) /* some other hook? */
return; /* don't call hook */
p = clLvalue(s2v(ci->func))->p;
L->top = ci->top; /* prepare top */
ci->u.l.savedpc++; /* hooks assume 'pc' is already incremented */
luaD_hook(L, hook, -1, 1, p->numparams);
ci->u.l.savedpc--; /* correct 'pc' */
L->oldpc = 0; /* set 'oldpc' for new function */
if (L->hookmask & LUA_MASKCALL) { /* is call hook on? */
int event = (ci->callstatus & CIST_TAIL) ? LUA_HOOKTAILCALL
: LUA_HOOKCALL;
Proto *p = ci_func(ci)->p;
ci->u.l.savedpc++; /* hooks assume 'pc' is already incremented */
luaD_hook(L, event, -1, 1, p->numparams);
ci->u.l.savedpc--; /* correct 'pc' */
}
}
static StkId rethook (lua_State *L, CallInfo *ci, StkId firstres, int nres) {
ptrdiff_t oldtop = savestack(L, L->top); /* hook may change top */
int delta = 0;
if (isLuacode(ci)) {
Proto *p = ci_func(ci)->p;
if (p->is_vararg)
delta = ci->u.l.nextraargs + p->numparams + 1;
if (L->top < ci->top)
L->top = ci->top; /* correct top to run hook */
}
/*
** Executes a return hook for Lua and C functions and sets/corrects
** 'oldpc'. (Note that this correction is needed by the line hook, so it
** is done even when return hooks are off.)
*/
static void rethook (lua_State *L, CallInfo *ci, int nres) {
if (L->hookmask & LUA_MASKRET) { /* is return hook on? */
StkId firstres = L->top - nres; /* index of first result */
int delta = 0; /* correction for vararg functions */
int ftransfer;
if (isLua(ci)) {
Proto *p = ci_func(ci)->p;
if (p->is_vararg)
delta = ci->u.l.nextraargs + p->numparams + 1;
}
ci->func += delta; /* if vararg, back to virtual 'func' */
ftransfer = cast(unsigned short, firstres - ci->func);
luaD_hook(L, LUA_HOOKRET, -1, ftransfer, nres); /* call it */
ci->func -= delta;
}
if (isLua(ci = ci->previous))
L->oldpc = pcRel(ci->u.l.savedpc, ci_func(ci)->p); /* update 'oldpc' */
return restorestack(L, oldtop);
L->oldpc = pcRel(ci->u.l.savedpc, ci_func(ci)->p); /* set 'oldpc' */
}
@@ -374,8 +390,8 @@ static StkId rethook (lua_State *L, CallInfo *ci, StkId firstres, int nres) {
void luaD_tryfuncTM (lua_State *L, StkId func) {
const TValue *tm = luaT_gettmbyobj(L, s2v(func), TM_CALL);
StkId p;
if (unlikely(ttisnil(tm)))
luaG_typeerror(L, s2v(func), "call"); /* nothing to call */
if (l_unlikely(ttisnil(tm)))
luaG_callerror(L, s2v(func)); /* nothing to call */
for (p = L->top; p > func; p--) /* open space for metamethod */
setobjs2s(L, p, p-1);
L->top++; /* stack space pre-allocated by the caller */
@@ -399,27 +415,34 @@ static void moveresults (lua_State *L, StkId res, int nres, int wanted) {
case 1: /* one value needed */
if (nres == 0) /* no results? */
setnilvalue(s2v(res)); /* adjust with nil */
else
else /* at least one result */
setobjs2s(L, res, L->top - nres); /* move it to proper place */
L->top = res + 1;
return;
case LUA_MULTRET:
wanted = nres; /* we want all results */
break;
default: /* multiple results (or to-be-closed variables) */
default: /* two/more results and/or to-be-closed variables */
if (hastocloseCfunc(wanted)) { /* to-be-closed variables? */
ptrdiff_t savedres = savestack(L, res);
luaF_close(L, res, LUA_OK); /* may change the stack */
res = restorestack(L, savedres);
wanted = codeNresults(wanted); /* correct value */
L->ci->callstatus |= CIST_CLSRET; /* in case of yields */
L->ci->u2.nres = nres;
luaF_close(L, res, CLOSEKTOP, 1);
L->ci->callstatus &= ~CIST_CLSRET;
if (L->hookmask) /* if needed, call hook after '__close's */
rethook(L, L->ci, nres);
res = restorestack(L, savedres); /* close and hook can move stack */
wanted = decodeNresults(wanted);
if (wanted == LUA_MULTRET)
wanted = nres;
wanted = nres; /* we want all results */
}
break;
}
/* generic case */
firstresult = L->top - nres; /* index of first result */
/* move all results to correct place */
for (i = 0; i < nres && i < wanted; i++)
if (nres > wanted) /* extra results? */
nres = wanted; /* don't need them */
for (i = 0; i < nres; i++) /* move all results to correct place */
setobjs2s(L, res + i, firstresult + i);
for (; i < wanted; i++) /* complete wanted number of results */
setnilvalue(s2v(res + i));
@@ -428,15 +451,21 @@ static void moveresults (lua_State *L, StkId res, int nres, int wanted) {
/*
** Finishes a function call: calls hook if necessary, removes CallInfo,
** moves current number of results to proper place.
** Finishes a function call: calls hook if necessary, moves current
** number of results to proper place, and returns to previous call
** info. If function has to close variables, hook must be called after
** that.
*/
void luaD_poscall (lua_State *L, CallInfo *ci, int nres) {
if (L->hookmask)
L->top = rethook(L, ci, L->top - nres, nres);
L->ci = ci->previous; /* back to caller */
int wanted = ci->nresults;
if (l_unlikely(L->hookmask && !hastocloseCfunc(wanted)))
rethook(L, ci, nres);
/* move results to proper place */
moveresults(L, ci->func, nres, ci->nresults);
moveresults(L, ci->func, nres, wanted);
/* function cannot be in any of these cases when returning */
lua_assert(!(ci->callstatus &
(CIST_HOOKED | CIST_YPCALL | CIST_FIN | CIST_TRAN | CIST_CLSRET)));
L->ci = ci->previous; /* back to caller (after closing variables) */
}
@@ -495,7 +524,7 @@ CallInfo *luaD_precall (lua_State *L, StkId func, int nresults) {
ci->top = L->top + LUA_MINSTACK;
ci->func = func;
lua_assert(ci->top <= L->stack_last);
if (L->hookmask & LUA_MASKCALL) {
if (l_unlikely(L->hookmask & LUA_MASKCALL)) {
int narg = cast_int(L->top - func) - 1;
luaD_hook(L, LUA_HOOKCALL, -1, 1, narg);
}
@@ -541,7 +570,7 @@ CallInfo *luaD_precall (lua_State *L, StkId func, int nresults) {
static void ccall (lua_State *L, StkId func, int nResults, int inc) {
CallInfo *ci;
L->nCcalls += inc;
if (unlikely(getCcalls(L) >= LUAI_MAXCCALLS))
if (l_unlikely(getCcalls(L) >= LUAI_MAXCCALLS))
luaE_checkcstack(L);
if ((ci = luaD_precall(L, func, nResults)) != NULL) { /* Lua function? */
ci->callstatus = CIST_FRESH; /* mark that it is a "fresh" execute */
@@ -568,27 +597,74 @@ void luaD_callnoyield (lua_State *L, StkId func, int nResults) {
/*
** Completes the execution of an interrupted C function, calling its
** continuation function.
** Finish the job of 'lua_pcallk' after it was interrupted by an yield.
** (The caller, 'finishCcall', does the final call to 'adjustresults'.)
** The main job is to complete the 'luaD_pcall' called by 'lua_pcallk'.
** If a '__close' method yields here, eventually control will be back
** to 'finishCcall' (when that '__close' method finally returns) and
** 'finishpcallk' will run again and close any still pending '__close'
** methods. Similarly, if a '__close' method errs, 'precover' calls
** 'unroll' which calls ''finishCcall' and we are back here again, to
** close any pending '__close' methods.
** Note that, up to the call to 'luaF_close', the corresponding
** 'CallInfo' is not modified, so that this repeated run works like the
** first one (except that it has at least one less '__close' to do). In
** particular, field CIST_RECST preserves the error status across these
** multiple runs, changing only if there is a new error.
*/
static void finishCcall (lua_State *L, int status) {
CallInfo *ci = L->ci;
int n;
/* must have a continuation and must be able to call it */
lua_assert(ci->u.c.k != NULL && yieldable(L));
/* error status can only happen in a protected call */
lua_assert((ci->callstatus & CIST_YPCALL) || status == LUA_YIELD);
if (ci->callstatus & CIST_YPCALL) { /* was inside a pcall? */
ci->callstatus &= ~CIST_YPCALL; /* continuation is also inside it */
L->errfunc = ci->u.c.old_errfunc; /* with the same error function */
static int finishpcallk (lua_State *L, CallInfo *ci) {
int status = getcistrecst(ci); /* get original status */
if (l_likely(status == LUA_OK)) /* no error? */
status = LUA_YIELD; /* was interrupted by an yield */
else { /* error */
StkId func = restorestack(L, ci->u2.funcidx);
L->allowhook = getoah(ci->callstatus); /* restore 'allowhook' */
luaF_close(L, func, status, 1); /* can yield or raise an error */
func = restorestack(L, ci->u2.funcidx); /* stack may be moved */
luaD_seterrorobj(L, status, func);
luaD_shrinkstack(L); /* restore stack size in case of overflow */
setcistrecst(ci, LUA_OK); /* clear original status */
}
ci->callstatus &= ~CIST_YPCALL;
L->errfunc = ci->u.c.old_errfunc;
/* if it is here, there were errors or yields; unlike 'lua_pcallk',
do not change status */
return status;
}
/*
** Completes the execution of a C function interrupted by an yield.
** The interruption must have happened while the function was either
** closing its tbc variables in 'moveresults' or executing
** 'lua_callk'/'lua_pcallk'. In the first case, it just redoes
** 'luaD_poscall'. In the second case, the call to 'finishpcallk'
** finishes the interrupted execution of 'lua_pcallk'. After that, it
** calls the continuation of the interrupted function and finally it
** completes the job of the 'luaD_call' that called the function. In
** the call to 'adjustresults', we do not know the number of results
** of the function called by 'lua_callk'/'lua_pcallk', so we are
** conservative and use LUA_MULTRET (always adjust).
*/
static void finishCcall (lua_State *L, CallInfo *ci) {
int n; /* actual number of results from C function */
if (ci->callstatus & CIST_CLSRET) { /* was returning? */
lua_assert(hastocloseCfunc(ci->nresults));
n = ci->u2.nres; /* just redo 'luaD_poscall' */
/* don't need to reset CIST_CLSRET, as it will be set again anyway */
}
else {
int status = LUA_YIELD; /* default if there were no errors */
/* must have a continuation and must be able to call it */
lua_assert(ci->u.c.k != NULL && yieldable(L));
if (ci->callstatus & CIST_YPCALL) /* was inside a 'lua_pcallk'? */
status = finishpcallk(L, ci); /* finish it */
adjustresults(L, LUA_MULTRET); /* finish 'lua_callk' */
lua_unlock(L);
n = (*ci->u.c.k)(L, status, ci->u.c.ctx); /* call continuation */
lua_lock(L);
api_checknelems(L, n);
}
/* finish 'lua_callk'/'lua_pcall'; CIST_YPCALL and 'errfunc' already
handled */
adjustresults(L, ci->nresults);
lua_unlock(L);
n = (*ci->u.c.k)(L, status, ci->u.c.ctx); /* call continuation function */
lua_lock(L);
api_checknelems(L, n);
luaD_poscall(L, ci, n); /* finish 'luaD_call' */
}
@@ -596,18 +672,14 @@ static void finishCcall (lua_State *L, int status) {
/*
** Executes "full continuation" (everything in the stack) of a
** previously interrupted coroutine until the stack is empty (or another
** interruption long-jumps out of the loop). If the coroutine is
** recovering from an error, 'ud' points to the error status, which must
** be passed to the first continuation function (otherwise the default
** status is LUA_YIELD).
** interruption long-jumps out of the loop).
*/
static void unroll (lua_State *L, void *ud) {
CallInfo *ci;
if (ud != NULL) /* error status? */
finishCcall(L, *(int *)ud); /* finish 'lua_pcallk' callee */
UNUSED(ud);
while ((ci = L->ci) != &L->base_ci) { /* something in the stack */
if (!isLua(ci)) /* C function? */
finishCcall(L, LUA_YIELD); /* complete its execution */
finishCcall(L, ci); /* complete its execution */
else { /* Lua function */
luaV_finishOp(L); /* finish interrupted instruction */
luaV_execute(L, ci); /* execute down to higher C 'boundary' */
@@ -630,28 +702,6 @@ static CallInfo *findpcall (lua_State *L) {
}
/*
** Recovers from an error in a coroutine. Finds a recover point (if
** there is one) and completes the execution of the interrupted
** 'luaD_pcall'. If there is no recover point, returns zero.
*/
static int recover (lua_State *L, int status) {
StkId oldtop;
CallInfo *ci = findpcall(L);
if (ci == NULL) return 0; /* no recovery point */
/* "finish" luaD_pcall */
oldtop = restorestack(L, ci->u2.funcidx);
L->ci = ci;
L->allowhook = getoah(ci->callstatus); /* restore original 'allowhook' */
status = luaF_close(L, oldtop, status); /* may change the stack */
oldtop = restorestack(L, ci->u2.funcidx);
luaD_seterrorobj(L, status, oldtop);
luaD_shrinkstack(L); /* restore stack size in case of overflow */
L->errfunc = ci->u.c.old_errfunc;
return 1; /* continue running the coroutine */
}
/*
** Signal an error in the call to 'lua_resume', not in the execution
** of the coroutine itself. (Such errors should not be handled by any
@@ -683,8 +733,10 @@ static void resume (lua_State *L, void *ud) {
lua_assert(L->status == LUA_YIELD);
L->status = LUA_OK; /* mark that it is running (again) */
luaE_incCstack(L); /* control the C stack */
if (isLua(ci)) /* yielded inside a hook? */
if (isLua(ci)) { /* yielded inside a hook? */
L->top = firstArg; /* discard arguments */
luaV_execute(L, ci); /* just continue running Lua code */
}
else { /* 'common' yield */
if (ci->u.c.k != NULL) { /* does it have a continuation function? */
lua_unlock(L);
@@ -698,6 +750,26 @@ static void resume (lua_State *L, void *ud) {
}
}
/*
** Unrolls a coroutine in protected mode while there are recoverable
** errors, that is, errors inside a protected call. (Any error
** interrupts 'unroll', and this loop protects it again so it can
** continue.) Stops with a normal end (status == LUA_OK), an yield
** (status == LUA_YIELD), or an unprotected error ('findpcall' doesn't
** find a recover point).
*/
static int precover (lua_State *L, int status) {
CallInfo *ci;
while (errorstatus(status) && (ci = findpcall(L)) != NULL) {
L->ci = ci; /* go down to recovery functions */
setcistrecst(ci, status); /* status to finish 'pcall' */
status = luaD_rawrunprotected(L, unroll, NULL);
}
return status;
}
LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs,
int *nresults) {
int status;
@@ -715,11 +787,8 @@ LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs,
api_checknelems(L, (L->status == LUA_OK) ? nargs + 1 : nargs);
status = luaD_rawrunprotected(L, resume, &nargs);
/* continue running after recoverable errors */
while (errorstatus(status) && recover(L, status)) {
/* unroll continuation */
status = luaD_rawrunprotected(L, unroll, &status);
}
if (likely(!errorstatus(status)))
status = precover(L, status);
if (l_likely(!errorstatus(status)))
lua_assert(status == L->status); /* normal end or yield */
else { /* unrecoverable error */
L->status = cast_byte(status); /* mark thread as 'dead' */
@@ -745,22 +814,22 @@ LUA_API int lua_yieldk (lua_State *L, int nresults, lua_KContext ctx,
lua_lock(L);
ci = L->ci;
api_checknelems(L, nresults);
if (unlikely(!yieldable(L))) {
if (l_unlikely(!yieldable(L))) {
if (L != G(L)->mainthread)
luaG_runerror(L, "attempt to yield across a C-call boundary");
else
luaG_runerror(L, "attempt to yield from outside a coroutine");
}
L->status = LUA_YIELD;
ci->u2.nyield = nresults; /* save number of results */
if (isLua(ci)) { /* inside a hook? */
lua_assert(!isLuacode(ci));
api_check(L, nresults == 0, "hooks cannot yield values");
api_check(L, k == NULL, "hooks cannot continue after yielding");
ci->u2.nyield = 0; /* no results */
}
else {
if ((ci->u.c.k = k) != NULL) /* is there a continuation? */
ci->u.c.ctx = ctx; /* save context */
ci->u2.nyield = nresults; /* save number of results */
luaD_throw(L, LUA_YIELD);
}
lua_assert(ci->callstatus & CIST_HOOKED); /* must be inside a hook */
@@ -769,6 +838,45 @@ LUA_API int lua_yieldk (lua_State *L, int nresults, lua_KContext ctx,
}
/*
** Auxiliary structure to call 'luaF_close' in protected mode.
*/
struct CloseP {
StkId level;
int status;
};
/*
** Auxiliary function to call 'luaF_close' in protected mode.
*/
static void closepaux (lua_State *L, void *ud) {
struct CloseP *pcl = cast(struct CloseP *, ud);
luaF_close(L, pcl->level, pcl->status, 0);
}
/*
** Calls 'luaF_close' in protected mode. Return the original status
** or, in case of errors, the new status.
*/
int luaD_closeprotected (lua_State *L, ptrdiff_t level, int status) {
CallInfo *old_ci = L->ci;
lu_byte old_allowhooks = L->allowhook;
for (;;) { /* keep closing upvalues until no more errors */
struct CloseP pcl;
pcl.level = restorestack(L, level); pcl.status = status;
status = luaD_rawrunprotected(L, &closepaux, &pcl);
if (l_likely(status == LUA_OK)) /* no more errors? */
return pcl.status;
else { /* an error occurred; restore saved state and repeat */
L->ci = old_ci;
L->allowhook = old_allowhooks;
}
}
}
/*
** Call the C function 'func' in protected mode, restoring basic
** thread information ('allowhook', etc.) and in particular
@@ -782,13 +890,11 @@ int luaD_pcall (lua_State *L, Pfunc func, void *u,
ptrdiff_t old_errfunc = L->errfunc;
L->errfunc = ef;
status = luaD_rawrunprotected(L, func, u);
if (unlikely(status != LUA_OK)) { /* an error occurred? */
StkId oldtop = restorestack(L, old_top);
if (l_unlikely(status != LUA_OK)) { /* an error occurred? */
L->ci = old_ci;
L->allowhook = old_allowhooks;
status = luaF_close(L, oldtop, status);
oldtop = restorestack(L, old_top); /* previous call may change stack */
luaD_seterrorobj(L, status, oldtop);
status = luaD_closeprotected(L, old_top, status);
luaD_seterrorobj(L, status, restorestack(L, old_top));
luaD_shrinkstack(L); /* restore stack size in case of overflow */
}
L->errfunc = old_errfunc;

View File

@@ -23,7 +23,7 @@
** at every check.
*/
#define luaD_checkstackaux(L,n,pre,pos) \
if (L->stack_last - L->top <= (n)) \
if (l_unlikely(L->stack_last - L->top <= (n))) \
{ pre; luaD_growstack(L, n, 1); pos; } \
else { condmovestack(L,pre,pos); }
@@ -63,6 +63,7 @@ LUAI_FUNC CallInfo *luaD_precall (lua_State *L, StkId func, int nResults);
LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults);
LUAI_FUNC void luaD_callnoyield (lua_State *L, StkId func, int nResults);
LUAI_FUNC void luaD_tryfuncTM (lua_State *L, StkId func);
LUAI_FUNC int luaD_closeprotected (lua_State *L, ptrdiff_t level, int status);
LUAI_FUNC int luaD_pcall (lua_State *L, Pfunc func, void *u,
ptrdiff_t oldtop, ptrdiff_t ef);
LUAI_FUNC void luaD_poscall (lua_State *L, CallInfo *ci, int nres);

View File

@@ -101,115 +101,83 @@ UpVal *luaF_findupval (lua_State *L, StkId level) {
}
static void callclose (lua_State *L, void *ud) {
UNUSED(ud);
luaD_callnoyield(L, L->top - 3, 0);
}
/*
** Prepare closing method plus its arguments for object 'obj' with
** error message 'err'. (This function assumes EXTRA_STACK.)
** Call closing method for object 'obj' with error message 'err'. The
** boolean 'yy' controls whether the call is yieldable.
** (This function assumes EXTRA_STACK.)
*/
static int prepclosingmethod (lua_State *L, TValue *obj, TValue *err) {
static void callclosemethod (lua_State *L, TValue *obj, TValue *err, int yy) {
StkId top = L->top;
const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE);
if (ttisnil(tm)) /* no metamethod? */
return 0; /* nothing to call */
setobj2s(L, top, tm); /* will call metamethod... */
setobj2s(L, top + 1, obj); /* with 'self' as the 1st argument */
setobj2s(L, top + 2, err); /* and error msg. as 2nd argument */
L->top = top + 3; /* add function and arguments */
return 1;
if (yy)
luaD_call(L, top, 0);
else
luaD_callnoyield(L, top, 0);
}
/*
** Raise an error with message 'msg', inserting the name of the
** local variable at position 'level' in the stack.
** Check whether object at given level has a close metamethod and raise
** an error if not.
*/
static void varerror (lua_State *L, StkId level, const char *msg) {
int idx = cast_int(level - L->ci->func);
const char *vname = luaG_findlocal(L, L->ci, idx, NULL);
if (vname == NULL) vname = "?";
luaG_runerror(L, msg, vname);
static void checkclosemth (lua_State *L, StkId level) {
const TValue *tm = luaT_gettmbyobj(L, s2v(level), TM_CLOSE);
if (ttisnil(tm)) { /* no metamethod? */
int idx = cast_int(level - L->ci->func); /* variable index */
const char *vname = luaG_findlocal(L, L->ci, idx, NULL);
if (vname == NULL) vname = "?";
luaG_runerror(L, "variable '%s' got a non-closable value", vname);
}
}
/*
** Prepare and call a closing method. If status is OK, code is still
** inside the original protected call, and so any error will be handled
** there. Otherwise, a previous error already activated the original
** protected call, and so the call to the closing method must be
** protected here. (A status == CLOSEPROTECT behaves like a previous
** error, to also run the closing method in protected mode).
** If status is OK, the call to the closing method will be pushed
** at the top of the stack. Otherwise, values are pushed after
** the 'level' of the upvalue being closed, as everything after
** that won't be used again.
** Prepare and call a closing method.
** If status is CLOSEKTOP, the call to the closing method will be pushed
** at the top of the stack. Otherwise, values can be pushed right after
** the 'level' of the upvalue being closed, as everything after that
** won't be used again.
*/
static int callclosemth (lua_State *L, StkId level, int status) {
static void prepcallclosemth (lua_State *L, StkId level, int status, int yy) {
TValue *uv = s2v(level); /* value being closed */
if (likely(status == LUA_OK)) {
if (prepclosingmethod(L, uv, &G(L)->nilvalue)) /* something to call? */
callclose(L, NULL); /* call closing method */
else if (!l_isfalse(uv)) /* non-closable non-false value? */
varerror(L, level, "attempt to close non-closable variable '%s'");
TValue *errobj;
if (status == CLOSEKTOP)
errobj = &G(L)->nilvalue; /* error object is nil */
else { /* 'luaD_seterrorobj' will set top to level + 2 */
errobj = s2v(level + 1); /* error object goes after 'uv' */
luaD_seterrorobj(L, status, level + 1); /* set error object */
}
else { /* must close the object in protected mode */
ptrdiff_t oldtop;
level++; /* space for error message */
oldtop = savestack(L, level + 1); /* top will be after that */
luaD_seterrorobj(L, status, level); /* set error message */
if (prepclosingmethod(L, uv, s2v(level))) { /* something to call? */
int newstatus = luaD_pcall(L, callclose, NULL, oldtop, 0);
if (newstatus != LUA_OK && status == CLOSEPROTECT) /* first error? */
status = newstatus; /* this will be the new error */
else {
if (newstatus != LUA_OK) /* suppressed error? */
luaE_warnerror(L, "__close metamethod");
/* leave original error (or nil) on top */
L->top = restorestack(L, oldtop);
}
}
/* else no metamethod; ignore this case and keep original error */
}
return status;
callclosemethod(L, uv, errobj, yy);
}
/*
** Try to create a to-be-closed upvalue
** (can raise a memory-allocation error)
** Maximum value for deltas in 'tbclist', dependent on the type
** of delta. (This macro assumes that an 'L' is in scope where it
** is used.)
*/
static void trynewtbcupval (lua_State *L, void *ud) {
newupval(L, 1, cast(StkId, ud), &L->openupval);
}
#define MAXDELTA \
((256ul << ((sizeof(L->stack->tbclist.delta) - 1) * 8)) - 1)
/*
** Create a to-be-closed upvalue. If there is a memory error
** when creating the upvalue, the closing method must be called here,
** as there is no upvalue to call it later.
** Insert a variable in the list of to-be-closed variables.
*/
void luaF_newtbcupval (lua_State *L, StkId level) {
TValue *obj = s2v(level);
lua_assert(L->openupval == NULL || uplevel(L->openupval) < level);
if (!l_isfalse(obj)) { /* false doesn't need to be closed */
int status;
const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE);
if (ttisnil(tm)) /* no metamethod? */
varerror(L, level, "variable '%s' got a non-closable value");
status = luaD_rawrunprotected(L, trynewtbcupval, level);
if (unlikely(status != LUA_OK)) { /* memory error creating upvalue? */
lua_assert(status == LUA_ERRMEM);
luaD_seterrorobj(L, LUA_ERRMEM, level + 1); /* save error message */
/* next call must succeed, as object is closable */
prepclosingmethod(L, s2v(level), s2v(level + 1));
callclose(L, NULL); /* call closing method */
luaD_throw(L, LUA_ERRMEM); /* throw memory error */
}
lua_assert(level > L->tbclist);
if (l_isfalse(s2v(level)))
return; /* false doesn't need to be closed */
checkclosemth(L, level); /* value must have a close method */
while (cast_uint(level - L->tbclist) > MAXDELTA) {
L->tbclist += MAXDELTA; /* create a dummy node at maximum delta */
L->tbclist->tbclist.delta = 0;
}
level->tbclist.delta = cast(unsigned short, level - L->tbclist);
L->tbclist = level;
}
@@ -221,18 +189,16 @@ void luaF_unlinkupval (UpVal *uv) {
}
int luaF_close (lua_State *L, StkId level, int status) {
/*
** Close all upvalues up to the given stack level.
*/
void luaF_closeupval (lua_State *L, StkId level) {
UpVal *uv;
while ((uv = L->openupval) != NULL && uplevel(uv) >= level) {
StkId upl; /* stack index pointed by 'uv' */
while ((uv = L->openupval) != NULL && (upl = uplevel(uv)) >= level) {
TValue *slot = &uv->u.value; /* new position for value */
lua_assert(uplevel(uv) < L->top);
if (uv->tbc && status != NOCLOSINGMETH) {
/* must run closing method, which may change the stack */
ptrdiff_t levelrel = savestack(L, level);
status = callclosemth(L, uplevel(uv), status);
level = restorestack(L, levelrel);
}
luaF_unlinkupval(uv);
luaF_unlinkupval(uv); /* remove upvalue from 'openupval' list */
setobj(L, slot, uv->v); /* move value to upvalue slot */
uv->v = slot; /* now current value lives here */
if (!iswhite(uv)) { /* neither white nor dead? */
@@ -240,7 +206,35 @@ int luaF_close (lua_State *L, StkId level, int status) {
luaC_barrier(L, uv, slot);
}
}
return status;
}
/*
** Remove firt element from the tbclist plus its dummy nodes.
*/
static void poptbclist (lua_State *L) {
StkId tbc = L->tbclist;
lua_assert(tbc->tbclist.delta > 0); /* first element cannot be dummy */
tbc -= tbc->tbclist.delta;
while (tbc > L->stack && tbc->tbclist.delta == 0)
tbc -= MAXDELTA; /* remove dummy nodes */
L->tbclist = tbc;
}
/*
** Close all upvalues and to-be-closed variables up to the given stack
** level.
*/
void luaF_close (lua_State *L, StkId level, int status, int yy) {
ptrdiff_t levelrel = savestack(L, level);
luaF_closeupval(L, level); /* first, close the upvalues */
while (L->tbclist >= level) { /* traverse tbc's down to that level */
StkId tbc = L->tbclist; /* get variable index */
poptbclist(L); /* remove it from list */
prepcallclosemth(L, tbc, status, yy); /* close variable */
level = restorestack(L, levelrel);
}
}
@@ -301,7 +295,7 @@ const char *luaF_getlocalname (const Proto *f, int local_number, int pc) {
void luaF_shareproto (Proto *f) {
int i;
if (f == NULL)
if (f == NULL || isshared(f))
return;
makeshared(f);
luaS_share(f->source);

View File

@@ -42,15 +42,9 @@
#define MAXMISS 10
/*
** Special "status" for 'luaF_close'
*/
/* close upvalues without running their closing methods */
#define NOCLOSINGMETH (-1)
/* close upvalues running all closing methods in protected mode */
#define CLOSEPROTECT (-2)
/* special status to close upvalues preserving the top of the stack */
#define CLOSEKTOP (-1)
LUAI_FUNC Proto *luaF_newproto (lua_State *L);
@@ -59,7 +53,8 @@ LUAI_FUNC LClosure *luaF_newLclosure (lua_State *L, int nupvals);
LUAI_FUNC void luaF_initupvals (lua_State *L, LClosure *cl);
LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level);
LUAI_FUNC void luaF_newtbcupval (lua_State *L, StkId level);
LUAI_FUNC int luaF_close (lua_State *L, StkId level, int status);
LUAI_FUNC void luaF_closeupval (lua_State *L, StkId level);
LUAI_FUNC void luaF_close (lua_State *L, StkId level, int status, int yy);
LUAI_FUNC void luaF_unlinkupval (UpVal *uv);
LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f);
LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number,

View File

@@ -80,9 +80,9 @@
(x->marked = cast_byte((x->marked & ~WHITEBITS) | bitmask(BLACKBIT)))
#define valiswhite(x) (iscollectable(x) && iswhite(gcvalue(x)))
#define valiswhite(x) (iscollectable(x) && ispurewhite(gcvalue(x)))
#define keyiswhite(n) (keyiscollectable(n) && iswhite(gckey(n)))
#define keyiswhite(n) (keyiscollectable(n) && ispurewhite(gckey(n)))
/*
@@ -96,7 +96,7 @@
#define markkey(g, n) { if keyiswhite(n) reallymarkobject(g,gckey(n)); }
#define markobject(g,t) { if (iswhite(t)) reallymarkobject(g, obj2gco(t)); }
#define markobject(g,t) { if (ispurewhite(t)) reallymarkobject(g, obj2gco(t)); }
/*
** mark an object that can be NULL (either because it is really optional,
@@ -188,7 +188,7 @@ static int iscleared (global_State *g, const GCObject *o) {
markobject(g, o); /* strings are 'values', so are never weak */
return 0;
}
else return iswhite(o);
else return ispurewhite(o);
}
@@ -289,8 +289,6 @@ GCObject *luaC_newobj (lua_State *L, int tt, size_t sz) {
** (only closures can), and a userdata's metatable must be a table.
*/
static void reallymarkobject (global_State *g, GCObject *o) {
if (isshared(o))
return;
switch (o->tt) {
case LUA_VSHRSTR:
case LUA_VLNGSTR: {
@@ -920,7 +918,7 @@ static void GCTM (lua_State *L) {
L->ci->callstatus &= ~CIST_FIN; /* not running a finalizer anymore */
L->allowhook = oldah; /* restore hooks */
g->gcrunning = running; /* restore state */
if (unlikely(status != LUA_OK)) { /* error while running __gc? */
if (l_unlikely(status != LUA_OK)) { /* error while running __gc? */
luaE_warnerror(L, "__gc metamethod");
L->top--; /* pops error object */
}
@@ -1583,52 +1581,64 @@ static int sweepstep (lua_State *L, global_State *g,
static lu_mem singlestep (lua_State *L) {
global_State *g = G(L);
lu_mem work;
lua_assert(!g->gcstopem); /* collector is not reentrant */
g->gcstopem = 1; /* no emergency collections while collecting */
switch (g->gcstate) {
case GCSpause: {
restartcollection(g);
g->gcstate = GCSpropagate;
return 1;
work = 1;
break;
}
case GCSpropagate: {
if (g->gray == NULL) { /* no more gray objects? */
g->gcstate = GCSenteratomic; /* finish propagate phase */
return 0;
work = 0;
}
else
return propagatemark(g); /* traverse one gray object */
work = propagatemark(g); /* traverse one gray object */
break;
}
case GCSenteratomic: {
lu_mem work = atomic(L); /* work is what was traversed by 'atomic' */
work = atomic(L); /* work is what was traversed by 'atomic' */
entersweep(L);
g->GCestimate = gettotalbytes(g); /* first estimate */;
return work;
break;
}
case GCSswpallgc: { /* sweep "regular" objects */
return sweepstep(L, g, GCSswpfinobj, &g->finobj);
work = sweepstep(L, g, GCSswpfinobj, &g->finobj);
break;
}
case GCSswpfinobj: { /* sweep objects with finalizers */
return sweepstep(L, g, GCSswptobefnz, &g->tobefnz);
work = sweepstep(L, g, GCSswptobefnz, &g->tobefnz);
break;
}
case GCSswptobefnz: { /* sweep objects to be finalized */
return sweepstep(L, g, GCSswpend, NULL);
work = sweepstep(L, g, GCSswpend, NULL);
break;
}
case GCSswpend: { /* finish sweeps */
checkSizes(L, g);
g->gcstate = GCScallfin;
return 0;
work = 0;
break;
}
case GCScallfin: { /* call remaining finalizers */
if (g->tobefnz && !g->gcemergency) {
int n = runafewfinalizers(L, GCFINMAX);
return n * GCFINALIZECOST;
g->gcstopem = 0; /* ok collections during finalizers */
work = runafewfinalizers(L, GCFINMAX) * GCFINALIZECOST;
}
else { /* emergency mode or no more finalizers */
g->gcstate = GCSpause; /* finish collection */
return 0;
work = 0;
}
break;
}
default: lua_assert(0); return 0;
}
g->gcstopem = 0;
return work;
}

View File

@@ -120,6 +120,7 @@
#define isshared(x) (getage(x) == G_SHARED)
#define makeshared(x) setage(x, G_SHARED)
#define ispurewhite(x) (iswhite(x) && !isshared(x))
#define changeage(o,f,t) \
check_exp(getage(o) == (f), (o)->marked ^= ((f)^(t)))
@@ -167,15 +168,15 @@
#define luaC_barrier(L,p,v) ( \
(iscollectable(v) && isblack(p) && iswhite(gcvalue(v)) && !isshared(gcvalue(v))) ? \
(iscollectable(v) && isblack(p) && ispurewhite(gcvalue(v))) ? \
luaC_barrier_(L,obj2gco(p),gcvalue(v)) : cast_void(0))
#define luaC_barrierback(L,p,v) ( \
(iscollectable(v) && isblack(p) && iswhite(gcvalue(v)) && !isshared(gcvalue(v))) ? \
(iscollectable(v) && isblack(p) && ispurewhite(gcvalue(v))) ? \
luaC_barrierback_(L,p) : cast_void(0))
#define luaC_objbarrier(L,p,o) ( \
(isblack(p) && iswhite(o) && !isshared(o)) ? \
(isblack(p) && ispurewhite(o)) ? \
luaC_barrier_(L,obj2gco(p),obj2gco(o)) : cast_void(0))
LUAI_FUNC void luaC_fix (lua_State *L, GCObject *o);

View File

@@ -52,12 +52,6 @@ static int l_checkmode (const char *mode) {
** =======================================================
*/
#if !defined(l_checkmodep)
/* By default, Lua accepts only "r" or "w" as mode */
#define l_checkmodep(m) ((m[0] == 'r' || m[0] == 'w') && m[1] == '\0')
#endif
#if !defined(l_popen) /* { */
#if defined(LUA_USE_POSIX) /* { */
@@ -70,6 +64,12 @@ static int l_checkmode (const char *mode) {
#define l_popen(L,c,m) (_popen(c,m))
#define l_pclose(L,file) (_pclose(file))
#if !defined(l_checkmodep)
/* Windows accepts "[rw][bt]?" as valid modes */
#define l_checkmodep(m) ((m[0] == 'r' || m[0] == 'w') && \
(m[1] == '\0' || ((m[1] == 'b' || m[1] == 't') && m[2] == '\0')))
#endif
#else /* }{ */
/* ISO C definitions */
@@ -83,6 +83,12 @@ static int l_checkmode (const char *mode) {
#endif /* } */
#if !defined(l_checkmodep)
/* By default, Lua accepts only "r" or "w" as valid modes */
#define l_checkmodep(m) ((m[0] == 'r' || m[0] == 'w') && m[1] == '\0')
#endif
/* }====================================================== */
@@ -180,7 +186,7 @@ static int f_tostring (lua_State *L) {
static FILE *tofile (lua_State *L) {
LStream *p = tolstream(L);
if (isclosed(p))
if (l_unlikely(isclosed(p)))
luaL_error(L, "attempt to use a closed file");
lua_assert(p->f);
return p->f;
@@ -255,7 +261,7 @@ static LStream *newfile (lua_State *L) {
static void opencheck (lua_State *L, const char *fname, const char *mode) {
LStream *p = newfile(L);
p->f = fopen(fname, mode);
if (p->f == NULL)
if (l_unlikely(p->f == NULL))
luaL_error(L, "cannot open file '%s' (%s)", fname, strerror(errno));
}
@@ -303,7 +309,7 @@ static FILE *getiofile (lua_State *L, const char *findex) {
LStream *p;
lua_getfield(L, LUA_REGISTRYINDEX, findex);
p = (LStream *)lua_touserdata(L, -1);
if (isclosed(p))
if (l_unlikely(isclosed(p)))
luaL_error(L, "default %s file is closed", findex + IOPREF_LEN);
return p->f;
}
@@ -430,7 +436,7 @@ typedef struct {
** Add current char to buffer (if not out of space) and read next one
*/
static int nextc (RN *rn) {
if (rn->n >= L_MAXLENNUM) { /* buffer overflow? */
if (l_unlikely(rn->n >= L_MAXLENNUM)) { /* buffer overflow? */
rn->buff[0] = '\0'; /* invalidate result */
return 0; /* fail */
}
@@ -493,8 +499,8 @@ static int read_number (lua_State *L, FILE *f) {
ungetc(rn.c, rn.f); /* unread look-ahead char */
l_unlockfile(rn.f);
rn.buff[rn.n] = '\0'; /* finish string */
if (lua_stringtonumber(L, rn.buff)) /* is this a valid number? */
return 1; /* ok */
if (l_likely(lua_stringtonumber(L, rn.buff)))
return 1; /* ok, it is a valid number */
else { /* invalid format */
lua_pushnil(L); /* "result" to be removed */
return 0; /* read fails */
@@ -670,7 +676,8 @@ static int g_write (lua_State *L, FILE *f, int arg) {
status = status && (fwrite(s, sizeof(char), l, f) == l);
}
}
if (status) return 1; /* file handle already on stack top */
if (l_likely(status))
return 1; /* file handle already on stack top */
else return luaL_fileresult(L, status, NULL);
}
@@ -697,7 +704,7 @@ static int f_seek (lua_State *L) {
luaL_argcheck(L, (lua_Integer)offset == p3, 3,
"not an integer in proper range");
op = l_fseek(f, offset, mode[op]);
if (op)
if (l_unlikely(op))
return luaL_fileresult(L, 0, NULL); /* error */
else {
lua_pushinteger(L, (lua_Integer)l_ftell(f));

View File

@@ -122,26 +122,29 @@ l_noret luaX_syntaxerror (LexState *ls, const char *msg) {
/*
** creates a new string and anchors it in scanner's table so that
** it will not be collected until the end of the compilation
** (by that time it should be anchored somewhere)
** Creates a new string and anchors it in scanner's table so that it
** will not be collected until the end of the compilation; by that time
** it should be anchored somewhere. It also internalizes long strings,
** ensuring there is only one copy of each unique string. The table
** here is used as a set: the string enters as the key, while its value
** is irrelevant. We use the string itself as the value only because it
** is a TValue readly available. Later, the code generation can change
** this value.
*/
TString *luaX_newstring (LexState *ls, const char *str, size_t l) {
lua_State *L = ls->L;
TValue *o; /* entry for 'str' */
TString *ts = luaS_newlstr(L, str, l); /* create new string */
setsvalue2s(L, L->top++, ts); /* temporarily anchor it in stack */
o = luaH_set(L, ls->h, s2v(L->top - 1));
if (isempty(o)) { /* not in use yet? */
/* boolean value does not need GC barrier;
table is not a metatable, so it does not need to invalidate cache */
setbtvalue(o); /* t[string] = true */
const TValue *o = luaH_getstr(ls->h, ts);
if (!ttisnil(o)) /* string already present? */
ts = keystrval(nodefromval(o)); /* get saved copy */
else { /* not in use yet */
TValue *stv = s2v(L->top++); /* reserve stack space for string */
setsvalue(L, stv, ts); /* temporarily anchor the string */
luaH_finishset(L, ls->h, stv, o, stv); /* t[string] = string */
/* table is not a metatable, so it does not need to invalidate cache */
luaC_checkGC(L);
L->top--; /* remove string from stack */
}
else { /* string already present */
ts = keystrval(nodefromval(o)); /* re-use value previously stored */
}
L->top--; /* remove string from stack */
return ts;
}

View File

@@ -149,22 +149,6 @@ typedef LUAI_UACINT l_uacInt;
#endif
/*
** macros to improve jump prediction (used mainly for error handling)
*/
#if !defined(likely)
#if defined(__GNUC__)
#define likely(x) (__builtin_expect(((x) != 0), 1))
#define unlikely(x) (__builtin_expect(((x) != 0), 0))
#else
#define likely(x) (x)
#define unlikely(x) (x)
#endif
#endif
/*
** non-return type
*/

View File

@@ -73,7 +73,7 @@ static int math_atan (lua_State *L) {
static int math_toint (lua_State *L) {
int valid;
lua_Integer n = lua_tointegerx(L, 1, &valid);
if (valid)
if (l_likely(valid))
lua_pushinteger(L, n);
else {
luaL_checkany(L, 1);
@@ -175,7 +175,8 @@ static int math_log (lua_State *L) {
lua_Number base = luaL_checknumber(L, 2);
#if !defined(LUA_USE_C89)
if (base == l_mathop(2.0))
res = l_mathop(log2)(x); else
res = l_mathop(log2)(x);
else
#endif
if (base == l_mathop(10.0))
res = l_mathop(log10)(x);

View File

@@ -24,12 +24,12 @@
#if defined(EMERGENCYGCTESTS)
/*
** First allocation will fail whenever not building initial state
** and not shrinking a block. (This fail will trigger 'tryagain' and
** a full GC cycle at every allocation.)
** First allocation will fail whenever not building initial state.
** (This fail will trigger 'tryagain' and a full GC cycle at every
** allocation.)
*/
static void *firsttry (global_State *g, void *block, size_t os, size_t ns) {
if (ttisnil(&g->nilvalue) && ns > os)
if (completestate(g) && ns > 0) /* frees never fail */
return NULL; /* fail */
else /* normal allocation */
return (*g->frealloc)(g->ud, block, os, ns);
@@ -83,7 +83,7 @@ void *luaM_growaux_ (lua_State *L, void *block, int nelems, int *psize,
if (nelems + 1 <= size) /* does one extra element still fit? */
return block; /* nothing to be done */
if (size >= limit / 2) { /* cannot double it? */
if (unlikely(size >= limit)) /* cannot grow even a little? */
if (l_unlikely(size >= limit)) /* cannot grow even a little? */
luaG_runerror(L, "too many %s (limit is %d)", what, limit);
size = limit; /* still have at least one free place */
}
@@ -138,15 +138,17 @@ void luaM_free_ (lua_State *L, void *block, size_t osize) {
/*
** In case of allocation fail, this function will call the GC to try
** to free some memory and then try the allocation again.
** (It should not be called when shrinking a block, because then the
** interpreter may be in the middle of a collection step.)
** In case of allocation fail, this function will do an emergency
** collection to free some memory and then try the allocation again.
** The GC should not be called while state is not fully built, as the
** collector is not yet fully initialized. Also, it should not be called
** when 'gcstopem' is true, because then the interpreter is in the
** middle of a collection step.
*/
static void *tryagain (lua_State *L, void *block,
size_t osize, size_t nsize) {
global_State *g = G(L);
if (ttisnil(&g->nilvalue)) { /* is state fully build? */
if (completestate(g) && !g->gcstopem) {
luaC_fullgc(L, 1); /* try to free some memory... */
return (*g->frealloc)(g->ud, block, osize, nsize); /* try again */
}
@@ -156,17 +158,14 @@ static void *tryagain (lua_State *L, void *block,
/*
** Generic allocation routine.
** If allocation fails while shrinking a block, do not try again; the
** GC shrinks some blocks and it is not reentrant.
*/
void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) {
void *newblock;
global_State *g = G(L);
lua_assert((osize == 0) == (block == NULL));
newblock = firsttry(g, block, osize, nsize);
if (unlikely(newblock == NULL && nsize > 0)) {
if (nsize > osize) /* not shrinking a block? */
newblock = tryagain(L, block, osize, nsize);
if (l_unlikely(newblock == NULL && nsize > 0)) {
newblock = tryagain(L, block, osize, nsize);
if (newblock == NULL) /* still no memory? */
return NULL; /* do not update 'GCdebt' */
}
@@ -179,7 +178,7 @@ void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) {
void *luaM_saferealloc_ (lua_State *L, void *block, size_t osize,
size_t nsize) {
void *newblock = luaM_realloc_(L, block, osize, nsize);
if (unlikely(newblock == NULL && nsize > 0)) /* allocation failed? */
if (l_unlikely(newblock == NULL && nsize > 0)) /* allocation failed? */
luaM_error(L);
return newblock;
}
@@ -191,7 +190,7 @@ void *luaM_malloc_ (lua_State *L, size_t size, int tag) {
else {
global_State *g = G(L);
void *newblock = firsttry(g, NULL, tag, size);
if (unlikely(newblock == NULL)) {
if (l_unlikely(newblock == NULL)) {
newblock = tryagain(L, NULL, tag, size);
if (newblock == NULL)
luaM_error(L);

View File

@@ -132,14 +132,16 @@ static void lsys_unloadlib (void *lib) {
static void *lsys_load (lua_State *L, const char *path, int seeglb) {
void *lib = dlopen(path, RTLD_NOW | (seeglb ? RTLD_GLOBAL : RTLD_LOCAL));
if (lib == NULL) lua_pushstring(L, dlerror());
if (l_unlikely(lib == NULL))
lua_pushstring(L, dlerror());
return lib;
}
static lua_CFunction lsys_sym (lua_State *L, void *lib, const char *sym) {
lua_CFunction f = cast_func(dlsym(lib, sym));
if (f == NULL) lua_pushstring(L, dlerror());
if (l_unlikely(f == NULL))
lua_pushstring(L, dlerror());
return f;
}
@@ -410,7 +412,7 @@ static int ll_loadlib (lua_State *L) {
const char *path = luaL_checkstring(L, 1);
const char *init = luaL_checkstring(L, 2);
int stat = lookforfunc(L, path, init);
if (stat == 0) /* no errors? */
if (l_likely(stat == 0)) /* no errors? */
return 1; /* return the loaded function */
else { /* error; error message is on stack top */
luaL_pushfail(L);
@@ -523,14 +525,14 @@ static const char *findfile (lua_State *L, const char *name,
const char *path;
lua_getfield(L, lua_upvalueindex(1), pname);
path = lua_tostring(L, -1);
if (path == NULL)
if (l_unlikely(path == NULL))
luaL_error(L, "'package.%s' must be a string", pname);
return searchpath(L, name, path, ".", dirsep);
}
static int checkload (lua_State *L, int stat, const char *filename) {
if (stat) { /* module loaded successfully? */
if (l_likely(stat)) { /* module loaded successfully? */
lua_pushstring(L, filename); /* will be 2nd argument to module */
return 2; /* return open function and file name */
}
@@ -623,13 +625,14 @@ static void findloader (lua_State *L, const char *name) {
int i;
luaL_Buffer msg; /* to build error message */
/* push 'package.searchers' to index 3 in the stack */
if (lua_getfield(L, lua_upvalueindex(1), "searchers") != LUA_TTABLE)
if (l_unlikely(lua_getfield(L, lua_upvalueindex(1), "searchers")
!= LUA_TTABLE))
luaL_error(L, "'package.searchers' must be a table");
luaL_buffinit(L, &msg);
/* iterate over available searchers to find a loader */
for (i = 1; ; i++) {
luaL_addstring(&msg, "\n\t"); /* error-message prefix */
if (lua_rawgeti(L, 3, i) == LUA_TNIL) { /* no more searchers? */
if (l_unlikely(lua_rawgeti(L, 3, i) == LUA_TNIL)) { /* no more searchers? */
lua_pop(L, 1); /* remove nil */
luaL_buffsub(&msg, 2); /* remove prefix */
luaL_pushresult(&msg); /* create error message */

View File

@@ -136,10 +136,19 @@ typedef struct TValue {
/*
** Entries in the Lua stack
** Entries in a Lua stack. Field 'tbclist' forms a list of all
** to-be-closed variables active in this stack. Dummy entries are
** used when the distance between two tbc variables does not fit
** in an unsigned short. They are represented by delta==0, and
** their real delta is always the maximum value that fits in
** that field.
*/
typedef union StackValue {
TValue val;
struct {
TValuefields;
unsigned short delta;
} tbclist;
} StackValue;
@@ -571,10 +580,11 @@ typedef struct Proto {
#define LUA_VCCL makevariant(LUA_TFUNCTION, 2) /* C closure */
#define ttisfunction(o) checktype(o, LUA_TFUNCTION)
#define ttisclosure(o) ((rawtt(o) & 0x1F) == LUA_VLCL)
#define ttisLclosure(o) checktag((o), ctb(LUA_VLCL))
#define ttislcf(o) checktag((o), LUA_VLCF)
#define ttisCclosure(o) checktag((o), ctb(LUA_VCCL))
#define ttisclosure(o) (ttisLclosure(o) || ttisCclosure(o))
#define isLfunction(o) ttisLclosure(o)

View File

@@ -225,13 +225,13 @@ OP_SELF,/* A B C R[A+1] := R[B]; R[A] := R[B][RK(C):string] */
OP_ADDI,/* A B sC R[A] := R[B] + sC */
OP_ADDK,/* A B C R[A] := R[B] + K[C] */
OP_SUBK,/* A B C R[A] := R[B] - K[C] */
OP_MULK,/* A B C R[A] := R[B] * K[C] */
OP_MODK,/* A B C R[A] := R[B] % K[C] */
OP_POWK,/* A B C R[A] := R[B] ^ K[C] */
OP_DIVK,/* A B C R[A] := R[B] / K[C] */
OP_IDIVK,/* A B C R[A] := R[B] // K[C] */
OP_ADDK,/* A B C R[A] := R[B] + K[C]:number */
OP_SUBK,/* A B C R[A] := R[B] - K[C]:number */
OP_MULK,/* A B C R[A] := R[B] * K[C]:number */
OP_MODK,/* A B C R[A] := R[B] % K[C]:number */
OP_POWK,/* A B C R[A] := R[B] ^ K[C]:number */
OP_DIVK,/* A B C R[A] := R[B] / K[C]:number */
OP_IDIVK,/* A B C R[A] := R[B] // K[C]:number */
OP_BANDK,/* A B C R[A] := R[B] & K[C]:integer */
OP_BORK,/* A B C R[A] := R[B] | K[C]:integer */

View File

@@ -170,7 +170,7 @@ static int os_tmpname (lua_State *L) {
char buff[LUA_TMPNAMBUFSIZE];
int err;
lua_tmpnam(buff, err);
if (err)
if (l_unlikely(err))
return luaL_error(L, "unable to generate a unique filename");
lua_pushstring(L, buff);
return 1;
@@ -208,7 +208,7 @@ static int os_clock (lua_State *L) {
*/
static void setfield (lua_State *L, const char *key, int value, int delta) {
#if (defined(LUA_NUMTIME) && LUA_MAXINTEGER <= INT_MAX)
if (value > LUA_MAXINTEGER - delta)
if (l_unlikely(value > LUA_MAXINTEGER - delta))
luaL_error(L, "field '%s' is out-of-bound", key);
#endif
lua_pushinteger(L, (lua_Integer)value + delta);
@@ -253,9 +253,9 @@ static int getfield (lua_State *L, const char *key, int d, int delta) {
int t = lua_getfield(L, -1, key); /* get field and its type */
lua_Integer res = lua_tointegerx(L, -1, &isnum);
if (!isnum) { /* field is not an integer? */
if (t != LUA_TNIL) /* some other value? */
if (l_unlikely(t != LUA_TNIL)) /* some other value? */
return luaL_error(L, "field '%s' is not an integer", key);
else if (d < 0) /* absent field; no default? */
else if (l_unlikely(d < 0)) /* absent field; no default? */
return luaL_error(L, "field '%s' missing in date table", key);
res = d;
}

View File

@@ -128,7 +128,7 @@ static void checknext (LexState *ls, int c) {
** in line 'where' (if that is not the current line).
*/
static void check_match (LexState *ls, int what, int who, int where) {
if (unlikely(!testnext(ls, what))) {
if (l_unlikely(!testnext(ls, what))) {
if (where == ls->linenumber) /* all in the same line? */
error_expected(ls, what); /* do not need a complex message */
else {
@@ -222,26 +222,26 @@ static Vardesc *getlocalvardesc (FuncState *fs, int vidx) {
/*
** Convert 'nvar', a compiler index level, to it corresponding
** stack index level. For that, search for the highest variable
** below that level that is in the stack and uses its stack
** index ('sidx').
** Convert 'nvar', a compiler index level, to its corresponding
** register. For that, search for the highest variable below that level
** that is in a register and uses its register index ('ridx') plus one.
*/
static int stacklevel (FuncState *fs, int nvar) {
static int reglevel (FuncState *fs, int nvar) {
while (nvar-- > 0) {
Vardesc *vd = getlocalvardesc(fs, nvar); /* get variable */
if (vd->vd.kind != RDKCTC) /* is in the stack? */
return vd->vd.sidx + 1;
Vardesc *vd = getlocalvardesc(fs, nvar); /* get previous variable */
if (vd->vd.kind != RDKCTC) /* is in a register? */
return vd->vd.ridx + 1;
}
return 0; /* no variables in the stack */
return 0; /* no variables in registers */
}
/*
** Return the number of variables in the stack for function 'fs'
** Return the number of variables in the register stack for the given
** function.
*/
int luaY_nvarstack (FuncState *fs) {
return stacklevel(fs, fs->nactvar);
return reglevel(fs, fs->nactvar);
}
@@ -267,7 +267,7 @@ static void init_var (FuncState *fs, expdesc *e, int vidx) {
e->f = e->t = NO_JUMP;
e->k = VLOCAL;
e->u.var.vidx = vidx;
e->u.var.sidx = getlocalvardesc(fs, vidx)->vd.sidx;
e->u.var.ridx = getlocalvardesc(fs, vidx)->vd.ridx;
}
@@ -310,12 +310,12 @@ static void check_readonly (LexState *ls, expdesc *e) {
*/
static void adjustlocalvars (LexState *ls, int nvars) {
FuncState *fs = ls->fs;
int stklevel = luaY_nvarstack(fs);
int reglevel = luaY_nvarstack(fs);
int i;
for (i = 0; i < nvars; i++) {
int vidx = fs->nactvar++;
Vardesc *var = getlocalvardesc(fs, vidx);
var->vd.sidx = stklevel++;
var->vd.ridx = reglevel++;
var->vd.pidx = registerlocalvar(ls, fs, var->vd.name);
}
}
@@ -366,7 +366,7 @@ static int newupvalue (FuncState *fs, TString *name, expdesc *v) {
FuncState *prev = fs->prev;
if (v->k == VLOCAL) {
up->instack = 1;
up->idx = v->u.var.sidx;
up->idx = v->u.var.ridx;
up->kind = getlocalvardesc(prev, v->u.var.vidx)->vd.kind;
lua_assert(eqstr(name, getlocalvardesc(prev, v->u.var.vidx)->vd.name));
}
@@ -517,7 +517,7 @@ static void solvegoto (LexState *ls, int g, Labeldesc *label) {
Labellist *gl = &ls->dyd->gt; /* list of goto's */
Labeldesc *gt = &gl->arr[g]; /* goto to be resolved */
lua_assert(eqstr(gt->name, label->name));
if (unlikely(gt->nactvar < label->nactvar)) /* enter some scope? */
if (l_unlikely(gt->nactvar < label->nactvar)) /* enter some scope? */
jumpscopeerror(ls, gt);
luaK_patchlist(ls->fs, gt->pc, label->pc);
for (i = g; i < gl->n - 1; i++) /* remove goto from pending list */
@@ -620,7 +620,7 @@ static void movegotosout (FuncState *fs, BlockCnt *bl) {
for (i = bl->firstgoto; i < gl->n; i++) { /* for each pending goto */
Labeldesc *gt = &gl->arr[i];
/* leaving a variable scope? */
if (stacklevel(fs, gt->nactvar) > stacklevel(fs, bl->nactvar))
if (reglevel(fs, gt->nactvar) > reglevel(fs, bl->nactvar))
gt->close |= bl->upval; /* jump may need a close */
gt->nactvar = bl->nactvar; /* update goto level */
}
@@ -661,7 +661,7 @@ static void leaveblock (FuncState *fs) {
BlockCnt *bl = fs->bl;
LexState *ls = fs->ls;
int hasclose = 0;
int stklevel = stacklevel(fs, bl->nactvar); /* level outside the block */
int stklevel = reglevel(fs, bl->nactvar); /* level outside the block */
if (bl->isloop) /* fix pending breaks? */
hasclose = createlabel(ls, luaS_newliteral(ls->L, "break"), 0, 0);
if (!hasclose && bl->previous && bl->upval)
@@ -1330,13 +1330,13 @@ static void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) {
}
}
else { /* table is a register */
if (v->k == VLOCAL && lh->v.u.ind.t == v->u.var.sidx) {
if (v->k == VLOCAL && lh->v.u.ind.t == v->u.var.ridx) {
conflict = 1; /* table is the local being assigned now */
lh->v.u.ind.t = extra; /* assignment will use safe copy */
}
/* is index the local being assigned? */
if (lh->v.k == VINDEXED && v->k == VLOCAL &&
lh->v.u.ind.idx == v->u.var.sidx) {
lh->v.u.ind.idx == v->u.var.ridx) {
conflict = 1;
lh->v.u.ind.idx = extra; /* previous assignment will use safe copy */
}
@@ -1346,7 +1346,7 @@ static void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) {
if (conflict) {
/* copy upvalue/local value to a temporary (in position 'extra') */
if (v->k == VLOCAL)
luaK_codeABC(fs, OP_MOVE, extra, v->u.var.sidx, 0);
luaK_codeABC(fs, OP_MOVE, extra, v->u.var.ridx, 0);
else
luaK_codeABC(fs, OP_GETUPVAL, extra, v->u.info, 0);
luaK_reserveregs(fs, 1);
@@ -1411,7 +1411,7 @@ static void gotostat (LexState *ls) {
newgotoentry(ls, name, line, luaK_jump(fs));
else { /* found a label */
/* backward jump; will be resolved here */
int lblevel = stacklevel(fs, lb->nactvar); /* label level */
int lblevel = reglevel(fs, lb->nactvar); /* label level */
if (luaY_nvarstack(fs) > lblevel) /* leaving the scope of a variable? */
luaK_codeABC(fs, OP_CLOSE, lblevel, 0, 0);
/* create jump and link it to the label */
@@ -1435,7 +1435,7 @@ static void breakstat (LexState *ls) {
*/
static void checkrepeated (LexState *ls, TString *name) {
Labeldesc *lb = findlabel(ls, name);
if (unlikely(lb != NULL)) { /* already defined? */
if (l_unlikely(lb != NULL)) { /* already defined? */
const char *msg = "label '%s' already defined on line %d";
msg = luaO_pushfstring(ls->L, msg, getstr(name), lb->line);
luaK_semerror(ls, msg); /* error */
@@ -1488,7 +1488,7 @@ static void repeatstat (LexState *ls, int line) {
if (bl2.upval) { /* upvalues? */
int exit = luaK_jump(fs); /* normal exit must jump over fix */
luaK_patchtohere(fs, condexit); /* repetition must close upvalues */
luaK_codeABC(fs, OP_CLOSE, stacklevel(fs, bl2.nactvar), 0, 0);
luaK_codeABC(fs, OP_CLOSE, reglevel(fs, bl2.nactvar), 0, 0);
condexit = luaK_jump(fs); /* repeat after closing upvalues */
luaK_patchtohere(fs, exit); /* normal exit comes to here */
}
@@ -1520,7 +1520,7 @@ static void fixforjump (FuncState *fs, int pc, int dest, int back) {
int offset = dest - (pc + 1);
if (back)
offset = -offset;
if (unlikely(offset > MAXARG_Bx))
if (l_unlikely(offset > MAXARG_Bx))
luaX_syntaxerror(fs->ls, "control structure too long");
SETARG_Bx(*jmp, offset);
}
@@ -1708,7 +1708,7 @@ static void checktoclose (LexState *ls, int level) {
FuncState *fs = ls->fs;
markupval(fs, level + 1);
fs->bl->insidetbc = 1; /* in the scope of a to-be-closed variable */
luaK_codeABC(fs, OP_TBC, stacklevel(fs, level), 0, 0);
luaK_codeABC(fs, OP_TBC, reglevel(fs, level), 0, 0);
}
}

View File

@@ -35,7 +35,7 @@ typedef enum {
(string is fixed by the lexer) */
VNONRELOC, /* expression has its value in a fixed register;
info = result register */
VLOCAL, /* local variable; var.sidx = stack index (local register);
VLOCAL, /* local variable; var.ridx = register index;
var.vidx = relative index in 'actvar.arr' */
VUPVAL, /* upvalue variable; info = index of upvalue in 'upvalues' */
VCONST, /* compile-time <const> variable;
@@ -77,7 +77,7 @@ typedef struct expdesc {
lu_byte t; /* table (register or upvalue) */
} ind;
struct { /* for local variables */
lu_byte sidx; /* index in the stack */
lu_byte ridx; /* register holding the variable */
unsigned short vidx; /* compiler index (in 'actvar.arr') */
} var;
} u;
@@ -97,7 +97,7 @@ typedef union Vardesc {
struct {
TValuefields; /* constant value (if it is a compile-time constant) */
lu_byte kind;
lu_byte sidx; /* index of the variable in the stack */
lu_byte ridx; /* register holding the variable */
short pidx; /* index of the variable in the Proto's 'locvars' array */
TString *name; /* variable name */
} vd;

View File

@@ -172,7 +172,7 @@ void luaE_checkcstack (lua_State *L) {
LUAI_FUNC void luaE_incCstack (lua_State *L) {
L->nCcalls++;
if (unlikely(getCcalls(L) >= LUAI_MAXCCALLS))
if (l_unlikely(getCcalls(L) >= LUAI_MAXCCALLS))
luaE_checkcstack(L);
}
@@ -181,6 +181,7 @@ static void stack_init (lua_State *L1, lua_State *L) {
int i; CallInfo *ci;
/* initialize stack array */
L1->stack = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, StackValue);
L1->tbclist = L1->stack;
for (i = 0; i < BASIC_STACK_SIZE + EXTRA_STACK; i++)
setnilvalue(s2v(L1->stack + i)); /* erase new stack */
L1->top = L1->stack;
@@ -213,24 +214,19 @@ static void freestack (lua_State *L) {
** Create registry table and its predefined values
*/
static void init_registry (lua_State *L, global_State *g) {
TValue temp;
/* create registry */
Table *registry = luaH_new(L);
sethvalue(L, &g->l_registry, registry);
luaH_resize(L, registry, LUA_RIDX_LAST, 0);
/* registry[LUA_RIDX_MAINTHREAD] = L */
setthvalue(L, &temp, L); /* temp = L */
luaH_setint(L, registry, LUA_RIDX_MAINTHREAD, &temp);
/* registry[LUA_RIDX_GLOBALS] = table of globals */
sethvalue(L, &temp, luaH_new(L)); /* temp = new table (global table) */
luaH_setint(L, registry, LUA_RIDX_GLOBALS, &temp);
setthvalue(L, &registry->array[LUA_RIDX_MAINTHREAD - 1], L);
/* registry[LUA_RIDX_GLOBALS] = new table (table of globals) */
sethvalue(L, &registry->array[LUA_RIDX_GLOBALS - 1], luaH_new(L));
}
/*
** open parts of the state that may cause memory-allocation errors.
** ('g->nilvalue' being a nil value flags that the state was completely
** build.)
*/
static void f_luaopen (lua_State *L, void *ud) {
global_State *g = G(L);
@@ -241,7 +237,7 @@ static void f_luaopen (lua_State *L, void *ud) {
luaT_init(L);
luaX_init(L);
g->gcrunning = 1; /* allow gc */
setnilvalue(&g->nilvalue);
setnilvalue(&g->nilvalue); /* now state is complete */
luai_userstateopen(L);
}
@@ -256,6 +252,7 @@ static void preinit_thread (lua_State *L, global_State *g) {
L->ci = NULL;
L->nci = 0;
L->twups = L; /* thread has no upvalues */
L->nCcalls = 0;
L->errorJmp = NULL;
L->hook = NULL;
L->hookmask = 0;
@@ -271,10 +268,13 @@ static void preinit_thread (lua_State *L, global_State *g) {
static void close_state (lua_State *L) {
global_State *g = G(L);
luaF_close(L, L->stack, CLOSEPROTECT); /* close all upvalues */
luaC_freeallobjects(L); /* collect all objects */
if (ttisnil(&g->nilvalue)) /* closing a fully built state? */
if (!completestate(g)) /* closing a partially built state? */
luaC_freeallobjects(L); /* jucst collect its objects */
else { /* closing a fully built state */
luaD_closeprotected(L, 1, LUA_OK); /* close all upvalues */
luaC_freeallobjects(L); /* collect all objects */
luai_userstateclose(L);
}
luaM_freearray(L, G(L)->strt.hash, G(L)->strt.size);
freestack(L);
lua_assert(gettotalbytes(g) == sizeof(LG));
@@ -299,7 +299,6 @@ LUA_API lua_State *lua_newthread (lua_State *L) {
setthvalue2s(L, L->top, L1);
api_incr_top(L);
preinit_thread(L1, g);
L1->nCcalls = 0;
L1->hookmask = L->hookmask;
L1->basehookcount = L->basehookcount;
L1->hook = L->hook;
@@ -316,7 +315,7 @@ LUA_API lua_State *lua_newthread (lua_State *L) {
void luaE_freethread (lua_State *L, lua_State *L1) {
LX *l = fromstate(L1);
luaF_close(L1, L1->stack, NOCLOSINGMETH); /* close all upvalues */
luaF_closeupval(L1, L1->stack); /* close all upvalues */
lua_assert(L1->openupval == NULL);
luai_userstatefree(L, L1);
freestack(L1);
@@ -324,23 +323,29 @@ void luaE_freethread (lua_State *L, lua_State *L1) {
}
int lua_resetthread (lua_State *L) {
CallInfo *ci;
int status;
lua_lock(L);
L->ci = ci = &L->base_ci; /* unwind CallInfo list */
int luaE_resetthread (lua_State *L, int status) {
CallInfo *ci = L->ci = &L->base_ci; /* unwind CallInfo list */
setnilvalue(s2v(L->stack)); /* 'function' entry for basic 'ci' */
ci->func = L->stack;
ci->callstatus = CIST_C;
status = luaF_close(L, L->stack, CLOSEPROTECT);
if (status != CLOSEPROTECT) /* real errors? */
luaD_seterrorobj(L, status, L->stack + 1);
else {
if (status == LUA_YIELD)
status = LUA_OK;
status = luaD_closeprotected(L, 1, status);
if (status != LUA_OK) /* errors? */
luaD_seterrorobj(L, status, L->stack + 1);
else
L->top = L->stack + 1;
}
ci->top = L->top + LUA_MINSTACK;
L->status = status;
L->status = cast_byte(status);
luaD_reallocstack(L, cast_int(ci->top - L->stack), 0);
return status;
}
LUA_API int lua_resetthread (lua_State *L) {
int status;
lua_lock(L);
status = luaE_resetthread(L, L->status);
lua_unlock(L);
return status;
}
@@ -360,7 +365,6 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
preinit_thread(L, g);
g->allgc = obj2gco(L); /* by now, only object is the main thread */
L->next = NULL;
L->nCcalls = 0;
incnny(L); /* main thread is always non yieldable */
g->frealloc = f;
g->ud = ud;
@@ -374,6 +378,7 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
g->panic = NULL;
g->gcstate = GCSpause;
g->gckind = KGC_INC;
g->gcstopem = 0;
g->gcemergency = 0;
g->finobj = g->tobefnz = g->fixedgc = NULL;
g->firstold1 = g->survival = g->old1 = g->reallyold = NULL;

View File

@@ -156,6 +156,18 @@ typedef struct stringtable {
/*
** Information about a call.
** About union 'u':
** - field 'l' is used only for Lua functions;
** - field 'c' is used only for C functions.
** About union 'u2':
** - field 'funcidx' is used only by C functions while doing a
** protected call;
** - field 'nyield' is used only while a function is "doing" an
** yield (from the yield until the next resume);
** - field 'nres' is used only while closing tbc variables when
** returning from a C function;
** - field 'transferinfo' is used only during call/returnhooks,
** before the function starts or after it ends.
*/
typedef struct CallInfo {
StkId func; /* function index in the stack */
@@ -176,6 +188,7 @@ typedef struct CallInfo {
union {
int funcidx; /* called-function index */
int nyield; /* number of values yielded */
int nres; /* number of values returned */
struct { /* info about transferred values (for call/return hooks) */
unsigned short ftransfer; /* offset of first value transferred */
unsigned short ntransfer; /* number of values transferred */
@@ -191,17 +204,34 @@ typedef struct CallInfo {
*/
#define CIST_OAH (1<<0) /* original value of 'allowhook' */
#define CIST_C (1<<1) /* call is running a C function */
#define CIST_FRESH (1<<2) /* call is on a fresh "luaV_execute" frame */
#define CIST_FRESH (1<<2) /* call is on a fresh "luaV_execute" frame */
#define CIST_HOOKED (1<<3) /* call is running a debug hook */
#define CIST_YPCALL (1<<4) /* call is a yieldable protected call */
#define CIST_YPCALL (1<<4) /* doing a yieldable protected call */
#define CIST_TAIL (1<<5) /* call was tail called */
#define CIST_HOOKYIELD (1<<6) /* last hook called yielded */
#define CIST_FIN (1<<7) /* call is running a finalizer */
#define CIST_FIN (1<<7) /* call is running a finalizer */
#define CIST_TRAN (1<<8) /* 'ci' has transfer information */
#define CIST_CLSRET (1<<9) /* function is closing tbc variables */
/* Bits 10-12 are used for CIST_RECST (see below) */
#define CIST_RECST 10
#if defined(LUA_COMPAT_LT_LE)
#define CIST_LEQ (1<<9) /* using __lt for __le */
#define CIST_LEQ (1<<13) /* using __lt for __le */
#endif
/*
** Field CIST_RECST stores the "recover status", used to keep the error
** status while closing to-be-closed variables in coroutines, so that
** Lua can correctly resume after an yield from a __close method called
** because of an error. (Three bits are enough for error status.)
*/
#define getcistrecst(ci) (((ci)->callstatus >> CIST_RECST) & 7)
#define setcistrecst(ci,st) \
check_exp(((st) & 7) == (st), /* status must fit in three bits */ \
((ci)->callstatus = ((ci)->callstatus & ~(7 << CIST_RECST)) \
| ((st) << CIST_RECST)))
/* active function is a Lua function */
#define isLua(ci) (!((ci)->callstatus & CIST_C))
@@ -229,6 +259,7 @@ typedef struct global_State {
lu_byte currentwhite;
lu_byte gcstate; /* state of garbage collector */
lu_byte gckind; /* kind of GC running */
lu_byte gcstopem; /* stops emergency collections */
lu_byte genminormul; /* control for minor generational collections */
lu_byte genmajormul; /* control for major generational collections */
lu_byte gcrunning; /* true if GC is running */
@@ -280,6 +311,7 @@ struct lua_State {
StkId stack_last; /* end of stack (last element + 1) */
StkId stack; /* stack base */
UpVal *openupval; /* list of open upvalues in this stack */
StkId tbclist; /* list of to-be-closed variables */
GCObject *gclist;
struct lua_State *twups; /* list of threads with open upvalues */
struct lua_longjmp *errorJmp; /* current error recover point */
@@ -296,6 +328,12 @@ struct lua_State {
#define G(L) (L->l_G)
/*
** 'g->nilvalue' being a nil value flags that the state was completely
** build.
*/
#define completestate(g) ttisnil(&g->nilvalue)
/*
** Union of all collectable objects (only for conversions)
@@ -358,6 +396,7 @@ LUAI_FUNC void luaE_checkcstack (lua_State *L);
LUAI_FUNC void luaE_incCstack (lua_State *L);
LUAI_FUNC void luaE_warning (lua_State *L, const char *msg, int tocont);
LUAI_FUNC void luaE_warnerror (lua_State *L, const char *where);
LUAI_FUNC int luaE_resetthread (lua_State *L, int status);
#endif

View File

@@ -23,7 +23,7 @@
#include "atomic.h"
static unsigned int STRSEED;
static size_t STRID = 0;
static ATOM_SIZET STRID = 0;
/*
** Maximum size for string table.
@@ -57,10 +57,10 @@ int luaS_eqshrstr (TString *a, TString *b) {
}
void luaS_share (TString *ts) {
if (ts == NULL)
if (ts == NULL || isshared(ts))
return;
makeshared(ts);
ts->id = ATOM_DEC(&STRID);
ts->id = ATOM_FDEC(&STRID)-1;
}
unsigned int luaS_hash (const char *str, size_t l, unsigned int seed) {
@@ -112,7 +112,7 @@ void luaS_resize (lua_State *L, int nsize) {
if (nsize < osize) /* shrinking table? */
tablerehash(tb->hash, osize, nsize); /* depopulate shrinking part */
newvect = luaM_reallocvector(L, tb->hash, osize, nsize, TString*);
if (unlikely(newvect == NULL)) { /* reallocation failed? */
if (l_unlikely(newvect == NULL)) { /* reallocation failed? */
if (nsize < osize) /* was it shrinking table? */
tablerehash(tb->hash, nsize, osize); /* restore to original size */
/* leave table as it was */
@@ -214,7 +214,7 @@ void luaS_remove (lua_State *L, TString *ts) {
static void growstrtab (lua_State *L, stringtable *tb) {
if (unlikely(tb->nuse == MAX_INT)) { /* too many strings? */
if (l_unlikely(tb->nuse == MAX_INT)) { /* too many strings? */
luaC_fullgc(L, 1); /* try to free some... */
if (tb->nuse == MAX_INT) /* still too many? */
luaM_error(L); /* cannot even create a message... */
@@ -265,7 +265,7 @@ TString *luaS_newlstr (lua_State *L, const char *str, size_t l) {
return internshrstr(L, str, l);
else {
TString *ts;
if (unlikely(l >= (MAX_SIZE - sizeof(TString))/sizeof(char)))
if (l_unlikely(l >= (MAX_SIZE - sizeof(TString))/sizeof(char)))
luaM_toobig(L);
ts = luaS_createlngstrobj(L, l);
memcpy(getstr(ts), str, l * sizeof(char));
@@ -301,7 +301,7 @@ Udata *luaS_newudata (lua_State *L, size_t s, int nuvalue) {
Udata *u;
int i;
GCObject *o;
if (unlikely(s > MAX_SIZE - udatamemoffset(nuvalue)))
if (l_unlikely(s > MAX_SIZE - udatamemoffset(nuvalue)))
luaM_toobig(L);
o = luaC_newobj(L, LUA_VUSERDATA, sizeudata(nuvalue, s));
u = gco2u(o);

View File

@@ -152,8 +152,9 @@ static int str_rep (lua_State *L) {
const char *s = luaL_checklstring(L, 1, &l);
lua_Integer n = luaL_checkinteger(L, 2);
const char *sep = luaL_optlstring(L, 3, "", &lsep);
if (n <= 0) lua_pushliteral(L, "");
else if (l + lsep < l || l + lsep > MAXSIZE / n) /* may overflow? */
if (n <= 0)
lua_pushliteral(L, "");
else if (l_unlikely(l + lsep < l || l + lsep > MAXSIZE / n))
return luaL_error(L, "resulting string too large");
else {
size_t totallen = (size_t)n * l + (size_t)(n - 1) * lsep;
@@ -181,7 +182,7 @@ static int str_byte (lua_State *L) {
size_t pose = getendpos(L, 3, pi, l);
int n, i;
if (posi > pose) return 0; /* empty interval; return no values */
if (pose - posi >= (size_t)INT_MAX) /* arithmetic overflow? */
if (l_unlikely(pose - posi >= (size_t)INT_MAX)) /* arithmetic overflow? */
return luaL_error(L, "string slice too long");
n = (int)(pose - posi) + 1;
luaL_checkstack(L, n, "string slice too long");
@@ -235,7 +236,7 @@ static int str_dump (lua_State *L) {
luaL_checktype(L, 1, LUA_TFUNCTION);
lua_settop(L, 1); /* ensure function is on the top of the stack */
state.init = 0;
if (lua_dump(L, writer, &state, strip) != 0)
if (l_unlikely(lua_dump(L, writer, &state, strip) != 0))
return luaL_error(L, "unable to dump given function");
luaL_pushresult(&state.B);
return 1;
@@ -275,7 +276,8 @@ static int tonum (lua_State *L, int arg) {
static void trymt (lua_State *L, const char *mtname) {
lua_settop(L, 2); /* back to the original arguments */
if (lua_type(L, 2) == LUA_TSTRING || !luaL_getmetafield(L, 2, mtname))
if (l_unlikely(lua_type(L, 2) == LUA_TSTRING ||
!luaL_getmetafield(L, 2, mtname)))
luaL_error(L, "attempt to %s a '%s' with a '%s'", mtname + 2,
luaL_typename(L, -2), luaL_typename(L, -1));
lua_insert(L, -3); /* put metamethod before arguments */
@@ -383,7 +385,8 @@ static const char *match (MatchState *ms, const char *s, const char *p);
static int check_capture (MatchState *ms, int l) {
l -= '1';
if (l < 0 || l >= ms->level || ms->capture[l].len == CAP_UNFINISHED)
if (l_unlikely(l < 0 || l >= ms->level ||
ms->capture[l].len == CAP_UNFINISHED))
return luaL_error(ms->L, "invalid capture index %%%d", l + 1);
return l;
}
@@ -400,14 +403,14 @@ static int capture_to_close (MatchState *ms) {
static const char *classend (MatchState *ms, const char *p) {
switch (*p++) {
case L_ESC: {
if (p == ms->p_end)
if (l_unlikely(p == ms->p_end))
luaL_error(ms->L, "malformed pattern (ends with '%%')");
return p+1;
}
case '[': {
if (*p == '^') p++;
do { /* look for a ']' */
if (p == ms->p_end)
if (l_unlikely(p == ms->p_end))
luaL_error(ms->L, "malformed pattern (missing ']')");
if (*(p++) == L_ESC && p < ms->p_end)
p++; /* skip escapes (e.g. '%]') */
@@ -482,7 +485,7 @@ static int singlematch (MatchState *ms, const char *s, const char *p,
static const char *matchbalance (MatchState *ms, const char *s,
const char *p) {
if (p >= ms->p_end - 1)
if (l_unlikely(p >= ms->p_end - 1))
luaL_error(ms->L, "malformed pattern (missing arguments to '%%b')");
if (*s != *p) return NULL;
else {
@@ -565,7 +568,7 @@ static const char *match_capture (MatchState *ms, const char *s, int l) {
static const char *match (MatchState *ms, const char *s, const char *p) {
if (ms->matchdepth-- == 0)
if (l_unlikely(ms->matchdepth-- == 0))
luaL_error(ms->L, "pattern too complex");
init: /* using goto's to optimize tail recursion */
if (p != ms->p_end) { /* end of pattern? */
@@ -599,7 +602,7 @@ static const char *match (MatchState *ms, const char *s, const char *p) {
case 'f': { /* frontier? */
const char *ep; char previous;
p += 2;
if (*p != '[')
if (l_unlikely(*p != '['))
luaL_error(ms->L, "missing '[' after '%%f' in pattern");
ep = classend(ms, p); /* points to what is next */
previous = (s == ms->src_init) ? '\0' : *(s - 1);
@@ -699,7 +702,7 @@ static const char *lmemfind (const char *s1, size_t l1,
static size_t get_onecapture (MatchState *ms, int i, const char *s,
const char *e, const char **cap) {
if (i >= ms->level) {
if (i != 0)
if (l_unlikely(i != 0))
luaL_error(ms->L, "invalid capture index %%%d", i + 1);
*cap = s;
return e - s;
@@ -707,7 +710,7 @@ static size_t get_onecapture (MatchState *ms, int i, const char *s,
else {
ptrdiff_t capl = ms->capture[i].len;
*cap = ms->capture[i].init;
if (capl == CAP_UNFINISHED)
if (l_unlikely(capl == CAP_UNFINISHED))
luaL_error(ms->L, "unfinished capture");
else if (capl == CAP_POSITION)
lua_pushinteger(ms->L, (ms->capture[i].init - ms->src_init) + 1);
@@ -926,7 +929,7 @@ static int add_value (MatchState *ms, luaL_Buffer *b, const char *s,
luaL_addlstring(b, s, e - s); /* keep original text */
return 0; /* no changes */
}
else if (!lua_isstring(L, -1))
else if (l_unlikely(!lua_isstring(L, -1)))
return luaL_error(L, "invalid replacement value (a %s)",
luaL_typename(L, -1));
else {
@@ -1058,7 +1061,7 @@ static int lua_number2strx (lua_State *L, char *buff, int sz,
for (i = 0; i < n; i++)
buff[i] = toupper(uchar(buff[i]));
}
else if (fmt[SIZELENMOD] != 'a')
else if (l_unlikely(fmt[SIZELENMOD] != 'a'))
return luaL_error(L, "modifiers for format '%%a'/'%%A' not implemented");
return n;
}
@@ -1358,16 +1361,6 @@ struct cD {
#define MAXALIGN (offsetof(struct cD, u))
/*
** Union for serializing floats
*/
typedef union Ftypes {
float f;
double d;
lua_Number n;
} Ftypes;
/*
** information to pack/unpack stuff
*/
@@ -1384,7 +1377,9 @@ typedef struct Header {
typedef enum KOption {
Kint, /* signed integers */
Kuint, /* unsigned integers */
Kfloat, /* floating-point numbers */
Kfloat, /* single-precision floating-point numbers */
Knumber, /* Lua "native" floating-point numbers */
Kdouble, /* double-precision floating-point numbers */
Kchar, /* fixed-length strings */
Kstring, /* strings with prefixed length */
Kzstr, /* zero-terminated strings */
@@ -1419,7 +1414,7 @@ static int getnum (const char **fmt, int df) {
*/
static int getnumlimit (Header *h, const char **fmt, int df) {
int sz = getnum(fmt, df);
if (sz > MAXINTSIZE || sz <= 0)
if (l_unlikely(sz > MAXINTSIZE || sz <= 0))
return luaL_error(h->L, "integral size (%d) out of limits [1,%d]",
sz, MAXINTSIZE);
return sz;
@@ -1453,14 +1448,14 @@ static KOption getoption (Header *h, const char **fmt, int *size) {
case 'J': *size = sizeof(lua_Integer); return Kuint;
case 'T': *size = sizeof(size_t); return Kuint;
case 'f': *size = sizeof(float); return Kfloat;
case 'd': *size = sizeof(double); return Kfloat;
case 'n': *size = sizeof(lua_Number); return Kfloat;
case 'n': *size = sizeof(lua_Number); return Knumber;
case 'd': *size = sizeof(double); return Kdouble;
case 'i': *size = getnumlimit(h, fmt, sizeof(int)); return Kint;
case 'I': *size = getnumlimit(h, fmt, sizeof(int)); return Kuint;
case 's': *size = getnumlimit(h, fmt, sizeof(size_t)); return Kstring;
case 'c':
*size = getnum(fmt, -1);
if (*size == -1)
if (l_unlikely(*size == -1))
luaL_error(h->L, "missing size for format option 'c'");
return Kchar;
case 'z': return Kzstr;
@@ -1499,7 +1494,7 @@ static KOption getdetails (Header *h, size_t totalsize,
else {
if (align > h->maxalign) /* enforce maximum alignment */
align = h->maxalign;
if ((align & (align - 1)) != 0) /* is 'align' not a power of 2? */
if (l_unlikely((align & (align - 1)) != 0)) /* not a power of 2? */
luaL_argerror(h->L, 1, "format asks for alignment not power of 2");
*ntoalign = (align - (int)(totalsize & (align - 1))) & (align - 1);
}
@@ -1580,15 +1575,27 @@ static int str_pack (lua_State *L) {
packint(&b, (lua_Unsigned)n, h.islittle, size, 0);
break;
}
case Kfloat: { /* floating-point options */
Ftypes u;
char *buff = luaL_prepbuffsize(&b, size);
lua_Number n = luaL_checknumber(L, arg); /* get argument */
if (size == sizeof(u.f)) u.f = (float)n; /* copy it into 'u' */
else if (size == sizeof(u.d)) u.d = (double)n;
else u.n = n;
/* move 'u' to final result, correcting endianness if needed */
copywithendian(buff, (char *)&u, size, h.islittle);
case Kfloat: { /* C float */
float f = (float)luaL_checknumber(L, arg); /* get argument */
char *buff = luaL_prepbuffsize(&b, sizeof(f));
/* move 'f' to final result, correcting endianness if needed */
copywithendian(buff, (char *)&f, sizeof(f), h.islittle);
luaL_addsize(&b, size);
break;
}
case Knumber: { /* Lua float */
lua_Number f = luaL_checknumber(L, arg); /* get argument */
char *buff = luaL_prepbuffsize(&b, sizeof(f));
/* move 'f' to final result, correcting endianness if needed */
copywithendian(buff, (char *)&f, sizeof(f), h.islittle);
luaL_addsize(&b, size);
break;
}
case Kdouble: { /* C double */
double f = (double)luaL_checknumber(L, arg); /* get argument */
char *buff = luaL_prepbuffsize(&b, sizeof(f));
/* move 'f' to final result, correcting endianness if needed */
copywithendian(buff, (char *)&f, sizeof(f), h.islittle);
luaL_addsize(&b, size);
break;
}
@@ -1679,7 +1686,7 @@ static lua_Integer unpackint (lua_State *L, const char *str,
else if (size > SZINT) { /* must check unread bytes */
int mask = (!issigned || (lua_Integer)res >= 0) ? 0 : MC;
for (i = limit; i < size; i++) {
if ((unsigned char)str[islittle ? i : size - 1 - i] != mask)
if (l_unlikely((unsigned char)str[islittle ? i : size - 1 - i] != mask))
luaL_error(L, "%d-byte integer does not fit into Lua Integer", size);
}
}
@@ -1714,13 +1721,21 @@ static int str_unpack (lua_State *L) {
break;
}
case Kfloat: {
Ftypes u;
lua_Number num;
copywithendian((char *)&u, data + pos, size, h.islittle);
if (size == sizeof(u.f)) num = (lua_Number)u.f;
else if (size == sizeof(u.d)) num = (lua_Number)u.d;
else num = u.n;
lua_pushnumber(L, num);
float f;
copywithendian((char *)&f, data + pos, sizeof(f), h.islittle);
lua_pushnumber(L, (lua_Number)f);
break;
}
case Knumber: {
lua_Number f;
copywithendian((char *)&f, data + pos, sizeof(f), h.islittle);
lua_pushnumber(L, f);
break;
}
case Kdouble: {
double f;
copywithendian((char *)&f, data + pos, sizeof(f), h.islittle);
lua_pushnumber(L, (lua_Number)f);
break;
}
case Kchar: {

View File

@@ -68,20 +68,25 @@
#define MAXHSIZE luaM_limitN(1u << MAXHBITS, Node)
/*
** When the original hash value is good, hashing by a power of 2
** avoids the cost of '%'.
*/
#define hashpow2(t,n) (gnode(t, lmod((n), sizenode(t))))
/*
** for other types, it is better to avoid modulo by power of 2, as
** they can have many 2 factors.
*/
#define hashmod(t,n) (gnode(t, ((n) % ((sizenode(t)-1)|1))))
#define hashstr(t,str) hashpow2(t, (str)->hash)
#define hashboolean(t,p) hashpow2(t, p)
#define hashint(t,i) hashpow2(t, i)
/*
** for some types, it is better to avoid modulus by power of 2, as
** they tend to have many 2 factors.
*/
#define hashmod(t,n) (gnode(t, ((n) % ((sizenode(t)-1)|1))))
#define hashpointer(t,p) hashmod(t, point2uint(p))
@@ -135,24 +140,38 @@ static int l_hashfloat (lua_Number n) {
*/
static Node *mainposition (const Table *t, int ktt, const Value *kvl) {
switch (withvariant(ktt)) {
case LUA_VNUMINT:
return hashint(t, ivalueraw(*kvl));
case LUA_VNUMFLT:
return hashmod(t, l_hashfloat(fltvalueraw(*kvl)));
case LUA_VSHRSTR:
return hashstr(t, tsvalueraw(*kvl));
case LUA_VLNGSTR:
return hashpow2(t, luaS_hashlongstr(tsvalueraw(*kvl)));
case LUA_VNUMINT: {
lua_Integer key = ivalueraw(*kvl);
return hashint(t, key);
}
case LUA_VNUMFLT: {
lua_Number n = fltvalueraw(*kvl);
return hashmod(t, l_hashfloat(n));
}
case LUA_VSHRSTR: {
TString *ts = tsvalueraw(*kvl);
return hashstr(t, ts);
}
case LUA_VLNGSTR: {
TString *ts = tsvalueraw(*kvl);
return hashpow2(t, luaS_hashlongstr(ts));
}
case LUA_VFALSE:
return hashboolean(t, 0);
case LUA_VTRUE:
return hashboolean(t, 1);
case LUA_VLIGHTUSERDATA:
return hashpointer(t, pvalueraw(*kvl));
case LUA_VLCF:
return hashpointer(t, fvalueraw(*kvl));
default:
return hashpointer(t, gcvalueraw(*kvl));
case LUA_VLIGHTUSERDATA: {
void *p = pvalueraw(*kvl);
return hashpointer(t, p);
}
case LUA_VLCF: {
lua_CFunction f = fvalueraw(*kvl);
return hashpointer(t, f);
}
default: {
GCObject *o = gcvalueraw(*kvl);
return hashpointer(t, o);
}
}
}
@@ -309,7 +328,7 @@ static unsigned int findindex (lua_State *L, Table *t, TValue *key,
return i; /* yes; that's the index */
else {
const TValue *n = getgeneric(t, key, 1);
if (unlikely(isabstkey(n)))
if (l_unlikely(isabstkey(n)))
luaG_runerror(L, "invalid key to 'next'"); /* key not found */
i = cast_int(nodefromval(n) - gnode(t, 0)); /* key index in hash table */
/* hash elements are numbered after array ones */
@@ -487,7 +506,7 @@ static void reinsert (lua_State *L, Table *ot, Table *t) {
already present in the table */
TValue k;
getnodekey(L, &k, old);
setobjt2t(L, luaH_set(L, t, &k), gval(old));
luaH_set(L, t, &k, gval(old));
}
}
}
@@ -543,7 +562,7 @@ void luaH_resize (lua_State *L, Table *t, unsigned int newasize,
}
/* allocate new array */
newarray = luaM_reallocvector(L, t->array, oldasize, newasize, TValue);
if (unlikely(newarray == NULL && newasize > 0)) { /* allocation failed? */
if (l_unlikely(newarray == NULL && newasize > 0)) { /* allocation failed? */
freehash(L, &newt); /* release new hash part */
luaM_error(L); /* raise error (with array unchanged) */
}
@@ -634,10 +653,12 @@ static Node *getfreepos (Table *t) {
** put new key in its main position; otherwise (colliding node is in its main
** position), new key goes to an empty position.
*/
TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key) {
void luaH_newkey (lua_State *L, Table *t, const TValue *key, TValue *value) {
Node *mp;
TValue aux;
if (unlikely(ttisnil(key)))
if (l_unlikely(isshared(t)))
luaG_runerror(L, "attempt to change a shared table");
if (l_unlikely(ttisnil(key)))
luaG_runerror(L, "table index is nil");
else if (ttisfloat(key)) {
lua_Number f = fltvalue(key);
@@ -646,9 +667,11 @@ TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key) {
setivalue(&aux, k);
key = &aux; /* insert it as an integer */
}
else if (unlikely(luai_numisnan(f)))
else if (l_unlikely(luai_numisnan(f)))
luaG_runerror(L, "table index is NaN");
}
if (ttisnil(value))
return; /* do not insert nil values */
mp = mainpositionTV(t, key);
if (!isempty(gval(mp)) || isdummy(t)) { /* main position is taken? */
Node *othern;
@@ -656,7 +679,8 @@ TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key) {
if (f == NULL) { /* cannot find a free place? */
rehash(L, t, key); /* grow table */
/* whatever called 'newkey' takes care of TM cache */
return luaH_set(L, t, key); /* insert key into grown table */
luaH_set(L, t, key, value); /* insert key into grown table */
return;
}
lua_assert(!isdummy(t));
othern = mainposition(t, keytt(mp), &keyval(mp));
@@ -684,7 +708,7 @@ TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key) {
setnodekey(L, mp, key);
luaC_barrierback(L, obj2gco(t), key);
lua_assert(isempty(gval(mp)));
return gval(mp);
setobj2t(L, gval(mp), value);
}
@@ -771,35 +795,46 @@ const TValue *luaH_get (Table *t, const TValue *key) {
}
/*
** Finish a raw "set table" operation, where 'slot' is where the value
** should have been (the result of a previous "get table").
** Beware: when using this function you probably need to check a GC
** barrier and invalidate the TM cache.
*/
void luaH_finishset (lua_State *L, Table *t, const TValue *key,
const TValue *slot, TValue *value) {
if (isabstkey(slot))
luaH_newkey(L, t, key, value);
else {
if (l_unlikely(isshared(t)))
luaG_runerror(L, "attempt to change a shared table");
setobj2t(L, cast(TValue *, slot), value);
}
}
/*
** beware: when using this function you probably need to check a GC
** barrier and invalidate the TM cache.
*/
TValue *luaH_set (lua_State *L, Table *t, const TValue *key) {
const TValue *p;
if(isshared(t))
luaG_runerror(L, "attempt to change a shared table");
p = luaH_get(t, key);
if (!isabstkey(p))
return cast(TValue *, p);
else return luaH_newkey(L, t, key);
void luaH_set (lua_State *L, Table *t, const TValue *key, TValue *value) {
const TValue *slot = luaH_get(t, key);
luaH_finishset(L, t, key, slot, value);
}
void luaH_setint (lua_State *L, Table *t, lua_Integer key, TValue *value) {
const TValue *p;
TValue *cell;
if(isshared(t))
luaG_runerror(L, "attempt to change a shared table");
p = luaH_getint(t, key);
if (!isabstkey(p))
cell = cast(TValue *, p);
else {
const TValue *p = luaH_getint(t, key);
if (isabstkey(p)) {
TValue k;
setivalue(&k, key);
cell = luaH_newkey(L, t, &k);
luaH_newkey(L, t, &k, value);
}
else {
if (l_unlikely(isshared(t)))
luaG_runerror(L, "attempt to change a shared table");
setobj2t(L, cast(TValue *, p), value);
}
setobj2t(L, cell, value);
}

View File

@@ -41,8 +41,12 @@ LUAI_FUNC void luaH_setint (lua_State *L, Table *t, lua_Integer key,
LUAI_FUNC const TValue *luaH_getshortstr (Table *t, TString *key);
LUAI_FUNC const TValue *luaH_getstr (Table *t, TString *key);
LUAI_FUNC const TValue *luaH_get (Table *t, const TValue *key);
LUAI_FUNC TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key);
LUAI_FUNC TValue *luaH_set (lua_State *L, Table *t, const TValue *key);
LUAI_FUNC void luaH_newkey (lua_State *L, Table *t, const TValue *key,
TValue *value);
LUAI_FUNC void luaH_set (lua_State *L, Table *t, const TValue *key,
TValue *value);
LUAI_FUNC void luaH_finishset (lua_State *L, Table *t, const TValue *key,
const TValue *slot, TValue *value);
LUAI_FUNC Table *luaH_new (lua_State *L);
LUAI_FUNC void luaH_resize (lua_State *L, Table *t, unsigned int nasize,
unsigned int nhsize);

View File

@@ -145,8 +145,8 @@ static int tmove (lua_State *L) {
static void addfield (lua_State *L, luaL_Buffer *b, lua_Integer i) {
lua_geti(L, 1, i);
if (!lua_isstring(L, -1))
luaL_error(L, "invalid value (%s) at index %d in table for 'concat'",
if (l_unlikely(!lua_isstring(L, -1)))
luaL_error(L, "invalid value (%s) at index %I in table for 'concat'",
luaL_typename(L, -1), i);
luaL_addvalue(b);
}
@@ -196,7 +196,8 @@ static int tunpack (lua_State *L) {
lua_Integer e = luaL_opt(L, luaL_checkinteger, 3, luaL_len(L, 1));
if (i > e) return 0; /* empty range */
n = (lua_Unsigned)e - i; /* number of elements minus 1 (avoid overflows) */
if (n >= (unsigned int)INT_MAX || !lua_checkstack(L, (int)(++n)))
if (l_unlikely(n >= (unsigned int)INT_MAX ||
!lua_checkstack(L, (int)(++n))))
return luaL_error(L, "too many results to unpack");
for (; i < e; i++) { /* push arg[i..e - 1] (to avoid overflows) */
lua_geti(L, 1, i);
@@ -300,14 +301,14 @@ static IdxT partition (lua_State *L, IdxT lo, IdxT up) {
for (;;) {
/* next loop: repeat ++i while a[i] < P */
while ((void)lua_geti(L, 1, ++i), sort_comp(L, -1, -2)) {
if (i == up - 1) /* a[i] < P but a[up - 1] == P ?? */
if (l_unlikely(i == up - 1)) /* a[i] < P but a[up - 1] == P ?? */
luaL_error(L, "invalid order function for sorting");
lua_pop(L, 1); /* remove a[i] */
}
/* after the loop, a[i] >= P and a[lo .. i - 1] < P */
/* next loop: repeat --j while P < a[j] */
while ((void)lua_geti(L, 1, --j), sort_comp(L, -3, -1)) {
if (j < i) /* j < i but a[j] > P ?? */
if (l_unlikely(j < i)) /* j < i but a[j] > P ?? */
luaL_error(L, "invalid order function for sorting");
lua_pop(L, 1); /* remove a[j] */
}

View File

@@ -147,7 +147,7 @@ static int callbinTM (lua_State *L, const TValue *p1, const TValue *p2,
void luaT_trybinTM (lua_State *L, const TValue *p1, const TValue *p2,
StkId res, TMS event) {
if (!callbinTM(L, p1, p2, res, event)) {
if (l_unlikely(!callbinTM(L, p1, p2, res, event))) {
switch (event) {
case TM_BAND: case TM_BOR: case TM_BXOR:
case TM_SHL: case TM_SHR: case TM_BNOT: {
@@ -166,7 +166,8 @@ void luaT_trybinTM (lua_State *L, const TValue *p1, const TValue *p2,
void luaT_tryconcatTM (lua_State *L) {
StkId top = L->top;
if (!callbinTM(L, s2v(top - 2), s2v(top - 1), top - 2, TM_CONCAT))
if (l_unlikely(!callbinTM(L, s2v(top - 2), s2v(top - 1), top - 2,
TM_CONCAT)))
luaG_concaterror(L, s2v(top - 2), s2v(top - 1));
}

View File

@@ -37,6 +37,26 @@ static lua_State *globalL = NULL;
static const char *progname = LUA_PROGNAME;
#if defined(LUA_USE_POSIX) /* { */
/*
** Use 'sigaction' when available.
*/
static void setsignal (int sig, void (*handler)(int)) {
struct sigaction sa;
sa.sa_handler = handler;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask); /* do not mask any signal */
sigaction(sig, &sa, NULL);
}
#else /* }{ */
#define setsignal signal
#endif /* } */
/*
** Hook set by signal function to stop the interpreter.
*/
@@ -55,7 +75,7 @@ static void lstop (lua_State *L, lua_Debug *ar) {
*/
static void laction (int i) {
int flag = LUA_MASKCALL | LUA_MASKRET | LUA_MASKLINE | LUA_MASKCOUNT;
signal(i, SIG_DFL); /* if another SIGINT happens, terminate process */
setsignal(i, SIG_DFL); /* if another SIGINT happens, terminate process */
lua_sethook(globalL, lstop, flag, 1);
}
@@ -135,9 +155,9 @@ static int docall (lua_State *L, int narg, int nres) {
lua_pushcfunction(L, msghandler); /* push message handler */
lua_insert(L, base); /* put it under function and args */
globalL = L; /* to be available to 'laction' */
signal(SIGINT, laction); /* set C-signal handler */
setsignal(SIGINT, laction); /* set C-signal handler */
status = lua_pcall(L, narg, nres, base);
signal(SIGINT, SIG_DFL); /* reset C-signal handler */
setsignal(SIGINT, SIG_DFL); /* reset C-signal handler */
lua_remove(L, base); /* remove message handler from the stack */
return status;
}

View File

@@ -18,14 +18,14 @@
#define LUA_VERSION_MAJOR "5"
#define LUA_VERSION_MINOR "4"
#define LUA_VERSION_RELEASE "2"
#define LUA_VERSION_RELEASE "3"
#define LUA_VERSION_NUM 504
#define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + 0)
#define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR
#define LUA_RELEASE LUA_VERSION "." LUA_VERSION_RELEASE
#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2020 Lua.org, PUC-Rio"
#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2021 Lua.org, PUC-Rio"
#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes"
@@ -351,7 +351,8 @@ LUA_API size_t (lua_stringtonumber) (lua_State *L, const char *s);
LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud);
LUA_API void (lua_setallocf) (lua_State *L, lua_Alloc f, void *ud);
LUA_API void (lua_toclose) (lua_State *L, int idx);
LUA_API void (lua_toclose) (lua_State *L, int idx);
LUA_API void (lua_closeslot) (lua_State *L, int idx);
/*
@@ -495,7 +496,7 @@ struct lua_Debug {
/******************************************************************************
* Copyright (C) 1994-2020 Lua.org, PUC-Rio.
* Copyright (C) 1994-2021 Lua.org, PUC-Rio.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the

View File

@@ -16,13 +16,13 @@
** ===================================================================
** General Configuration File for Lua
**
** Some definitions here can be changed externally, through the
** compiler (e.g., with '-D' options). Those are protected by
** '#if !defined' guards. However, several other definitions should
** be changed directly here, either because they affect the Lua
** ABI (by making the changes here, you ensure that all software
** connected to Lua, such as C libraries, will be compiled with the
** same configuration); or because they are seldom changed.
** Some definitions here can be changed externally, through the compiler
** (e.g., with '-D' options): They are commented out or protected
** by '#if !defined' guards. However, several other definitions
** should be changed directly here, either because they affect the
** Lua ABI (by making the changes here, you ensure that all software
** connected to Lua, such as C libraries, will be compiled with the same
** configuration); or because they are seldom changed.
**
** Search for "@@" to find all configurable definitions.
** ===================================================================
@@ -81,26 +81,12 @@
/*
** {==================================================================
** Configuration for Number types.
** Configuration for Number types. These options should not be
** set externally, because any other code connected to Lua must
** use the same configuration.
** ===================================================================
*/
/*
@@ LUA_32BITS enables Lua with 32-bit integers and 32-bit floats.
*/
/* #define LUA_32BITS */
/*
@@ LUA_C89_NUMBERS ensures that Lua uses the largest types available for
** C89 ('long' and 'double'); Windows always has '__int64', so it does
** not need to use this case.
*/
#if defined(LUA_USE_C89) && !defined(LUA_USE_WINDOWS)
#define LUA_C89_NUMBERS
#endif
/*
@@ LUA_INT_TYPE defines the type for Lua integers.
@@ LUA_FLOAT_TYPE defines the type for Lua floats.
@@ -121,7 +107,31 @@
#define LUA_FLOAT_DOUBLE 2
#define LUA_FLOAT_LONGDOUBLE 3
#if defined(LUA_32BITS) /* { */
/* Default configuration ('long long' and 'double', for 64-bit Lua) */
#define LUA_INT_DEFAULT LUA_INT_LONGLONG
#define LUA_FLOAT_DEFAULT LUA_FLOAT_DOUBLE
/*
@@ LUA_32BITS enables Lua with 32-bit integers and 32-bit floats.
*/
#define LUA_32BITS 0
/*
@@ LUA_C89_NUMBERS ensures that Lua uses the largest types available for
** C89 ('long' and 'double'); Windows always has '__int64', so it does
** not need to use this case.
*/
#if defined(LUA_USE_C89) && !defined(LUA_USE_WINDOWS)
#define LUA_C89_NUMBERS 1
#else
#define LUA_C89_NUMBERS 0
#endif
#if LUA_32BITS /* { */
/*
** 32-bit integers and 'float'
*/
@@ -132,27 +142,22 @@
#endif
#define LUA_FLOAT_TYPE LUA_FLOAT_FLOAT
#elif defined(LUA_C89_NUMBERS) /* }{ */
#elif LUA_C89_NUMBERS /* }{ */
/*
** largest types available for C89 ('long' and 'double')
*/
#define LUA_INT_TYPE LUA_INT_LONG
#define LUA_FLOAT_TYPE LUA_FLOAT_DOUBLE
#else /* }{ */
/* use defaults */
#define LUA_INT_TYPE LUA_INT_DEFAULT
#define LUA_FLOAT_TYPE LUA_FLOAT_DEFAULT
#endif /* } */
/*
** default configuration for 64-bit Lua ('long long' and 'double')
*/
#if !defined(LUA_INT_TYPE)
#define LUA_INT_TYPE LUA_INT_LONGLONG
#endif
#if !defined(LUA_FLOAT_TYPE)
#define LUA_FLOAT_TYPE LUA_FLOAT_DOUBLE
#endif
/* }================================================================== */
@@ -373,14 +378,13 @@
/*
** {==================================================================
** Configuration for Numbers.
** Configuration for Numbers (low-level part).
** Change these definitions if no predefined LUA_FLOAT_* / LUA_INT_*
** satisfy your needs.
** ===================================================================
*/
/*
@@ LUA_NUMBER is the floating-point type used by Lua.
@@ LUAI_UACNUMBER is the result of a 'default argument promotion'
@@ over a floating number.
@@ l_floatatt(x) corrects float attribute 'x' to the proper float type
@@ -473,10 +477,7 @@
/*
@@ LUA_INTEGER is the integer type used by Lua.
**
@@ LUA_UNSIGNED is the unsigned version of LUA_INTEGER.
**
@@ LUAI_UACINT is the result of a 'default argument promotion'
@@ over a LUA_INTEGER.
@@ LUA_INTEGER_FRMLEN is the length modifier for reading/writing integers.
@@ -659,6 +660,34 @@
#define lua_getlocaledecpoint() (localeconv()->decimal_point[0])
#endif
/*
** macros to improve jump prediction, used mostly for error handling
** and debug facilities. (Some macros in the Lua API use these macros.
** Define LUA_NOBUILTIN if you do not want '__builtin_expect' in your
** code.)
*/
#if !defined(luai_likely)
#if defined(__GNUC__) && !defined(LUA_NOBUILTIN)
#define luai_likely(x) (__builtin_expect(((x) != 0), 1))
#define luai_unlikely(x) (__builtin_expect(((x) != 0), 0))
#else
#define luai_likely(x) (x)
#define luai_unlikely(x) (x)
#endif
#endif
#if defined(LUA_CORE) || defined(LUA_LIB)
/* shorter names for Lua's own use */
#define l_likely(x) luai_likely(x)
#define l_unlikely(x) luai_unlikely(x)
#endif
/* }================================================================== */

View File

@@ -52,10 +52,4 @@ LUALIB_API void (luaL_initcodecache) (void);
LUALIB_API void (luaL_openlibs) (lua_State *L);
#if !defined(lua_assert)
#define lua_assert(x) ((void)0)
#endif
#endif

View File

@@ -235,11 +235,11 @@ static int forprep (lua_State *L, StkId ra) {
}
else { /* try making all values floats */
lua_Number init; lua_Number limit; lua_Number step;
if (unlikely(!tonumber(plimit, &limit)))
if (l_unlikely(!tonumber(plimit, &limit)))
luaG_forerror(L, plimit, "limit");
if (unlikely(!tonumber(pstep, &step)))
if (l_unlikely(!tonumber(pstep, &step)))
luaG_forerror(L, pstep, "step");
if (unlikely(!tonumber(pinit, &init)))
if (l_unlikely(!tonumber(pinit, &init)))
luaG_forerror(L, pinit, "initial value");
if (step == 0)
luaG_runerror(L, "'for' step is zero");
@@ -292,7 +292,7 @@ void luaV_finishget (lua_State *L, const TValue *t, TValue *key, StkId val,
if (slot == NULL) { /* 't' is not a table? */
lua_assert(!ttistable(t));
tm = luaT_gettmbyobj(L, t, TM_INDEX);
if (unlikely(notm(tm)))
if (l_unlikely(notm(tm)))
luaG_typeerror(L, t, "index"); /* no metamethod */
/* else will try the metamethod */
}
@@ -339,10 +339,7 @@ void luaV_finishset (lua_State *L, const TValue *t, TValue *key,
lua_assert(isempty(slot)); /* slot must be empty */
tm = fasttm(L, h->metatable, TM_NEWINDEX); /* get metamethod */
if (tm == NULL) { /* no metamethod? */
if (isabstkey(slot)) /* no previous entry? */
slot = luaH_newkey(L, h, key); /* create one */
/* no metamethod and (now) there is an entry with given key */
setobj2t(L, cast(TValue *, slot), val); /* set its new value */
luaH_finishset(L, h, key, slot, val); /* set new value */
invalidateTMcache(h);
luaC_barrierback(L, obj2gco(h), val);
return;
@@ -351,7 +348,7 @@ void luaV_finishset (lua_State *L, const TValue *t, TValue *key,
}
else { /* not a table; check metamethod */
tm = luaT_gettmbyobj(L, t, TM_NEWINDEX);
if (unlikely(notm(tm)))
if (l_unlikely(notm(tm)))
luaG_typeerror(L, t, "index");
}
/* try the metamethod */
@@ -573,8 +570,13 @@ int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2) {
if (ttype(t1) != ttype(t2) || ttype(t1) != LUA_TNUMBER)
return 0; /* only numbers can be equal with different variants */
else { /* two numbers with different variants */
lua_Integer i1, i2; /* compare them as integers */
return (tointegerns(t1, &i1) && tointegerns(t2, &i2) && i1 == i2);
/* One of them is an integer. If the other does not have an
integer value, they cannot be equal; otherwise, compare their
integer values. */
lua_Integer i1, i2;
return (luaV_tointegerns(t1, &i1, F2Ieq) &&
luaV_tointegerns(t2, &i2, F2Ieq) &&
i1 == i2);
}
}
/* values have same type and same variant */
@@ -656,7 +658,7 @@ void luaV_concat (lua_State *L, int total) {
/* collect total length and number of strings */
for (n = 1; n < total && tostring(L, s2v(top - n - 1)); n++) {
size_t l = vslen(s2v(top - n - 1));
if (unlikely(l >= (MAX_SIZE/sizeof(char)) - tl))
if (l_unlikely(l >= (MAX_SIZE/sizeof(char)) - tl))
luaG_runerror(L, "string length overflow");
tl += l;
}
@@ -700,7 +702,7 @@ void luaV_objlen (lua_State *L, StkId ra, const TValue *rb) {
}
default: { /* try metamethod */
tm = luaT_gettmbyobj(L, rb, TM_LEN);
if (unlikely(notm(tm))) /* no metamethod? */
if (l_unlikely(notm(tm))) /* no metamethod? */
luaG_typeerror(L, rb, "get length of");
break;
}
@@ -716,7 +718,7 @@ void luaV_objlen (lua_State *L, StkId ra, const TValue *rb) {
** otherwise 'floor(q) == trunc(q) - 1'.
*/
lua_Integer luaV_idiv (lua_State *L, lua_Integer m, lua_Integer n) {
if (unlikely(l_castS2U(n) + 1u <= 1u)) { /* special cases: -1 or 0 */
if (l_unlikely(l_castS2U(n) + 1u <= 1u)) { /* special cases: -1 or 0 */
if (n == 0)
luaG_runerror(L, "attempt to divide by zero");
return intop(-, 0, m); /* n==-1; avoid overflow with 0x80000...//-1 */
@@ -736,7 +738,7 @@ lua_Integer luaV_idiv (lua_State *L, lua_Integer m, lua_Integer n) {
** about luaV_idiv.)
*/
lua_Integer luaV_mod (lua_State *L, lua_Integer m, lua_Integer n) {
if (unlikely(l_castS2U(n) + 1u <= 1u)) { /* special cases: -1 or 0 */
if (l_unlikely(l_castS2U(n) + 1u <= 1u)) { /* special cases: -1 or 0 */
if (n == 0)
luaG_runerror(L, "attempt to perform 'n%%0'");
return 0; /* m % -1 == 0; avoid overflow with 0x80000...%-1 */
@@ -847,6 +849,10 @@ void luaV_finishOp (lua_State *L) {
luaV_concat(L, total); /* concat them (may yield again) */
break;
}
case OP_CLOSE: case OP_RETURN: { /* yielded closing variables */
ci->u.l.savedpc--; /* repeat instruction to close other vars. */
break;
}
default: {
/* only these other opcodes can yield */
lua_assert(op == OP_TFORCALL || op == OP_CALL ||
@@ -922,7 +928,7 @@ void luaV_finishOp (lua_State *L) {
*/
#define op_arithfK(L,fop) { \
TValue *v1 = vRB(i); \
TValue *v2 = KC(i); \
TValue *v2 = KC(i); lua_assert(ttisnumber(v2)); \
op_arithf_aux(L, v1, v2, fop); }
@@ -951,7 +957,7 @@ void luaV_finishOp (lua_State *L) {
*/
#define op_arithK(L,iop,fop) { \
TValue *v1 = vRB(i); \
TValue *v2 = KC(i); \
TValue *v2 = KC(i); lua_assert(ttisnumber(v2)); \
op_arith_aux(L, v1, v2, iop, fop); }
@@ -1050,7 +1056,8 @@ void luaV_finishOp (lua_State *L) {
#define updatebase(ci) (base = ci->func + 1)
#define updatestack(ci) { if (trap) { updatebase(ci); ra = RA(i); } }
#define updatestack(ci) \
{ if (l_unlikely(trap)) { updatebase(ci); ra = RA(i); } }
/*
@@ -1108,7 +1115,7 @@ void luaV_finishOp (lua_State *L) {
/* fetch an instruction and prepare its execution */
#define vmfetch() { \
if (trap) { /* stack reallocation or hooks? */ \
if (l_unlikely(trap)) { /* stack reallocation or hooks? */ \
trap = luaG_traceexec(L, pc); /* handle hooks */ \
updatebase(ci); /* correct stack */ \
} \
@@ -1136,7 +1143,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
cl = clLvalue(s2v(ci->func));
k = cl->p->k;
pc = ci->u.l.savedpc;
if (trap) {
if (l_unlikely(trap)) {
if (pc == cl->p->code) { /* first instruction (not resuming)? */
if (cl->p->is_vararg)
trap = 0; /* hooks will start after VARARGPREP instruction */
@@ -1151,6 +1158,8 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
Instruction i; /* instruction being executed */
StkId ra; /* instruction's A register */
vmfetch();
// low-level line tracing for debugging Lua
// printf("line: %d\n", luaG_getfuncline(cl->p, pcRel(pc, cl->p)));
lua_assert(base == ci->func + 1);
lua_assert(base <= L->top && L->top < L->stack_last);
/* invalidate top for instructions not expecting it */
@@ -1529,7 +1538,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
vmbreak;
}
vmcase(OP_CLOSE) {
Protect(luaF_close(L, ra, LUA_OK));
Protect(luaF_close(L, ra, LUA_OK, 1));
vmbreak;
}
vmcase(OP_TBC) {
@@ -1634,10 +1643,8 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
b = cast_int(L->top - ra);
savepc(ci); /* several calls here can raise errors */
if (TESTARG_k(i)) {
/* close upvalues from current call; the compiler ensures
that there are no to-be-closed variables here, so this
call cannot change the stack */
luaF_close(L, base, NOCLOSINGMETH);
luaF_closeupval(L, base); /* close upvalues from current call */
lua_assert(L->tbclist < base); /* no pending tbc variables */
lua_assert(base == ci->func + 1);
}
while (!ttisfunction(s2v(ra))) { /* not a function? */
@@ -1667,7 +1674,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
if (TESTARG_k(i)) { /* may there be open upvalues? */
if (L->top < ci->top)
L->top = ci->top;
luaF_close(L, base, LUA_OK);
luaF_close(L, base, CLOSEKTOP, 1);
updatetrap(ci);
updatestack(ci);
}
@@ -1679,23 +1686,23 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
goto ret;
}
vmcase(OP_RETURN0) {
if (L->hookmask) {
if (l_unlikely(L->hookmask)) {
L->top = ra;
savepc(ci);
luaD_poscall(L, ci, 0); /* no hurry... */
trap = 1;
}
else { /* do the 'poscall' here */
int nres = ci->nresults;
int nres;
L->ci = ci->previous; /* back to caller */
L->top = base - 1;
while (nres-- > 0)
for (nres = ci->nresults; l_unlikely(nres > 0); nres--)
setnilvalue(s2v(L->top++)); /* all results are nil */
}
goto ret;
}
vmcase(OP_RETURN1) {
if (L->hookmask) {
if (l_unlikely(L->hookmask)) {
L->top = ra + 1;
savepc(ci);
luaD_poscall(L, ci, 1); /* no hurry... */
@@ -1709,8 +1716,8 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
else {
setobjs2s(L, base - 1, ra); /* at least this result */
L->top = base;
while (--nres > 0) /* complete missing results */
setnilvalue(s2v(L->top++));
for (; l_unlikely(nres > 1); nres--)
setnilvalue(s2v(L->top++)); /* complete missing results */
}
}
ret: /* return from a Lua function */
@@ -1813,7 +1820,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
}
vmcase(OP_VARARGPREP) {
ProtectNT(luaT_adjustvarargs(L, GETARG_A(i), ci, cl->p));
if (trap) {
if (l_unlikely(trap)) { /* previous "Protect" updated trap */
luaD_hookcall(L, ci);
L->oldpc = 1; /* next opcode will be seen as a "new" line */
}

View File

@@ -60,12 +60,14 @@ typedef enum {
/* convert an object to an integer (including string coercion) */
#define tointeger(o,i) \
(ttisinteger(o) ? (*(i) = ivalue(o), 1) : luaV_tointeger(o,i,LUA_FLOORN2I))
(l_likely(ttisinteger(o)) ? (*(i) = ivalue(o), 1) \
: luaV_tointeger(o,i,LUA_FLOORN2I))
/* convert an object to an integer (without string coercion) */
#define tointegerns(o,i) \
(ttisinteger(o) ? (*(i) = ivalue(o), 1) : luaV_tointegerns(o,i,LUA_FLOORN2I))
(l_likely(ttisinteger(o)) ? (*(i) = ivalue(o), 1) \
: luaV_tointegerns(o,i,LUA_FLOORN2I))
#define intop(op,v1,v2) l_castU2S(l_castS2U(v1) op l_castS2U(v2))

View File

@@ -1,3 +1,14 @@
v1.5.0 (2021-11-9)
-----------
* Update Lua to 5.4.3
* Fix socket half close issues
* Fix TLS issues
* Improve websocket support
* Improve redis support
* Rework skynet.init/skynet.require
* Add socket.onclose
* Add httpc.request_stream
v1.4.0 (2020-11-16)
-----------
* Update Lua to 5.4.2

View File

@@ -119,7 +119,8 @@ $(LUA_CLIB_PATH)/lpeg.so : 3rd/lpeg/lpcap.c 3rd/lpeg/lpcode.c 3rd/lpeg/lpprint.c
$(CC) $(CFLAGS) $(SHARED) -I3rd/lpeg $^ -o $@
clean :
rm -f $(SKYNET_BUILD_PATH)/skynet $(CSERVICE_PATH)/*.so $(LUA_CLIB_PATH)/*.so
rm -f $(SKYNET_BUILD_PATH)/skynet $(CSERVICE_PATH)/*.so $(LUA_CLIB_PATH)/*.so && \
rm -rf $(SKYNET_BUILD_PATH)/*.dSYM $(CSERVICE_PATH)/*.dSYM $(LUA_CLIB_PATH)/*.dSYM
cleanall: clean
ifneq (,$(wildcard 3rd/jemalloc/Makefile))

View File

@@ -32,7 +32,7 @@ Run these in different consoles:
## About Lua version
Skynet now uses a modified version of lua 5.4.2 ( https://github.com/ejoy/lua/tree/skynet54 ) for multiple lua states.
Skynet now uses a modified version of lua 5.4.3 ( https://github.com/ejoy/lua/tree/skynet54 ) for multiple lua states.
Official Lua versions can also be used as long as the Makefile is edited.

View File

@@ -182,6 +182,7 @@ _ltls_context_handshake(lua_State* L) {
return 0;
} else if (ret < 0) {
int err = SSL_get_error(tls_p->ssl, ret);
ERR_clear_error();
if(err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
int all_read = _bio_read(L, tls_p);
if(all_read>0) {
@@ -192,6 +193,7 @@ _ltls_context_handshake(lua_State* L) {
}
} else {
int err = SSL_get_error(tls_p->ssl, ret);
ERR_clear_error();
luaL_error(L, "SSL_do_handshake error:%d ret:%d", err, ret);
}
}
@@ -219,6 +221,7 @@ _ltls_context_read(lua_State* L) {
read = SSL_read(tls_p->ssl, outbuff, sizeof(outbuff));
if(read <= 0) {
int err = SSL_get_error(tls_p->ssl, read);
ERR_clear_error();
if(err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
break;
}
@@ -244,6 +247,7 @@ _ltls_context_write(lua_State* L) {
int written = SSL_write(tls_p->ssl, unencrypted_data, slen);
if(written <= 0) {
int err = SSL_get_error(tls_p->ssl, written);
ERR_clear_error();
luaL_error(L, "SSL_write error:%d", err);
}else if(written <= slen) {
unencrypted_data += written;
@@ -354,6 +358,10 @@ lnew_tls(lua_State* L) {
if(strcmp(method, "client") == 0) {
_init_client_context(L, tls_p, ctx_p);
if (!lua_isnoneornil(L, 3)) {
const char* hostname = luaL_checkstring(L, 3);
SSL_set_tlsext_host_name(tls_p->ssl, hostname);
}
}else if(strcmp(method, "server") == 0) {
_init_server_context(L, tls_p, ctx_p);
} else {
@@ -378,9 +386,11 @@ lnew_tls(lua_State* L) {
return 1;
}
int
luaopen_ltls_c(lua_State* L) {
if(!TLS_IS_INIT) {
luaL_error(L, "ltls need init, Put enablessl = true in you config file.");
}
luaL_Reg l[] = {
{"newctx", lnew_ctx},
{"newtls", lnew_tls},
@@ -391,18 +401,24 @@ luaopen_ltls_c(lua_State* L) {
return 1;
}
void __attribute__((constructor)) ltls_init(void) {
// for ltls init
static int
ltls_init_constructor(lua_State* L) {
#ifndef OPENSSL_EXTERNAL_INITIALIZATION
SSL_library_init();
SSL_load_error_strings();
ERR_load_BIO_strings();
OpenSSL_add_all_algorithms();
TLS_IS_INIT = true;
if(!TLS_IS_INIT) {
SSL_library_init();
SSL_load_error_strings();
ERR_load_BIO_strings();
OpenSSL_add_all_algorithms();
}
#endif
TLS_IS_INIT = true;
return 0;
}
void __attribute__((destructor)) ltls_destory(void) {
static int
ltls_init_destructor(lua_State* L) {
#ifndef OPENSSL_EXTERNAL_INITIALIZATION
if(TLS_IS_INIT) {
ENGINE_cleanup();
CONF_modules_unload(1);
@@ -411,4 +427,19 @@ void __attribute__((destructor)) ltls_destory(void) {
sk_SSL_COMP_free(SSL_COMP_get_compression_methods());
CRYPTO_cleanup_all_ex_data();
}
#endif
TLS_IS_INIT = false;
return 0;
}
int
luaopen_ltls_init_c(lua_State* L) {
luaL_Reg l[] = {
{"constructor", ltls_init_constructor},
{"destructor", ltls_init_destructor},
{NULL, NULL},
};
luaL_checkversion(L);
luaL_newlib(L, l);
return 1;
}

View File

@@ -512,16 +512,14 @@ pack_meta_dict(lua_State *L, struct bson *b, int depth) {
static bool
is_rawarray(lua_State *L) {
size_t len = lua_rawlen(L, -1);
if (len > 0) {
lua_pushinteger(L, len);
if (lua_next(L,-2) == 0) {
return true;
} else {
lua_pop(L,2);
}
lua_pushnil(L);
if (lua_next(L, -2) == 0) {
// empty table
return false;
}
return false;
lua_Integer firstkey = lua_isinteger(L, -2) ? lua_tointeger(L, -2) : 0;
lua_pop(L, 2);
return firstkey > 0;
}
static void
@@ -1086,6 +1084,7 @@ lbinary(lua_State *L) {
luaL_addchar(&b, 0);
luaL_addchar(&b, BSON_BINARY);
luaL_addchar(&b, 0); // sub type
lua_pushvalue(L,1);
luaL_addvalue(&b);
luaL_pushresult(&b);
@@ -1245,11 +1244,11 @@ typeclosure(lua_State *L) {
}
static uint8_t oid_header[5];
static uint32_t oid_counter;
static ATOM_ULONG oid_counter;
static void
init_oid_header() {
if (oid_counter) {
if (ATOM_LOAD(&oid_counter)) {
// already init
return;
}
@@ -1269,11 +1268,11 @@ init_oid_header() {
oid_header[3] = pid & 0xff;
oid_header[4] = (pid >> 8) & 0xff;
uint32_t c = h ^ time(NULL) ^ (uintptr_t)&h;
unsigned long c = h ^ time(NULL) ^ (uintptr_t)&h;
if (c == 0) {
c = 1;
}
oid_counter = c;
ATOM_STORE(&oid_counter, c);
}
static inline int

View File

@@ -185,6 +185,40 @@ lreadstdin(lua_State *L) {
return 1;
}
static int
lshutdown(lua_State *L) {
int fd = luaL_checkinteger(L,1);
const char *mode = luaL_checkstring(L,2);
int v = 0;
int i;
int read = 1;
int write = 2;
for (i=0;mode[i];i++) {
switch(mode[i]) {
case 'r':
v |= read;
break;
case 'w':
v |= write;
break;
default:
return luaL_error(L, "Invalid mode %c", mode[i]);
}
}
if (v == 0) {
return luaL_error(L, "mode should be r or/and w");
}
if (v == read)
v = SHUT_RD;
else if (v == write)
v = SHUT_WR;
else
v = SHUT_RDWR;
printf("SHUTDOWN %d %d\n", fd, v);
shutdown(fd, v);
return 0;
}
LUAMOD_API int
luaopen_client_socket(lua_State *L) {
luaL_checkversion(L);
@@ -192,6 +226,7 @@ luaopen_client_socket(lua_State *L) {
{ "connect", lconnect },
{ "recv", lrecv },
{ "send", lsend },
{ "shutdown", lshutdown },
{ "close", lclose },
{ "usleep", lusleep },
{ NULL, NULL },

View File

@@ -366,6 +366,8 @@ lunpackrequest(lua_State *L) {
msg = luaL_checklstring(L,1,&ssz);
sz = (int)ssz;
}
if (sz == 0)
return luaL_error(L, "Invalid req package. size == 0");
switch (msg[0]) {
case 0:
return unpackreq_number(L, (const uint8_t *)msg, sz);

View File

@@ -10,7 +10,7 @@
#include "atomic.h"
struct mc_package {
int reference;
ATOM_INT reference;
uint32_t size;
void *data;
};
@@ -18,7 +18,7 @@ struct mc_package {
static int
pack(lua_State *L, void *data, size_t size) {
struct mc_package * pack = skynet_malloc(sizeof(struct mc_package));
pack->reference = 0;
ATOM_INIT(&pack->reference, 0);
pack->size = (uint32_t)size;
pack->data = data;
struct mc_package ** ret = skynet_malloc(sizeof(*ret));
@@ -91,10 +91,10 @@ static int
mc_bindrefer(lua_State *L) {
struct mc_package ** pack = lua_touserdata(L,1);
int ref = luaL_checkinteger(L,2);
if ((*pack)->reference != 0) {
if (ATOM_LOAD(&(*pack)->reference) != 0) {
return luaL_error(L, "Can't bind a multicast package more than once");
}
(*pack)->reference = ref;
ATOM_STORE(&(*pack)->reference , ref);
lua_pushlightuserdata(L, *pack);
@@ -110,7 +110,7 @@ static int
mc_closelocal(lua_State *L) {
struct mc_package *pack = lua_touserdata(L,1);
int ref = ATOM_DEC(&pack->reference);
int ref = ATOM_FDEC(&pack->reference)-1;
if (ref <= 0) {
skynet_free(pack->data);
skynet_free(pack);

View File

@@ -115,7 +115,7 @@ rball_init(struct read_block * rb, char * buffer, int size) {
rb->ptr = 0;
}
static void *
static const void *
rb_read(struct read_block *rb, int sz) {
if (rb->len < sz) {
return NULL;
@@ -363,7 +363,7 @@ get_integer(lua_State *L, struct read_block *rb, int cookie) {
return 0;
case TYPE_NUMBER_BYTE: {
uint8_t n;
uint8_t * pn = rb_read(rb,sizeof(n));
const uint8_t * pn = (const uint8_t *)rb_read(rb,sizeof(n));
if (pn == NULL)
invalid_stream(L,rb);
n = *pn;
@@ -371,7 +371,7 @@ get_integer(lua_State *L, struct read_block *rb, int cookie) {
}
case TYPE_NUMBER_WORD: {
uint16_t n;
uint16_t * pn = rb_read(rb,sizeof(n));
const void * pn = rb_read(rb,sizeof(n));
if (pn == NULL)
invalid_stream(L,rb);
memcpy(&n, pn, sizeof(n));
@@ -379,7 +379,7 @@ get_integer(lua_State *L, struct read_block *rb, int cookie) {
}
case TYPE_NUMBER_DWORD: {
int32_t n;
int32_t * pn = rb_read(rb,sizeof(n));
const void * pn = rb_read(rb,sizeof(n));
if (pn == NULL)
invalid_stream(L,rb);
memcpy(&n, pn, sizeof(n));
@@ -387,7 +387,7 @@ get_integer(lua_State *L, struct read_block *rb, int cookie) {
}
case TYPE_NUMBER_QWORD: {
int64_t n;
int64_t * pn = rb_read(rb,sizeof(n));
const void * pn = rb_read(rb,sizeof(n));
if (pn == NULL)
invalid_stream(L,rb);
memcpy(&n, pn, sizeof(n));
@@ -402,7 +402,7 @@ get_integer(lua_State *L, struct read_block *rb, int cookie) {
static double
get_real(lua_State *L, struct read_block *rb) {
double n;
double * pn = rb_read(rb,sizeof(n));
const void * pn = rb_read(rb,sizeof(n));
if (pn == NULL)
invalid_stream(L,rb);
memcpy(&n, pn, sizeof(n));
@@ -412,7 +412,7 @@ get_real(lua_State *L, struct read_block *rb) {
static void *
get_pointer(lua_State *L, struct read_block *rb) {
void * userdata = 0;
void ** v = (void **)rb_read(rb,sizeof(userdata));
const void * v = rb_read(rb,sizeof(userdata));
if (v == NULL) {
invalid_stream(L,rb);
}
@@ -422,7 +422,7 @@ get_pointer(lua_State *L, struct read_block *rb) {
static void
get_buffer(lua_State *L, struct read_block *rb, int len) {
char * p = rb_read(rb,len);
const char * p = (const char *)rb_read(rb,len);
if (p == NULL) {
invalid_stream(L,rb);
}
@@ -435,7 +435,7 @@ static void
unpack_table(lua_State *L, struct read_block *rb, int array_size) {
if (array_size == MAX_COOKIE-1) {
uint8_t type;
uint8_t *t = rb_read(rb, sizeof(type));
const uint8_t * t = (const uint8_t *)rb_read(rb, sizeof(type));
if (t==NULL) {
invalid_stream(L,rb);
}
@@ -488,7 +488,7 @@ push_value(lua_State *L, struct read_block *rb, int type, int cookie) {
break;
case TYPE_LONG_STRING: {
if (cookie == 2) {
uint16_t *plen = rb_read(rb, 2);
const void * plen = rb_read(rb, 2);
if (plen == NULL) {
invalid_stream(L,rb);
}
@@ -499,7 +499,7 @@ push_value(lua_State *L, struct read_block *rb, int type, int cookie) {
if (cookie != 4) {
invalid_stream(L,rb);
}
uint32_t *plen = rb_read(rb, 4);
const void * plen = rb_read(rb, 4);
if (plen == NULL) {
invalid_stream(L,rb);
}
@@ -523,7 +523,7 @@ push_value(lua_State *L, struct read_block *rb, int type, int cookie) {
static void
unpack_one(lua_State *L, struct read_block *rb) {
uint8_t type;
uint8_t *t = rb_read(rb, sizeof(type));
const uint8_t * t = (const uint8_t *)rb_read(rb, sizeof(type));
if (t==NULL) {
invalid_stream(L, rb);
}
@@ -584,7 +584,7 @@ luaseri_unpack(lua_State *L) {
luaL_checkstack(L,LUA_MINSTACK,NULL);
}
uint8_t type = 0;
uint8_t *t = rb_read(&rb, sizeof(type));
const uint8_t * t = (const uint8_t *)rb_read(&rb, sizeof(type));
if (t==NULL)
break;
type = *t;

View File

@@ -40,7 +40,7 @@ struct node {
struct state {
int dirty;
int ref;
ATOM_INT ref;
struct table * root;
};
@@ -383,7 +383,7 @@ convert_stringmap(struct context *ctx, struct table *tbl) {
lua_pushvalue(L, 1);
struct state * s = lua_newuserdatauv(L, sizeof(*s), 1);
s->dirty = 0;
s->ref = 0;
ATOM_INIT(&s->ref , 0);
s->root = tbl;
lua_replace(L, 1);
lua_replace(L, -2);
@@ -665,7 +665,7 @@ releaseobj(lua_State *L) {
struct ctrl *c = lua_touserdata(L, 1);
struct table *tbl = c->root;
struct state *s = lua_touserdata(tbl->L, 1);
ATOM_DEC(&s->ref);
ATOM_FDEC(&s->ref);
c->root = NULL;
c->update = NULL;
@@ -676,7 +676,7 @@ static int
lboxconf(lua_State *L) {
struct table * tbl = get_table(L,1);
struct state * s = lua_touserdata(tbl->L, 1);
ATOM_INC(&s->ref);
ATOM_FINC(&s->ref);
struct ctrl * c = lua_newuserdatauv(L, sizeof(*c), 1);
c->root = tbl;
@@ -712,7 +712,7 @@ static int
lgetref(lua_State *L) {
struct table *tbl = get_table(L,1);
struct state * s = lua_touserdata(tbl->L, 1);
lua_pushinteger(L , s->ref);
lua_pushinteger(L , ATOM_LOAD(&s->ref));
return 1;
}
@@ -721,7 +721,7 @@ static int
lincref(lua_State *L) {
struct table *tbl = get_table(L,1);
struct state * s = lua_touserdata(tbl->L, 1);
int ref = ATOM_INC(&s->ref);
int ref = ATOM_FINC(&s->ref)+1;
lua_pushinteger(L , ref);
return 1;
@@ -731,7 +731,7 @@ static int
ldecref(lua_State *L) {
struct table *tbl = get_table(L,1);
struct state * s = lua_touserdata(tbl->L, 1);
int ref = ATOM_DEC(&s->ref);
int ref = ATOM_FDEC(&s->ref)-1;
lua_pushinteger(L , ref);
return 1;

View File

@@ -41,6 +41,7 @@ mark_shared(lua_State *L) {
} else if (!lua_iscfunction(L, idx)) {
LClosure *f = (LClosure *)lua_topointer(L, idx);
makeshared(f);
lua_sharefunction(L, idx);
}
break;
case LUA_TSTRING:

View File

@@ -11,8 +11,11 @@
#include <lua.h>
#include <lauxlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include "skynet.h"
#include "skynet_socket.h"
@@ -742,6 +745,9 @@ getinfo(lua_State *L, struct socket_info *si) {
case SOCKET_INFO_BIND:
lua_pushstring(L, "BIND");
break;
case SOCKET_INFO_CLOSING:
lua_pushstring(L, "CLOSING");
break;
default:
lua_pushstring(L, "UNKNOWN");
lua_setfield(L, -2, "type");
@@ -783,6 +789,34 @@ linfo(lua_State *L) {
return 1;
}
static int
lresolve(lua_State *L) {
const char * host = luaL_checkstring(L, 1);
int status;
struct addrinfo ai_hints;
struct addrinfo *ai_list = NULL;
struct addrinfo *ai_ptr = NULL;
memset( &ai_hints, 0, sizeof( ai_hints ) );
status = getaddrinfo( host, NULL, &ai_hints, &ai_list);
if ( status != 0 ) {
return luaL_error(L, gai_strerror(status));
}
lua_newtable(L);
int idx = 1;
char tmp[128];
for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next ) {
struct sockaddr * addr = ai_ptr->ai_addr;
void * sin_addr = (ai_ptr->ai_family == AF_INET) ? (void*)&((struct sockaddr_in *)addr)->sin_addr : (void*)&((struct sockaddr_in6 *)addr)->sin6_addr;
if (inet_ntop(ai_ptr->ai_family, sin_addr, tmp, sizeof(tmp))) {
lua_pushstring(L, tmp);
lua_rawseti(L, -2, idx++);
}
}
freeaddrinfo(ai_list);
return 1;
}
LUAMOD_API int
luaopen_skynet_socketdriver(lua_State *L) {
luaL_checkversion(L);
@@ -817,6 +851,7 @@ luaopen_skynet_socketdriver(lua_State *L) {
{ "udp_connect", ludp_connect },
{ "udp_send", ludp_send },
{ "udp_address", ludp_address },
{ "resolve", lresolve },
{ NULL, NULL },
};
lua_getfield(L, LUA_REGISTRYINDEX, "skynet_context");

View File

@@ -13,12 +13,12 @@
struct stm_object {
struct rwlock lock;
int reference;
ATOM_INT reference;
struct stm_copy * copy;
};
struct stm_copy {
int reference;
ATOM_INT reference;
uint32_t sz;
void * msg;
};
@@ -27,7 +27,7 @@ struct stm_copy {
static struct stm_copy *
stm_newcopy(void * msg, int32_t sz) {
struct stm_copy * copy = skynet_malloc(sizeof(*copy));
copy->reference = 1;
ATOM_INIT(&copy->reference, 1);
copy->sz = sz;
copy->msg = msg;
@@ -38,7 +38,7 @@ static struct stm_object *
stm_new(void * msg, int32_t sz) {
struct stm_object * obj = skynet_malloc(sizeof(*obj));
rwlock_init(&obj->lock);
obj->reference = 1;
ATOM_INIT(&obj->reference , 1);
obj->copy = stm_newcopy(msg, sz);
return obj;
@@ -48,7 +48,7 @@ static void
stm_releasecopy(struct stm_copy *copy) {
if (copy == NULL)
return;
if (ATOM_DEC(&copy->reference) == 0) {
if (ATOM_FDEC(&copy->reference) <= 1) {
skynet_free(copy->msg);
skynet_free(copy);
}
@@ -61,7 +61,7 @@ stm_release(struct stm_object *obj) {
// writer release the stm object, so release the last copy .
stm_releasecopy(obj->copy);
obj->copy = NULL;
if (--obj->reference > 0) {
if (ATOM_FDEC(&obj->reference) > 1) {
// stm object grab by readers, reset the copy to NULL.
rwlock_wunlock(&obj->lock);
return;
@@ -73,7 +73,7 @@ stm_release(struct stm_object *obj) {
static void
stm_releasereader(struct stm_object *obj) {
rwlock_rlock(&obj->lock);
if (ATOM_DEC(&obj->reference) == 0) {
if (ATOM_FDEC(&obj->reference) == 1) {
// last reader, no writer. so no need to unlock
assert(obj->copy == NULL);
skynet_free(obj);

View File

@@ -1,6 +1,5 @@
local skynet = require "skynet"
local socket = require "http.sockethelper"
local url = require "http.url"
local internal = require "http.internal"
local dns = require "skynet.dns"
local string = string
@@ -35,7 +34,7 @@ local function check_protocol(host)
end
local SSLCTX_CLIENT = nil
local function gen_interface(protocol, fd)
local function gen_interface(protocol, fd, hostname)
if protocol == "http" then
return {
init = nil,
@@ -49,7 +48,7 @@ local function gen_interface(protocol, fd)
elseif protocol == "https" then
local tls = require "http.tlshelper"
SSLCTX_CLIENT = SSLCTX_CLIENT or tls.newctx()
local tls_ctx = tls.newtls("client", SSLCTX_CLIENT)
local tls_ctx = tls.newtls("client", SSLCTX_CLIENT, hostname)
return {
init = tls.init_requestfunc(fd, tls_ctx),
close = tls.closefunc(tls_ctx),
@@ -62,47 +61,57 @@ local function gen_interface(protocol, fd)
end
end
function httpc.request(method, host, url, recvheader, header, content)
local function connect(host, timeout)
local protocol
local timeout = httpc.timeout -- get httpc.timeout before any blocked api
protocol, host = check_protocol(host)
local hostname, port = host:match"([^:]+):?(%d*)$"
local hostaddr, port = host:match"([^:]+):?(%d*)$"
if port == "" then
port = protocol=="http" and 80 or protocol=="https" and 443
else
port = tonumber(port)
end
if async_dns and not hostname:match(".*%d+$") then
hostname = dns.resolve(hostname)
local hostname
if not hostaddr:match(".*%d+$") then
hostname = hostaddr
if async_dns then
hostaddr = dns.resolve(hostname)
end
end
local fd = socket.connect(hostname, port, timeout)
local fd = socket.connect(hostaddr, port, timeout)
if not fd then
error(string.format("%s connect error host:%s, port:%s, timeout:%s", protocol, hostname, port, timeout))
return
error(string.format("%s connect error host:%s, port:%s, timeout:%s", protocol, hostaddr, port, timeout))
end
-- print("protocol hostname port", protocol, hostname, port)
local interface = gen_interface(protocol, fd)
local finish
if timeout then
skynet.timeout(timeout, function()
if not finish then
socket.shutdown(fd) -- shutdown the socket fd, need close later.
if interface.close then
interface.close()
end
end
end)
end
local interface = gen_interface(protocol, fd, hostname)
if interface.init then
interface.init()
end
local ok , statuscode, body = pcall(internal.request, interface, method, host, url, recvheader, header, content)
finish = true
if timeout then
skynet.timeout(timeout, function()
if not interface.finish then
socket.shutdown(fd) -- shutdown the socket fd, need close later.
end
end)
end
return fd, interface, host
end
local function close_interface(interface, fd)
interface.finish = true
socket.close(fd)
if interface.close then
interface.close()
interface.close = nil
end
end
function httpc.request(method, hostname, url, recvheader, header, content)
local fd, interface, host = connect(hostname, httpc.timeout)
local ok , statuscode, body , header = pcall(internal.request, interface, method, host, url, recvheader, header, content)
if ok then
ok, body = pcall(internal.response, interface, statuscode, body, header)
end
close_interface(interface, fd)
if ok then
return statuscode, body
else
@@ -110,6 +119,34 @@ function httpc.request(method, host, url, recvheader, header, content)
end
end
function httpc.head(hostname, url, recvheader, header, content)
local fd, interface, host = connect(hostname, httpc.timeout)
local ok , statuscode = pcall(internal.request, interface, "HEAD", host, url, recvheader, header, content)
close_interface(interface, fd)
if ok then
return statuscode
else
error(statuscode)
end
end
function httpc.request_stream(method, hostname, url, recvheader, header, content)
local fd, interface, host = connect(hostname, httpc.timeout)
local ok , statuscode, body , header = pcall(internal.request, interface, method, host, url, recvheader, header, content)
interface.finish = true -- don't shutdown fd in timeout
local function close_fd()
close_interface(interface, fd)
end
if not ok then
close_fd()
error(statuscode)
end
-- todo: stream support timeout
local stream = internal.response_stream(interface, statuscode, body, header)
stream._onclose = close_fd
return stream
end
function httpc.get(...)
return httpc.request("GET", ...)
end

View File

@@ -1,6 +1,5 @@
local internal = require "http.internal"
local table = table
local string = string
local type = type

View File

@@ -1,6 +1,5 @@
local table = table
local type = type
local sockethelper = require "http.sockethelper"
local M = {}
@@ -142,9 +141,29 @@ function M.recvchunkedbody(readbytes, bodylimit, header, body)
return result, header
end
local function recvbody(interface, code, header, body)
local length = header["content-length"]
if length then
length = tonumber(length)
end
if length then
if #body >= length then
body = body:sub(1,length)
else
local padding = interface.read(length - #body)
body = body .. padding
end
elseif code == 204 or code == 304 or code < 200 then
body = ""
-- See https://stackoverflow.com/questions/15991173/is-the-content-length-header-required-for-a-http-1-0-response
else
-- no content-length, read all
body = body .. interface.readall()
end
return body
end
function M.request(interface, method, host, url, recvheader, header, content)
local is_ws = interface.websocket
local read = interface.read
local write = interface.write
local header_content = ""
@@ -160,18 +179,18 @@ function M.request(interface, method, host, url, recvheader, header, content)
end
if content then
local data = string.format("%s %s HTTP/1.1\r\n%scontent-length:%d\r\n\r\n", method, url, header_content, #content)
local data = string.format("%s %s HTTP/1.1\r\n%sContent-length:%d\r\n\r\n", method, url, header_content, #content)
write(data)
write(content)
else
local request_header = string.format("%s %s HTTP/1.1\r\n%scontent-length:0\r\n\r\n", method, url, header_content)
local request_header = string.format("%s %s HTTP/1.1\r\n%sContent-length:0\r\n\r\n", method, url, header_content)
write(request_header)
end
local tmpline = {}
local body = M.recvheader(read, tmpline, "")
if not body then
error(sockethelper.socket_error)
error("Recv header failed")
end
local statusline = tmpline[1]
@@ -182,11 +201,10 @@ function M.request(interface, method, host, url, recvheader, header, content)
if not header then
error("Invalid HTTP response header")
end
return code, body, header
end
local length = header["content-length"]
if length then
length = tonumber(length)
end
function M.response(interface, code, body, header)
local mode = header["transfer-encoding"]
if mode then
if mode ~= "identity" and mode ~= "chunked" then
@@ -195,32 +213,172 @@ function M.request(interface, method, host, url, recvheader, header, content)
end
if mode == "chunked" then
body, header = M.recvchunkedbody(read, nil, header, body)
body, header = M.recvchunkedbody(interface.read, nil, header, body)
if not body then
error("Invalid response body")
end
else
-- identity mode
if length then
if #body >= length then
body = body:sub(1,length)
body = recvbody(interface, code, header, body)
end
return body
end
local stream = {}; stream.__index = stream
function stream:close()
if self._onclose then
self._onclose(self)
self._onclose = nil
end
end
function stream:padding()
return self._reading(self), self
end
stream.__close = stream.close
stream.__call = stream.padding
local function stream_nobody(stream)
stream._reading = stream.close
stream.connected = nil
return ""
end
local function stream_length(length)
return function(stream)
local body = stream._body
if body == nil then
local ret, padding = stream._interface.read()
if not ret then
-- disconnected
body = padding
stream.connected = false
else
local padding = read(length - #body)
body = body .. padding
body = ret
end
elseif code == 204 or code == 304 or code < 200 then
body = ""
-- See https://stackoverflow.com/questions/15991173/is-the-content-length-header-required-for-a-http-1-0-response
elseif is_ws and code == 101 then
-- if websocket handshake success
return code, body
end
local n = #body
if n >= length then
stream._reading = stream.close
stream.connected = nil
return (body:sub(1,length))
else
-- no content-length, read all
body = body .. interface.readall()
length = length - n
stream._body = nil
if not stream.connected then
stream._reading = stream.close
end
return body
end
end
end
local function stream_read(stream)
local ret, padding = stream._interface.read()
if ret == "" or not ret then
stream.connected = nil
stream:close()
if padding == "" then
return
end
return padding
end
return ret
end
local function stream_all(stream)
local body = stream._body
stream._body = nil
stream._reading = stream_read
return body
end
local function stream_chunked(stream)
local read = stream._interface.read
local sz, body = chunksize(read, stream._body)
if not sz then
stream.connected = false
stream:close()
return
end
if sz == 0 then
-- last chunk
local tmpline = {}
body = M.recvheader(read, tmpline, body)
if not body then
stream.connected = false
stream:close()
return
end
M.parseheader(tmpline,1, stream.header)
stream._reading = stream.close
stream.connected = nil
return ""
end
local n = #body
local remain
if n >= sz then
remain = body:sub(sz+1)
body = body:sub(1,sz)
else
body = body .. read(sz - n)
remain = ""
end
remain = readcrln(read, remain)
if not remain then
stream.connected = false
stream:close()
return
end
stream._body = remain
return body
end
function M.response_stream(interface, code, body, header)
local mode = header["transfer-encoding"]
if mode then
if mode ~= "identity" and mode ~= "chunked" then
error ("Unsupport transfer-encoding")
end
end
return code, body
local read_func
if mode == "chunked" then
read_func = stream_chunked
else
-- identity mode
local length = header["content-length"]
if length then
length = tonumber(length)
end
if length then
read_func = stream_length(length)
elseif code == 204 or code == 304 or code < 200 then
read_func = stream_nobody
else
read_func = stream_all
end
end
-- todo: timeout
return setmetatable({
status = code,
_body = body,
_interface = interface,
_reading = read_func,
header = header,
connected = true,
}, stream)
end
return M

View File

@@ -43,13 +43,16 @@ function tlshelper.closefunc(tls_ctx)
end
function tlshelper.readfunc(fd, tls_ctx)
local readfunc = socket.readfunc(fd)
local function readfunc()
readfunc = socket.readfunc(fd)
return ""
end
local read_buff = ""
return function (sz)
if not sz then
local s = ""
if #read_buff == 0 then
local ds = readfunc(sz)
local ds = readfunc()
s = tls_ctx:read(ds)
end
s = read_buff .. s
@@ -77,7 +80,6 @@ function tlshelper.writefunc(fd, tls_ctx)
end
function tlshelper.readallfunc(fd, tls_ctx)
local readfunc = socket.readfunc(fd)
return function ()
local ds = socket.readall(fd)
local s = tls_ctx:read(ds)
@@ -89,8 +91,8 @@ function tlshelper.newctx()
return c.newctx()
end
function tlshelper.newtls(method, ssl_ctx)
return c.newtls(method, ssl_ctx)
function tlshelper.newtls(method, ssl_ctx, hostname)
return c.newtls(method, ssl_ctx, hostname)
end
return tlshelper

View File

@@ -43,8 +43,9 @@ local function write_handshake(self, host, url, header)
local recvheader = {}
local code, body = internal.request(self, "GET", host, url, recvheader, request_header)
if code ~= 101 then
error(string.format("websocket handshake error: code[%s] info:%s", code, body))
error(string.format("websocket handshake error: code[%s] info:%s", code, body))
end
assert(body == "") -- todo: M.read may need handle it
if not recvheader["upgrade"] or recvheader["upgrade"]:lower() ~= "websocket" then
error("websocket handshake upgrade must websocket")
@@ -67,26 +68,32 @@ local function write_handshake(self, host, url, header)
end
local function read_handshake(self)
local tmpline = {}
local header_body = internal.recvheader(self.read, tmpline, "")
if not header_body then
return 413
local function read_handshake(self, upgrade_ops)
local header, method, url
if upgrade_ops then
header, method, url = upgrade_ops.header, upgrade_ops.method, upgrade_ops.url
else
local tmpline = {}
local header_body = internal.recvheader(self.read, tmpline, "")
if not header_body then
return 413
end
local request = assert(tmpline[1])
local httpver
method, url, httpver = request:match "^(%a+)%s+(.-)%s+HTTP/([%d%.]+)$"
assert(method and url and httpver)
if method ~= "GET" then
return 400, "need GET method"
end
httpver = assert(tonumber(httpver))
if httpver < 1.1 then
return 505 -- HTTP Version not supported
end
header = internal.parseheader(tmpline, 2, {})
end
local request = assert(tmpline[1])
local method, url, httpver = request:match "^(%a+)%s+(.-)%s+HTTP/([%d%.]+)$"
assert(method and url and httpver)
if method ~= "GET" then
return 400, "need GET method"
end
httpver = assert(tonumber(httpver))
if httpver < 1.1 then
return 505 -- HTTP Version not supported
end
local header = internal.parseheader(tmpline, 2, {})
if not header then
return 400 -- Bad request
end
@@ -239,9 +246,9 @@ local function read_frame(self)
end
local function resolve_accept(self)
local function resolve_accept(self, options)
try_handle(self, "connect")
local code, err, url = read_handshake(self)
local code, err, url = read_handshake(self, options and options.upgrade)
if code then
local ok, s = httpd.write_response(self.write, code, err)
if not ok then
@@ -255,6 +262,7 @@ local function resolve_accept(self)
try_handle(self, "handshake", header, url)
local recv_count = 0
local recv_buf = {}
local first_op
while true do
if _isws_closed(self.id) then
try_handle(self, "close")
@@ -280,11 +288,13 @@ local function resolve_accept(self)
if recv_count > MAX_FRAME_SIZE then
error("payload_len is too large")
end
first_op = first_op or op
if fin then
local s = table.concat(recv_buf)
try_handle(self, "message", s, op)
try_handle(self, "message", s, first_op)
recv_buf = {} -- clear recv_buf
recv_count = 0
first_op = nil
end
end
end
@@ -293,11 +303,10 @@ end
local SSLCTX_CLIENT = nil
local function _new_client_ws(socket_id, protocol)
local function _new_client_ws(socket_id, protocol, hostname)
local obj
if protocol == "ws" then
obj = {
websocket = true,
close = function ()
socket.close(socket_id)
end,
@@ -310,14 +319,13 @@ local function _new_client_ws(socket_id, protocol)
elseif protocol == "wss" then
local tls = require "http.tlshelper"
SSLCTX_CLIENT = SSLCTX_CLIENT or tls.newctx()
local tls_ctx = tls.newtls("client", SSLCTX_CLIENT)
local tls_ctx = tls.newtls("client", SSLCTX_CLIENT, hostname)
local init = tls.init_requestfunc(socket_id, tls_ctx)
init()
obj = {
websocket = true,
close = function ()
socket.close(socket_id)
tls.closefunc(tls_ctx)()
tls.closefunc(tls_ctx)()
end,
read = tls.readfunc(socket_id, tls_ctx),
write = tls.writefunc(socket_id, tls_ctx),
@@ -363,7 +371,7 @@ local function _new_server_ws(socket_id, handle, protocol)
obj = {
close = function ()
socket.close(socket_id)
tls.closefunc(tls_ctx)()
tls.closefunc(tls_ctx)()
end,
read = tls.readfunc(socket_id, tls_ctx),
write = tls.writefunc(socket_id, tls_ctx),
@@ -384,8 +392,10 @@ end
-- handle interface
-- connect / handshake / message / ping / pong / close / error
function M.accept(socket_id, handle, protocol, addr)
socket.start(socket_id)
function M.accept(socket_id, handle, protocol, addr, options)
if not (options and options.upgrade) then
socket.start(socket_id)
end
protocol = protocol or "ws"
local ws_obj = _new_server_ws(socket_id, handle, protocol)
ws_obj.addr = addr
@@ -396,7 +406,7 @@ function M.accept(socket_id, handle, protocol, addr)
end)
end
local ok, err = xpcall(resolve_accept, debug.traceback, ws_obj)
local ok, err = xpcall(resolve_accept, debug.traceback, ws_obj, options)
local closed = _isws_closed(socket_id)
if not closed then
_close_websocket(ws_obj)
@@ -422,19 +432,23 @@ function M.connect(url, header, timeout)
if protocol ~= "wss" and protocol ~= "ws" then
error(string.format("invalid protocol: %s", protocol))
end
assert(host)
local host_name, host_port = string.match(host, "^([^:]+):?(%d*)$")
assert(host_name and host_port)
local host_addr, host_port = string.match(host, "^([^:]+):?(%d*)$")
assert(host_addr and host_port)
if host_port == "" then
host_port = protocol == "ws" and 80 or 443
end
local hostname
if not host_addr:match(".*%d+$") then
hostname = host_addr
end
uri = uri == "" and "/" or uri
local socket_id = sockethelper.connect(host_name, host_port, timeout)
local ws_obj = _new_client_ws(socket_id, protocol)
local socket_id = sockethelper.connect(host_addr, host_port, timeout)
local ws_obj = _new_client_ws(socket_id, protocol, hostname)
ws_obj.addr = host
write_handshake(ws_obj, host_name, uri, header)
write_handshake(ws_obj, host_addr, uri, header)
return socket_id
end
@@ -462,7 +476,6 @@ function M.read(id)
end
end
end
assert(false)
end

View File

@@ -45,4 +45,6 @@ if LUA_PRELOAD then
LUA_PRELOAD = nil
end
_G.require = (require "skynet.require").require
main(select(2, table.unpack(args)))

View File

@@ -1,5 +1,6 @@
-- read https://github.com/cloudwu/skynet/wiki/FAQ for the module "skynet.core"
local c = require "skynet.core"
local skynet_require = require "skynet.require"
local tostring = tostring
local coroutine = coroutine
local assert = assert
@@ -65,7 +66,7 @@ local sleep_session = {}
local watching_session = {}
local error_queue = {}
local fork_queue = {}
local fork_queue = { h = 1, t = 0 }
do ---- request/select
local function send_requests(self)
@@ -412,7 +413,7 @@ end
function skynet.wait(token)
local session = c.genid()
token = token or coroutine.running()
local ret, msg = suspend_sleep(session, token)
suspend_sleep(session, token)
sleep_session[token] = nil
session_id_coroutine[session] = nil
end
@@ -429,9 +430,12 @@ function skynet.killthread(thread)
end
end
else
for i = 1, #fork_queue do
local t = fork_queue.t
for i = fork_queue.h, t do
if fork_queue[i] == thread then
tremove(fork_queue, i)
table.move(fork_queue, i+1, t, i)
fork_queue[t] = nil
fork_queue.t = t - 1
return thread
end
end
@@ -446,7 +450,6 @@ function skynet.killthread(thread)
if co == nil then
return
end
watching_session[session] = nil
local addr = session_coroutine_address[co]
if addr then
session_coroutine_address[co] = nil
@@ -517,7 +520,7 @@ function skynet.time()
end
function skynet.exit()
fork_queue = {} -- no fork coroutine can be execute after skynet.exit
fork_queue = { h = 1, t = 0 } -- no fork coroutine can be execute after skynet.exit
skynet.send(".launcher","lua","REMOVE",skynet.self(), false)
-- report the sources that call me
for co, session in pairs(session_coroutine_id) do
@@ -527,7 +530,7 @@ function skynet.exit()
end
end
for session, co in pairs(session_id_coroutine) do
if type(co) == "thread" then
if type(co) == "thread" and co ~= running_thread then
coroutine.close(co)
end
end
@@ -630,6 +633,9 @@ function skynet.ret(msg, sz)
local tag = session_coroutine_tracetag[running_thread]
if tag then c.trace(tag, "response") end
local co_session = session_coroutine_id[running_thread]
if co_session == nil then
error "No session"
end
session_coroutine_id[running_thread] = nil
if co_session == 0 then
if sz ~= nil then
@@ -638,9 +644,6 @@ function skynet.ret(msg, sz)
return false -- send don't need ret
end
local co_address = session_coroutine_address[running_thread]
if not co_session then
error "No session"
end
local ret = c.send(co_address, skynet.PTYPE_RESPONSE, co_session, msg, sz)
if ret then
return true
@@ -757,7 +760,9 @@ function skynet.fork(func,...)
local args = { ... }
co = co_create(function() func(table.unpack(args,1,n)) end)
end
tinsert(fork_queue, co)
local t = fork_queue.t + 1
fork_queue.t = t
fork_queue[t] = co
return co
end
@@ -828,10 +833,18 @@ end
function skynet.dispatch_message(...)
local succ, err = pcall(raw_dispatch_message,...)
while true do
local co = tremove(fork_queue,1)
if co == nil then
if fork_queue.h > fork_queue.t then
-- queue is empty
fork_queue.h = 1
fork_queue.t = 0
break
end
-- pop queue
local h = fork_queue.h
local co = fork_queue[h]
fork_queue[h] = nil
fork_queue.h = h + 1
local fork_succ, fork_err = pcall(suspend,co,coroutine_resume(co))
if not fork_succ then
if succ then
@@ -912,49 +925,16 @@ do
}
end
local init_func = {}
function skynet.init(f, name)
assert(type(f) == "function")
if init_func == nil then
f()
else
tinsert(init_func, f)
if name then
assert(type(name) == "string")
assert(init_func[name] == nil)
init_func[name] = f
end
end
end
local function init_all()
local funcs = init_func
init_func = nil
if funcs then
for _,f in ipairs(funcs) do
f()
end
end
end
local function ret(f, ...)
f()
return ...
end
local function init_template(start, ...)
init_all()
init_func = {}
return ret(init_all, start(...))
end
function skynet.pcall(start, ...)
return xpcall(init_template, traceback, start, ...)
end
skynet.init = skynet_require.init
-- skynet.pcall is deprecated, use pcall directly
skynet.pcall = pcall
function skynet.init_service(start)
local ok, err = skynet.pcall(start)
local function main()
skynet_require.init_all()
start()
end
local ok, err = xpcall(main, traceback)
if not ok then
skynet.error("init service failed: " .. tostring(err))
skynet.send(".launcher","lua", "ERROR")
@@ -988,7 +968,9 @@ function skynet.task(ret)
if ret == nil then
local t = 0
for session,co in pairs(session_id_coroutine) do
t = t + 1
if co ~= "BREAK" then
t = t + 1
end
end
return t
end
@@ -1003,7 +985,9 @@ function skynet.task(ret)
if tt == "table" then
for session,co in pairs(session_id_coroutine) do
local key = string.format("%s session: %d", tostring(co), session)
if timeout_traceback and timeout_traceback[co] then
if co == "BREAK" then
ret[key] = "BREAK"
elseif timeout_traceback and timeout_traceback[co] then
ret[key] = timeout_traceback[co]
else
ret[key] = traceback(co)
@@ -1013,7 +997,11 @@ function skynet.task(ret)
elseif tt == "number" then
local co = session_id_coroutine[ret]
if co then
return traceback(co)
if co == "BREAK" then
return "BREAK"
else
return traceback(co)
end
else
return "No session"
end

View File

@@ -5,6 +5,10 @@ local cluster = {}
local sender = {}
local task_queue = {}
local function repack(address, ...)
return address, skynet.pack(...)
end
local function request_sender(q, node)
local ok, c = pcall(skynet.call, clusterd, "lua", "sender", node)
if not ok then
@@ -16,9 +20,9 @@ local function request_sender(q, node)
q.confirm = confirm
q.sender = c
for _, task in ipairs(q) do
if type(task) == "table" then
if type(task) == "string" then
if c then
skynet.send(c, "lua", "push", task[1], skynet.pack(table.unpack(task,2,task.n)))
skynet.send(c, "lua", "push", repack(skynet.unpack(task)))
end
else
skynet.wakeup(task)
@@ -53,14 +57,19 @@ end
function cluster.call(node, address, ...)
-- skynet.pack(...) will free by cluster.core.packrequest
return skynet.call(get_sender(node), "lua", "req", address, skynet.pack(...))
local s = sender[node]
if not s then
local task = skynet.packstring(address, ...)
return skynet.call(get_sender(node), "lua", "req", repack(skynet.unpack(task)))
end
return skynet.call(s, "lua", "req", address, skynet.pack(...))
end
function cluster.send(node, address, ...)
-- push is the same with req, but no response
local s = sender[node]
if not s then
table.insert(task_queue[node], table.pack(address, ...))
table.insert(task_queue[node], skynet.packstring(address, ...))
else
skynet.send(sender[node], "lua", "push", address, skynet.pack(...))
end

View File

@@ -6,6 +6,7 @@ local coroutine_resume = coroutine.resume
local coroutine_yield = coroutine.yield
local coroutine_status = coroutine.status
local coroutine_running = coroutine.running
local coroutine_close = coroutine.close
local select = select
local skynetco = {}
@@ -15,6 +16,9 @@ skynetco.running = coroutine.running
skynetco.status = coroutine.status
local skynet_coroutines = setmetatable({}, { __mode = "kv" })
-- true : skynet coroutine
-- false : skynet suspend
-- nil : exit
function skynetco.create(f)
local co = coroutine.create(f)
@@ -84,7 +88,7 @@ do -- begin skynetco.resume
end -- end of skynetco.resume
function skynetco.status(co)
local status = coroutine.status(co)
local status = coroutine_status(co)
if status == "suspended" then
if skynet_coroutines[co] == false then
return "blocked"
@@ -121,4 +125,9 @@ end
end -- end of skynetco.wrap
function skynetco.close(co)
skynet_coroutines[co] = nil
return coroutine_close(co)
end
return skynetco

View File

@@ -365,6 +365,18 @@ function mongo_collection:batch_insert(docs)
sock:request(pack)
end
function mongo_collection:safe_batch_insert(docs)
for i = 1, #docs do
if docs[i]._id == nil then
docs[i]._id = bson.objectid()
end
docs[i] = bson_encode(docs[i])
end
local r = self.database:runCommand("insert", self.name, "documents", docs)
return werror(r)
end
function mongo_collection:update(selector,update,upsert,multi)
local flags = (upsert and 1 or 0) + (multi and 2 or 0)
local sock = self.connection.__sock

View File

@@ -862,6 +862,7 @@ local _binary_parser = {
[0x0c] = _get_datetime,
[0x0f] = _from_length_coded_str,
[0x10] = _from_length_coded_str,
[0xf5] = _from_length_coded_str,
[0xf9] = _from_length_coded_str,
[0xfa] = _from_length_coded_str,
[0xfb] = _from_length_coded_str,

View File

@@ -1,10 +1,16 @@
local skynet = require "skynet"
local socket = require "skynet.socket"
local socketchannel = require "skynet.socketchannel"
local tostring = tostring
local tonumber = tonumber
local table = table
local string = string
local assert = assert
local setmetatable = setmetatable
local ipairs = ipairs
local type = type
local select = select
local pairs = pairs
local redis = {}
local command = {}
@@ -96,19 +102,36 @@ local count_cache = make_cache(function(t,k)
return s
end)
local command_np_cache = make_cache(function(t, cmd)
local s = "*1" .. command_cache[cmd] .. "\r\n"
t[cmd] = s
return s
end)
local function compose_message(cmd, msg)
if msg == nil then
return command_np_cache[cmd]
end
local t = type(msg)
local lines = {}
if t == "table" then
lines[1] = count_cache[#msg+1]
local n = msg.n or #msg
lines[1] = count_cache[n+1]
lines[2] = command_cache[cmd]
local idx = 3
for _,v in ipairs(msg) do
v= tostring(v)
lines[idx] = header_cache[#v]
lines[idx+1] = v
idx = idx + 2
for i = 1, n do
local v = msg[i]
if v == nil then
lines[idx] = "\r\n$-1"
idx = idx + 1
else
v= tostring(v)
lines[idx] = header_cache[#v]
lines[idx+1] = v
idx = idx + 2
end
end
lines[idx] = "\r\n"
else
@@ -123,10 +146,17 @@ local function compose_message(cmd, msg)
return lines
end
local function redis_login(auth, db)
local function redis_login(conf)
local auth = conf.auth
local db = conf.db
if auth == nil and db == nil then
return
end
if auth then
if conf.username then
auth = { conf.username, auth }
end
end
return function(so)
if auth then
so:request(compose_message("AUTH", auth), read_response)
@@ -141,7 +171,7 @@ function redis.connect(db_conf)
local channel = socketchannel.channel {
host = db_conf.host,
port = db_conf.port or 6379,
auth = redis_login(db_conf.auth, db_conf.db),
auth = redis_login(db_conf),
nodelay = true,
overload = db_conf.overload,
}
@@ -153,10 +183,12 @@ end
setmetatable(command, { __index = function(t,k)
local cmd = string.upper(k)
local f = function (self, v, ...)
if type(v) == "table" then
if v == nil then
return self[1]:request(compose_message(cmd), read_response)
elseif type(v) == "table" then
return self[1]:request(compose_message(cmd, v), read_response)
else
return self[1]:request(compose_message(cmd, {v, ...}), read_response)
return self[1]:request(compose_message(cmd, table.pack(v, ...)), read_response)
end
end
t[k] = f
@@ -175,7 +207,7 @@ end
function command:sismember(key, value)
local fd = self[1]
return fd:request(compose_message ("SISMEMBER", {key, value}), read_boolean)
return fd:request(compose_message ("SISMEMBER", table.pack(key, value)), read_boolean)
end
local function compose_table(lines, msg)
@@ -202,7 +234,7 @@ function command:pipeline(ops,resp)
if resp then
return fd:request(cmds, function (fd)
for i=1, #ops do
for _=1, #ops do
local ok, out = read_response(fd)
table.insert(resp, {ok = ok, out = out})
end
@@ -211,7 +243,7 @@ function command:pipeline(ops,resp)
else
return fd:request(cmds, function (fd)
local ok, out
for i=1, #ops do
for _=1, #ops do
ok, out = read_response(fd)
end
-- return last response
@@ -231,10 +263,11 @@ local watchmeta = {
end,
}
local function watch_login(obj, auth)
local function watch_login(conf, obj)
local login_auth = redis_login(conf)
return function(so)
if auth then
so:request(compose_message("AUTH", auth), read_response)
if login_auth then
login_auth(so)
end
for k in pairs(obj.__psubscribe) do
so:request(compose_message ("PSUBSCRIBE", k))
@@ -253,7 +286,7 @@ function redis.watch(db_conf)
local channel = socketchannel.channel {
host = db_conf.host,
port = db_conf.port or 6379,
auth = watch_login(obj, db_conf.auth),
auth = watch_login(db_conf, obj),
nodelay = true,
}
obj.__sock = channel
@@ -288,18 +321,18 @@ function watch:message()
local so = self.__sock
while true do
local ret = so:response(read_response)
local type , channel, data , data2 = ret[1], ret[2], ret[3], ret[4]
if type == "message" then
local ttype , channel, data , data2 = ret[1], ret[2], ret[3], ret[4]
if ttype == "message" then
return data, channel
elseif type == "pmessage" then
elseif ttype == "pmessage" then
return data2, data, channel
elseif type == "subscribe" then
elseif ttype == "subscribe" then
self.__subscribe[channel] = true
elseif type == "psubscribe" then
elseif ttype == "psubscribe" then
self.__psubscribe[channel] = true
elseif type == "unsubscribe" then
elseif ttype == "unsubscribe" then
self.__subscribe[channel] = nil
elseif type == "punsubscribe" then
elseif ttype == "punsubscribe" then
self.__psubscribe[channel] = nil
end
end

View File

@@ -1,17 +1,30 @@
local skynet = require "skynet"
local c = require "skynet.core"
local function number_address(name)
local t = type(name)
if t == "number" then
return name
elseif t == "string" then
local hex = name:match "^:(%x+)"
if hex then
return tonumber(hex, 16)
end
end
end
function skynet.launch(...)
local addr = c.command("LAUNCH", table.concat({...}," "))
if addr then
return tonumber("0x" .. string.sub(addr , 2))
return tonumber(string.sub(addr , 2), 16)
end
end
function skynet.kill(name)
if type(name) == "number" then
skynet.send(".launcher","lua","REMOVE",name, true)
name = skynet.address(name)
local addr = number_address(name)
if addr then
skynet.send(".launcher","lua","REMOVE", addr, true)
name = skynet.address(addr)
end
c.command("KILL",name)
end
@@ -27,7 +40,7 @@ local function globalname(name, handle)
return false
end
assert(#name <= 16) -- GLOBALNAME_LENGTH is 16, defined in skynet_harbor.h
assert(#name < 16) -- GLOBALNAME_LENGTH is 16, defined in skynet_harbor.h
assert(tonumber(name) == nil) -- global name can't be number
local harbor = require "skynet.harbor"

View File

@@ -95,6 +95,6 @@ local function init()
}
end
skynet.init(init, "multicast")
skynet.init(init)
return multicast

114
lualib/skynet/require.lua Normal file
View File

@@ -0,0 +1,114 @@
-- skynet module two-step initialize . When you require a skynet module :
-- 1. Run module main function as official lua module behavior.
-- 2. Run the functions register by skynet.init() during the step 1,
-- unless calling `require` in main thread .
-- If you call `require` in main thread ( service main function ), the functions
-- registered by skynet.init() do not execute immediately, they will be executed
-- by skynet.start() before start function.
local M = {}
local mainthread, ismain = coroutine.running()
assert(ismain, "skynet.require must initialize in main thread")
local context = {
[mainthread] = {},
}
do
local require = _G.require
local loaded = package.loaded
local loading = {}
function M.require(name)
local m = loaded[name]
if m ~= nil then
return m
end
local co, main = coroutine.running()
if main then
return require(name)
end
local filename = package.searchpath(name, package.path)
if not filename then
return require(name)
end
local modfunc = loadfile(filename)
if not modfunc then
return require(name)
end
local loading_queue = loading[name]
if loading_queue then
assert(loading_queue.co ~= co, "circular dependency")
-- Module is in the init process (require the same mod at the same time in different coroutines) , waiting.
local skynet = require "skynet"
loading_queue[#loading_queue+1] = co
skynet.wait(co)
local m = loaded[name]
if m == nil then
error(string.format("require %s failed", name))
end
return m
end
loading_queue = {co = co}
loading[name] = loading_queue
local old_init_list = context[co]
local init_list = {}
context[co] = init_list
-- We should call modfunc in lua, because modfunc may yield by calling M.require recursive.
local function execute_module()
local m = modfunc(name, filename)
for _, f in ipairs(init_list) do
f()
end
if m == nil then
m = true
end
loaded[name] = m
end
local ok, err = xpcall(execute_module, debug.traceback)
context[co] = old_init_list
local waiting = #loading_queue
if waiting > 0 then
local skynet = require "skynet"
for i = 1, waiting do
skynet.wakeup(loading_queue[i])
end
end
loading[name] = nil
if ok then
return loaded[name]
else
error(err)
end
end
end
function M.init_all()
for _, f in ipairs(context[mainthread]) do
f()
end
context[mainthread] = nil
end
function M.init(f)
assert(type(f) == "function")
local co = coroutine.running()
table.insert(context[co], f)
end
return M

View File

@@ -16,6 +16,7 @@ local socket_pool = setmetatable( -- store all socket object
}
)
local socket_onclose = {}
local socket_message = {}
local function wakeup(s)
@@ -27,7 +28,7 @@ local function wakeup(s)
end
local function pause_socket(s, size)
if s.pause then
if s.pause ~= nil then
return
end
if size then
@@ -117,11 +118,17 @@ end
-- SKYNET_SOCKET_TYPE_CLOSE = 3
socket_message[3] = function(id)
local s = socket_pool[id]
if s == nil then
return
if s then
s.connected = false
wakeup(s)
else
driver.close(id)
end
local cb = socket_onclose[id]
if cb then
cb(id)
socket_onclose[id] = nil
end
s.connected = false
wakeup(s)
end
-- SKYNET_SOCKET_TYPE_ACCEPT = 4
@@ -138,6 +145,7 @@ end
socket_message[5] = function(id, _, err)
local s = socket_pool[id]
if s == nil then
driver.shutdown(id)
skynet.error("socket: error on unknown", id, err)
return
end
@@ -211,6 +219,7 @@ local function connect(id, func)
callback = func,
protocol = "TCP",
}
assert(not socket_onclose[id], "socket has onclose callback")
assert(not socket_pool[id], "socket is not closed")
socket_pool[id] = s
suspend(s)
@@ -245,7 +254,7 @@ end
function socket.pause(id)
local s = socket_pool[id]
if s == nil or s.pause then
if s == nil then
return
end
pause_socket(s)
@@ -269,8 +278,9 @@ function socket.close(id)
if s == nil then
return
end
driver.close(id)
if s.connected then
driver.close(id)
s.pause = false -- Do not resume this fd if it paused.
if s.co then
-- reading this socket on another coroutine, so don't shutdown (clear the buffer) immediately
-- wait reading coroutine read the buffer.
@@ -282,7 +292,6 @@ function socket.close(id)
end
s.connected = false
end
assert(s.lock == nil or next(s.lock) == nil)
socket_pool[id] = nil
end
@@ -398,34 +407,6 @@ function socket.listen(host, port, backlog)
return driver.listen(host, port, backlog)
end
function socket.lock(id)
local s = socket_pool[id]
assert(s)
local lock_set = s.lock
if not lock_set then
lock_set = {}
s.lock = lock_set
end
if #lock_set == 0 then
lock_set[1] = true
else
local co = coroutine.running()
table.insert(lock_set, co)
skynet.wait(co)
end
end
function socket.unlock(id)
local s = socket_pool[id]
assert(s)
local lock_set = assert(s.lock)
table.remove(lock_set,1)
local co = lock_set[1]
if co then
skynet.wakeup(co)
end
end
-- abandon use to forward socket id to other service
-- you must call socket.start(id) later in other service
function socket.abandon(id)
@@ -433,6 +414,7 @@ function socket.abandon(id)
if s then
s.connected = false
wakeup(s)
socket_onclose[id] = nil
socket_pool[id] = nil
end
end
@@ -476,6 +458,7 @@ end
socket.sendto = assert(driver.udp_send)
socket.udp_address = assert(driver.udp_address)
socket.netstat = assert(driver.info)
socket.resolve = assert(driver.resolve)
function socket.warning(id, callback)
local obj = socket_pool[id]
@@ -483,4 +466,8 @@ function socket.warning(id, callback)
obj.on_warning = callback
end
function socket.onclose(id, callback)
socket_onclose[id] = callback
end
return socket

View File

@@ -128,6 +128,16 @@ local function pop_response(self)
end
end
-- on close callback
local function autoclose_cb(self, fd)
local sock = self.__sock
if self.__wait_response and sock and sock[1] == fd then
-- closed by peer
skynet.error("socket closed by peer : ", self.__host, self.__port)
close_channel_socket(self)
end
end
local function push_response(self, response, co)
if self.__response then
-- response is session
@@ -170,7 +180,15 @@ local function dispatch_by_order(self)
wakeup_all(self, "channel_closed")
break
end
local ok, result_ok, result_data = pcall(get_response, func, self.__sock)
local sock = self.__sock
if not sock then
-- closed by peer
self.__result[co] = socket_error
skynet.wakeup(co)
wakeup_all(self)
break
end
local ok, result_ok, result_data = pcall(get_response, func, sock)
if ok then
self.__result[co] = result_ok
if result_ok and self.__result_data[co] then
@@ -197,6 +215,9 @@ local function dispatch_function(self)
if self.__response then
return dispatch_by_session
else
socket.onclose(self.__sock[1], function(fd)
autoclose_cb(self, fd)
end)
return dispatch_by_order
end
end
@@ -402,12 +423,12 @@ local function block_connect(self, once)
else
self.__connecting[1] = true
err = try_connect(self, once)
self.__connecting[1] = nil
for i=2, #self.__connecting do
local co = self.__connecting[i]
self.__connecting[i] = nil
skynet.wakeup(co)
end
self.__connecting[1] = nil
end
r = check_connection(self)

View File

@@ -12,6 +12,9 @@ local CMD = setmetatable({}, { __gc = function() netpack.clear(queue) end })
local nodelay = false
local connection = {}
-- true : connected
-- nil : closed
-- false : close read
function gateserver.openclient(fd)
if connection[fd] then
@@ -21,8 +24,8 @@ end
function gateserver.closeclient(fd)
local c = connection[fd]
if c then
connection[fd] = false
if c ~= nil then
connection[fd] = nil
socketdriver.close(fd)
end
end
@@ -80,7 +83,7 @@ function gateserver.start(handler)
function MSG.open(fd, msg)
if client_number >= maxclient then
socketdriver.close(fd)
socketdriver.shutdown(fd)
return
end
if nodelay then
@@ -91,20 +94,15 @@ function gateserver.start(handler)
handler.connect(fd, msg)
end
local function close_fd(fd)
local c = connection[fd]
if c ~= nil then
connection[fd] = nil
client_number = client_number - 1
end
end
function MSG.close(fd)
if fd ~= socket then
client_number = client_number - 1
if connection[fd] then
connection[fd] = false -- close read
end
if handler.disconnect then
handler.disconnect(fd)
end
close_fd(fd)
else
socket = nil
end
@@ -112,12 +110,12 @@ function gateserver.start(handler)
function MSG.error(fd, msg)
if fd == socket then
skynet.error("gateserver accpet error:",msg)
skynet.error("gateserver accept error:",msg)
else
socketdriver.shutdown(fd)
if handler.error then
handler.error(fd, msg)
end
close_fd(fd)
end
end
@@ -141,7 +139,7 @@ function gateserver.start(handler)
end
}
skynet.start(function()
local function init()
skynet.dispatch("lua", function (_, address, cmd, ...)
local f = CMD[cmd]
if f then
@@ -150,7 +148,13 @@ function gateserver.start(handler)
skynet.ret(skynet.pack(handler.command(cmd, address, ...)))
end
end)
end)
end
if handler.embed then
init()
else
skynet.start(init)
end
end
return gateserver

View File

@@ -348,6 +348,7 @@ local function packtype(name, t, alltypes)
else
tmp.type = nil
end
tmp.map = nil
if f.key then
assert(f.array)
if f.key == "" then

View File

@@ -62,7 +62,7 @@ _return_message(struct databuffer *db, struct messagepool *mp) {
}
static void
databuffer_read(struct databuffer *db, struct messagepool *mp, void * buffer, int sz) {
databuffer_read(struct databuffer *db, struct messagepool *mp, char * buffer, int sz) {
assert(db->size >= sz);
db->size -= sz;
for (;;) {

View File

@@ -175,13 +175,13 @@ _forward(struct gate *g, struct connection * c, int size) {
}
if (g->broker) {
void * temp = skynet_malloc(size);
databuffer_read(&c->buffer,&g->mp,temp, size);
databuffer_read(&c->buffer,&g->mp,(char *)temp, size);
skynet_send(ctx, 0, g->broker, g->client_tag | PTYPE_TAG_DONTCOPY, fd, temp, size);
return;
}
if (c->agent) {
void * temp = skynet_malloc(size);
databuffer_read(&c->buffer,&g->mp,temp, size);
databuffer_read(&c->buffer,&g->mp,(char *)temp, size);
skynet_send(ctx, c->client, c->agent, g->client_tag | PTYPE_TAG_DONTCOPY, fd , temp, size);
} else if (g->watchdog) {
char * tmp = skynet_malloc(size + 32);

View File

@@ -38,8 +38,9 @@ static int
timestring(struct logger *inst, char tmp[SIZETIMEFMT]) {
uint64_t now = skynet_now();
time_t ti = now/100 + inst->starttime;
struct tm *info = localtime(&ti);
strftime(tmp, SIZETIMEFMT, "%D %T", info);
struct tm info;
(void)localtime_r(&ti,&info);
strftime(tmp, SIZETIMEFMT, "%d/%m/%y %H:%M:%S", &info);
return now % 100;
}

View File

@@ -30,7 +30,7 @@ struct snlua {
size_t mem_report;
size_t mem_limit;
lua_State * activeL;
volatile int trap;
ATOM_INT trap;
};
// LUA_CACHELIB may defined in patched lua for shared proto
@@ -67,8 +67,8 @@ signal_hook(lua_State *L, lua_Debug *ar) {
struct snlua *l = (struct snlua *)ud;
lua_sethook (L, NULL, 0, 0);
if (l->trap) {
l->trap = 0;
if (ATOM_LOAD(&l->trap)) {
ATOM_STORE(&l->trap , 0);
luaL_error(L, "signal 0");
}
}
@@ -76,7 +76,7 @@ signal_hook(lua_State *L, lua_Debug *ar) {
static void
switchL(lua_State *L, struct snlua *l) {
l->activeL = L;
if (l->trap) {
if (ATOM_LOAD(&l->trap)) {
lua_sethook(L, signal_hook, LUA_MASKCOUNT, 1);
}
}
@@ -88,9 +88,9 @@ lua_resumeX(lua_State *L, lua_State *from, int nargs, int *nresults) {
struct snlua *l = (struct snlua *)ud;
switchL(L, l);
int err = lua_resume(L, from, nargs, nresults);
if (l->trap) {
if (ATOM_LOAD(&l->trap)) {
// wait for lua_sethook. (l->trap == -1)
while (l->trap >= 0) ;
while (ATOM_LOAD(&l->trap) >= 0) ;
}
switchL(from, l);
return err;
@@ -195,7 +195,7 @@ timing_resume(lua_State *L, int co_index, int n) {
lua_rawset(L, lua_upvalueindex(1)); // set start time
}
int r = auxresume(L, co, lua_gettop(L) - 1);
int r = auxresume(L, co, n);
if (timing_enable(L, co_index, &start_time)) {
double total_time = timing_total(L, co_index);
@@ -506,7 +506,7 @@ snlua_create(void) {
l->mem_limit = 0;
l->L = lua_newstate(lalloc, l);
l->activeL = NULL;
l->trap = 0;
ATOM_INIT(&l->trap , 0);
return l;
}
@@ -520,13 +520,15 @@ void
snlua_signal(struct snlua *l, int signal) {
skynet_error(l->ctx, "recv a signal %d", signal);
if (signal == 0) {
if (l->trap == 0) {
if (ATOM_LOAD(&l->trap) == 0) {
int zero = 0;
// only one thread can set trap ( l->trap 0->1 )
if (!ATOM_CAS(&l->trap, 0, 1))
if (!ATOM_CAS(&l->trap, zero, 1))
return;
lua_sethook (l->activeL, signal_hook, LUA_MASKCOUNT, 1);
// finish set ( l->trap 1 -> -1 )
ATOM_CAS(&l->trap, 1, -1);
int one = 1;
ATOM_CAS(&l->trap, one, -1);
}
} else if (signal == 1) {
skynet_error(l->ctx, "Current Memory %.3fK", (float)l->mem / 1024);

View File

@@ -1,5 +1,6 @@
local skynet = require "skynet"
local harbor = require "skynet.harbor"
local service = require "skynet.service"
require "skynet.manager" -- import skynet.launch, ...
skynet.start(function()
@@ -39,6 +40,15 @@ skynet.start(function()
skynet.name("DATACENTER", datacenter)
end
skynet.newservice "service_mgr"
local enablessl = skynet.getenv "enablessl"
if enablessl then
service.new("ltls_holder", function ()
local c = require "ltls.init.c"
c.constructor()
end)
end
pcall(skynet.newservice,skynet.getenv "start" or "main")
skynet.exit()
end)

View File

@@ -15,9 +15,15 @@ local function open_channel(t, key)
local ct = connecting[key]
if ct then
local co = coroutine.running()
table.insert(ct, co)
skynet.wait(co)
return assert(ct.channel)
local channel
while ct do
table.insert(ct, co)
skynet.wait(co)
channel = ct.channel
ct = connecting[key]
-- reload again if ct ~= nil
end
return assert(node_address[key] and channel)
end
ct = {}
connecting[key] = ct
@@ -53,17 +59,26 @@ local function open_channel(t, key)
else
err = string.format("changenode [%s] (%s:%s) failed", key, host, port)
end
elseif address == false then
c = node_sender[key]
if c == nil then
-- no sender, always succ
succ = true
else
-- trun off the sender
succ, err = pcall(skynet.call, c, "lua", "changenode", false)
end
else
err = string.format("cluster node [%s] is %s.", key, address == false and "down" or "absent")
err = string.format("cluster node [%s] is absent.", key)
end
connecting[key] = nil
for _, co in ipairs(ct) do
skynet.wakeup(co)
end
assert(succ, err)
if node_address[key] ~= address then
return open_channel(t,key)
end
assert(succ, err)
return c
end
@@ -89,8 +104,9 @@ local function loadconfig(tmp)
assert(address == false or type(address) == "string")
if node_address[name] ~= address then
-- address changed
if rawget(node_channel, name) then
node_channel[name] = nil -- reset connection
if node_sender[name] then
-- reset connection if node_sender[name] exist
node_channel[name] = nil
table.insert(reload, name)
end
node_address[name] = address

View File

@@ -59,8 +59,13 @@ local function read_response(sock)
end
function command.changenode(host, port)
channel:changehost(host, tonumber(port))
channel:connect(true)
if not host then
skynet.error(string.format("Close cluster sender %s:%d", channel.__host, channel.__port))
channel:close()
else
channel:changehost(host, tonumber(port))
channel:connect(true)
end
skynet.ret(skynet.pack(nil))
end

View File

@@ -15,7 +15,7 @@ end
function command.QUERY(key, ...)
local d = database[key]
if d then
if d ~= nil then
return query(d, ...)
end
end
@@ -59,7 +59,7 @@ end
function command.UPDATE(...)
local ret, value = update(database, ...)
if ret or value == nil then
if ret ~= nil or value == nil then
return ret
end
local q = wakeup(wait_queue, ...)
@@ -97,7 +97,7 @@ skynet.start(function()
skynet.dispatch("lua", function (_, _, cmd, ...)
if cmd == "WAIT" then
local ret = command.QUERY(...)
if ret then
if ret ~= nil then
skynet.ret(skynet.pack(ret))
else
waitfor(wait_queue, ...)

View File

@@ -165,6 +165,7 @@ function COMMAND.help()
profactive = "profactive [on|off] : active/deactive jemalloc heap profilling",
dumpheap = "dumpheap : dump heap profilling",
killtask = "killtask address threadname : threadname listed by task",
dbgcmd = "run address debug command",
}
end
@@ -273,24 +274,25 @@ function COMMAND.inject(address, filename, ...)
return output
end
function COMMAND.task(address)
function COMMAND.dbgcmd(address, cmd, ...)
address = adjust_address(address)
return skynet.call(address,"debug","TASK")
return skynet.call(address, "debug", cmd, ...)
end
function COMMAND.task(address)
return COMMAND.dbgcmd(address, "TASK")
end
function COMMAND.killtask(address, threadname)
address = adjust_address(address)
return skynet.call(address, "debug", "KILLTASK", threadname)
return COMMAND.dbgcmd(address, "KILLTASK", threadname)
end
function COMMAND.uniqtask(address)
address = adjust_address(address)
return skynet.call(address,"debug","UNIQTASK")
return COMMAND.dbgcmd(address, "UNIQTASK")
end
function COMMAND.info(address, ...)
address = adjust_address(address)
return skynet.call(address,"debug","INFO", ...)
return COMMAND.dbgcmd(address, "INFO", ...)
end
function COMMANDX.debug(cmd)

View File

@@ -14,7 +14,7 @@ local function newobj(name, tbl)
assert(pool[name] == nil)
local cobj = sharedata.host.new(tbl)
sharedata.host.incref(cobj)
local v = { value = tbl , obj = cobj, watch = {} }
local v = {obj = cobj, watch = {} }
objmap[cobj] = v
pool[name] = v
pool_count[name] = { n = 0, threshold = 16 }

View File

@@ -1,14 +1,44 @@
#ifndef SKYNET_ATOMIC_H
#define SKYNET_ATOMIC_H
#ifdef __STDC_NO_ATOMICS__
#include <stddef.h>
#define ATOM_INT volatile int
#define ATOM_POINTER volatile uintptr_t
#define ATOM_SIZET volatile size_t
#define ATOM_ULONG volatile unsigned long
#define ATOM_INIT(ptr, v) (*(ptr) = v)
#define ATOM_LOAD(ptr) (*(ptr))
#define ATOM_STORE(ptr, v) (*(ptr) = v)
#define ATOM_CAS(ptr, oval, nval) __sync_bool_compare_and_swap(ptr, oval, nval)
#define ATOM_CAS_POINTER(ptr, oval, nval) __sync_bool_compare_and_swap(ptr, oval, nval)
#define ATOM_INC(ptr) __sync_add_and_fetch(ptr, 1)
#define ATOM_FINC(ptr) __sync_fetch_and_add(ptr, 1)
#define ATOM_DEC(ptr) __sync_sub_and_fetch(ptr, 1)
#define ATOM_FDEC(ptr) __sync_fetch_and_sub(ptr, 1)
#define ATOM_ADD(ptr,n) __sync_add_and_fetch(ptr, n)
#define ATOM_SUB(ptr,n) __sync_sub_and_fetch(ptr, n)
#define ATOM_AND(ptr,n) __sync_and_and_fetch(ptr, n)
#define ATOM_FADD(ptr,n) __sync_fetch_and_add(ptr, n)
#define ATOM_FSUB(ptr,n) __sync_fetch_and_sub(ptr, n)
#define ATOM_FAND(ptr,n) __sync_fetch_and_and(ptr, n)
#else
#include <stdatomic.h>
#define ATOM_INT atomic_int
#define ATOM_POINTER atomic_uintptr_t
#define ATOM_SIZET atomic_size_t
#define ATOM_ULONG atomic_ulong
#define ATOM_INIT(ref, v) atomic_init(ref, v)
#define ATOM_LOAD(ptr) atomic_load(ptr)
#define ATOM_STORE(ptr, v) atomic_store(ptr, v)
#define ATOM_CAS(ptr, oval, nval) atomic_compare_exchange_weak(ptr, &(oval), nval)
#define ATOM_CAS_POINTER(ptr, oval, nval) atomic_compare_exchange_weak(ptr, &(oval), nval)
#define ATOM_FINC(ptr) atomic_fetch_add(ptr, 1)
#define ATOM_FDEC(ptr) atomic_fetch_sub(ptr, 1)
#define ATOM_FADD(ptr,n) atomic_fetch_add(ptr, n)
#define ATOM_FSUB(ptr,n) atomic_fetch_sub(ptr, n)
#define ATOM_FAND(ptr,n) atomic_fetch_and(ptr, n)
#endif
#endif

View File

@@ -15,8 +15,8 @@
#define MEMORY_ALLOCTAG 0x20140605
#define MEMORY_FREETAG 0x0badf00d
static size_t _used_memory = 0;
static size_t _memory_block = 0;
static ATOM_SIZET _used_memory = 0;
static ATOM_SIZET _memory_block = 0;
struct mem_data {
uint32_t handle;
@@ -67,21 +67,21 @@ get_allocated_field(uint32_t handle) {
inline static void
update_xmalloc_stat_alloc(uint32_t handle, size_t __n) {
ATOM_ADD(&_used_memory, __n);
ATOM_INC(&_memory_block);
ATOM_FADD(&_used_memory, __n);
ATOM_FINC(&_memory_block);
ssize_t* allocated = get_allocated_field(handle);
if(allocated) {
ATOM_ADD(allocated, __n);
ATOM_FADD(allocated, __n);
}
}
inline static void
update_xmalloc_stat_free(uint32_t handle, size_t __n) {
ATOM_SUB(&_used_memory, __n);
ATOM_DEC(&_memory_block);
ATOM_FSUB(&_used_memory, __n);
ATOM_FDEC(&_memory_block);
ssize_t* allocated = get_allocated_field(handle);
if(allocated) {
ATOM_SUB(allocated, __n);
ATOM_FSUB(allocated, __n);
}
}
@@ -273,12 +273,12 @@ mallctl_cmd(const char* name) {
size_t
malloc_used_memory(void) {
return _used_memory;
return ATOM_LOAD(&_used_memory);
}
size_t
malloc_memory_block(void) {
return _memory_block;
return ATOM_LOAD(&_memory_block);
}
void

View File

@@ -3,26 +3,26 @@
#ifndef USE_PTHREAD_LOCK
#include "atomic.h"
struct rwlock {
int write;
int read;
ATOM_INT write;
ATOM_INT read;
};
static inline void
rwlock_init(struct rwlock *lock) {
lock->write = 0;
lock->read = 0;
ATOM_INIT(&lock->write, 0);
ATOM_INIT(&lock->read, 0);
}
static inline void
rwlock_rlock(struct rwlock *lock) {
for (;;) {
while(lock->write) {
__sync_synchronize();
}
__sync_add_and_fetch(&lock->read,1);
if (lock->write) {
__sync_sub_and_fetch(&lock->read,1);
while(ATOM_LOAD(&lock->write)) {}
ATOM_FINC(&lock->read);
if (ATOM_LOAD(&lock->write)) {
ATOM_FDEC(&lock->read);
} else {
break;
}
@@ -31,20 +31,19 @@ rwlock_rlock(struct rwlock *lock) {
static inline void
rwlock_wlock(struct rwlock *lock) {
while (__sync_lock_test_and_set(&lock->write,1)) {}
while(lock->read) {
__sync_synchronize();
}
int clear = 0;
while (!ATOM_CAS(&lock->write,clear,1)) {}
while(ATOM_LOAD(&lock->read)) {}
}
static inline void
rwlock_wunlock(struct rwlock *lock) {
__sync_lock_release(&lock->write);
ATOM_STORE(&lock->write, 0);
}
static inline void
rwlock_runlock(struct rwlock *lock) {
__sync_sub_and_fetch(&lock->read,1);
ATOM_FDEC(&lock->read);
}
#else

View File

@@ -9,7 +9,7 @@
#include <string.h>
struct skynet_monitor {
int version;
ATOM_INT version;
int check_version;
uint32_t source;
uint32_t destination;
@@ -31,7 +31,7 @@ void
skynet_monitor_trigger(struct skynet_monitor *sm, uint32_t source, uint32_t destination) {
sm->source = source;
sm->destination = destination;
ATOM_INC(&sm->version);
ATOM_FINC(&sm->version);
}
void

View File

@@ -46,13 +46,13 @@ struct skynet_context {
void * cb_ud;
skynet_cb cb;
struct message_queue *queue;
FILE * logfile;
ATOM_POINTER logfile;
uint64_t cpu_cost; // in microsec
uint64_t cpu_start; // in microsec
char result[32];
uint32_t handle;
int session_id;
int ref;
ATOM_INT ref;
int message_count;
bool init;
bool endless;
@@ -62,7 +62,7 @@ struct skynet_context {
};
struct skynet_node {
int total;
ATOM_INT total;
int init;
uint32_t monitor_exit;
pthread_key_t handle_key;
@@ -73,17 +73,17 @@ static struct skynet_node G_NODE;
int
skynet_context_total() {
return G_NODE.total;
return ATOM_LOAD(&G_NODE.total);
}
static void
context_inc() {
ATOM_INC(&G_NODE.total);
ATOM_FINC(&G_NODE.total);
}
static void
context_dec() {
ATOM_DEC(&G_NODE.total);
ATOM_FDEC(&G_NODE.total);
}
uint32_t
@@ -137,11 +137,11 @@ skynet_context_new(const char * name, const char *param) {
ctx->mod = mod;
ctx->instance = inst;
ctx->ref = 2;
ATOM_INIT(&ctx->ref , 2);
ctx->cb = NULL;
ctx->cb_ud = NULL;
ctx->session_id = 0;
ctx->logfile = NULL;
ATOM_INIT(&ctx->logfile, (uintptr_t)NULL);
ctx->init = false;
ctx->endless = false;
@@ -194,7 +194,7 @@ skynet_context_newsession(struct skynet_context *ctx) {
void
skynet_context_grab(struct skynet_context *ctx) {
ATOM_INC(&ctx->ref);
ATOM_FINC(&ctx->ref);
}
void
@@ -207,8 +207,9 @@ skynet_context_reserve(struct skynet_context *ctx) {
static void
delete_context(struct skynet_context *ctx) {
if (ctx->logfile) {
fclose(ctx->logfile);
FILE *f = (FILE *)ATOM_LOAD(&ctx->logfile);
if (f) {
fclose(f);
}
skynet_module_instance_release(ctx->mod, ctx->instance);
skynet_mq_mark_release(ctx->queue);
@@ -219,7 +220,7 @@ delete_context(struct skynet_context *ctx) {
struct skynet_context *
skynet_context_release(struct skynet_context *ctx) {
if (ATOM_DEC(&ctx->ref) == 0) {
if (ATOM_FDEC(&ctx->ref) == 1) {
delete_context(ctx);
return NULL;
}
@@ -264,8 +265,9 @@ dispatch_message(struct skynet_context *ctx, struct skynet_message *msg) {
pthread_setspecific(G_NODE.handle_key, (void *)(uintptr_t)(ctx->handle));
int type = msg->sz >> MESSAGE_TYPE_SHIFT;
size_t sz = msg->sz & MESSAGE_TYPE_MASK;
if (ctx->logfile) {
skynet_log_output(ctx->logfile, msg->source, type, msg->session, msg->data, sz);
FILE *f = (FILE *)ATOM_LOAD(&ctx->logfile);
if (f) {
skynet_log_output(f, msg->source, type, msg->session, msg->data, sz);
}
++ctx->message_count;
int reserve_msg;
@@ -589,11 +591,12 @@ cmd_logon(struct skynet_context * context, const char * param) {
if (ctx == NULL)
return NULL;
FILE *f = NULL;
FILE * lastf = ctx->logfile;
FILE * lastf = (FILE *)ATOM_LOAD(&ctx->logfile);
if (lastf == NULL) {
f = skynet_log_open(context, handle);
if (f) {
if (!ATOM_CAS_POINTER(&ctx->logfile, NULL, f)) {
uintptr_t exp = 0;
if (!ATOM_CAS_POINTER(&ctx->logfile, exp, (uintptr_t)f)) {
// logfile opens in other thread, close this one.
fclose(f);
}
@@ -611,10 +614,11 @@ cmd_logoff(struct skynet_context * context, const char * param) {
struct skynet_context * ctx = skynet_handle_grab(handle);
if (ctx == NULL)
return NULL;
FILE * f = ctx->logfile;
FILE * f = (FILE *)ATOM_LOAD(&ctx->logfile);
if (f) {
// logfile may close in other thread
if (ATOM_CAS_POINTER(&ctx->logfile, f, NULL)) {
uintptr_t fptr = (uintptr_t)f;
if (ATOM_CAS_POINTER(&ctx->logfile, fptr, (uintptr_t)NULL)) {
skynet_log_close(context, f, handle);
}
}
@@ -806,7 +810,7 @@ skynet_context_send(struct skynet_context * ctx, void * msg, size_t sz, uint32_t
void
skynet_globalinit(void) {
G_NODE.total = 0;
ATOM_INIT(&G_NODE.total , 0);
G_NODE.monitor_exit = 0;
G_NODE.init = 1;
if (pthread_key_create(&G_NODE.handle_key, NULL)) {

View File

@@ -235,7 +235,17 @@ bootstrap(struct skynet_context * logger, const char * cmdline) {
int sz = strlen(cmdline);
char name[sz+1];
char args[sz+1];
sscanf(cmdline, "%s %s", name, args);
int arg_pos;
sscanf(cmdline, "%s", name);
arg_pos = strlen(name);
if (arg_pos < sz) {
while(cmdline[arg_pos] == ' ') {
arg_pos++;
}
strncpy(args, cmdline + arg_pos, sz);
} else {
args[0] = '\0';
}
struct skynet_context *ctx = skynet_context_new(name, args);
if (ctx == NULL) {
skynet_error(NULL, "Bootstrap error : %s\n", cmdline);

View File

@@ -12,13 +12,6 @@
#include <stdlib.h>
#include <stdint.h>
#if defined(__APPLE__)
#include <AvailabilityMacros.h>
#include <sys/time.h>
#include <mach/task.h>
#include <mach/mach.h>
#endif
typedef void (*timer_execute_func)(void *ud,void *arg);
#define TIME_NEAR_SHIFT 8
@@ -233,33 +226,19 @@ skynet_timeout(uint32_t handle, int time, int session) {
// centisecond: 1/100 second
static void
systime(uint32_t *sec, uint32_t *cs) {
#if !defined(__APPLE__) || defined(AVAILABLE_MAC_OS_X_VERSION_10_12_AND_LATER)
struct timespec ti;
clock_gettime(CLOCK_REALTIME, &ti);
*sec = (uint32_t)ti.tv_sec;
*cs = (uint32_t)(ti.tv_nsec / 10000000);
#else
struct timeval tv;
gettimeofday(&tv, NULL);
*sec = tv.tv_sec;
*cs = tv.tv_usec / 10000;
#endif
}
static uint64_t
gettime() {
uint64_t t;
#if !defined(__APPLE__) || defined(AVAILABLE_MAC_OS_X_VERSION_10_12_AND_LATER)
struct timespec ti;
clock_gettime(CLOCK_MONOTONIC, &ti);
t = (uint64_t)ti.tv_sec * 100;
t += ti.tv_nsec / 10000000;
#else
struct timeval tv;
gettimeofday(&tv, NULL);
t = (uint64_t)tv.tv_sec * 100;
t += tv.tv_usec / 10000;
#endif
return t;
}
@@ -306,18 +285,8 @@ skynet_timer_init(void) {
uint64_t
skynet_thread_time(void) {
#if !defined(__APPLE__) || defined(AVAILABLE_MAC_OS_X_VERSION_10_12_AND_LATER)
struct timespec ti;
clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ti);
return (uint64_t)ti.tv_sec * MICROSEC + (uint64_t)ti.tv_nsec / (NANOSEC / MICROSEC);
#else
struct task_thread_times_info aTaskInfo;
mach_msg_type_number_t aTaskInfoCount = TASK_THREAD_TIMES_INFO_COUNT;
if (KERN_SUCCESS != task_info(mach_task_self(), TASK_THREAD_TIMES_INFO, (task_info_t )&aTaskInfo, &aTaskInfoCount)) {
return 0;
}
return (uint64_t)(aTaskInfo.user_time.seconds) + (uint64_t)aTaskInfo.user_time.microseconds;
#endif
}

View File

@@ -61,9 +61,9 @@ sp_wait(int efd, struct event *e, int max) {
e[i].s = ev[i].data.ptr;
unsigned flag = ev[i].events;
e[i].write = (flag & EPOLLOUT) != 0;
e[i].read = (flag & (EPOLLIN | EPOLLHUP)) != 0;
e[i].read = (flag & EPOLLIN) != 0;
e[i].error = (flag & EPOLLERR) != 0;
e[i].eof = false;
e[i].eof = (flag & EPOLLHUP) != 0;
}
return n;

View File

@@ -6,6 +6,7 @@
#define SOCKET_INFO_TCP 2
#define SOCKET_INFO_UDP 3
#define SOCKET_INFO_BIND 4
#define SOCKET_INFO_CLOSING 5
#include <stdint.h>

View File

@@ -4,7 +4,6 @@
#include "socket_poll.h"
#include "atomic.h"
#include "spinlock.h"
#include "skynet.h"
#include <sys/types.h>
#include <sys/socket.h>
@@ -29,9 +28,10 @@
#define SOCKET_TYPE_LISTEN 3
#define SOCKET_TYPE_CONNECTING 4
#define SOCKET_TYPE_CONNECTED 5
#define SOCKET_TYPE_HALFCLOSE 6
#define SOCKET_TYPE_PACCEPT 7
#define SOCKET_TYPE_BIND 8
#define SOCKET_TYPE_HALFCLOSE_READ 6
#define SOCKET_TYPE_HALFCLOSE_WRITE 7
#define SOCKET_TYPE_PACCEPT 8
#define SOCKET_TYPE_BIND 9
#define MAX_SOCKET (1<<MAX_SOCKET_P)
@@ -67,11 +67,12 @@ struct write_buffer {
char *ptr;
size_t sz;
bool userobject;
uint8_t udp_address[UDP_ADDRESS_SIZE];
};
#define SIZEOF_TCPBUFFER (offsetof(struct write_buffer, udp_address[0]))
#define SIZEOF_UDPBUFFER (sizeof(struct write_buffer))
struct write_buffer_udp {
struct write_buffer buffer;
uint8_t udp_address[UDP_ADDRESS_SIZE];
};
struct wb_list {
struct write_buffer * head;
@@ -91,14 +92,15 @@ struct socket {
struct wb_list low;
int64_t wb_size;
struct socket_stat stat;
volatile uint32_t sending;
ATOM_ULONG sending;
int fd;
int id;
ATOM_INT type;
uint8_t protocol;
uint8_t type;
bool reading;
bool writing;
int udpconnecting;
bool closing;
ATOM_INT udpconnecting;
int64_t warn_size;
union {
int size;
@@ -116,7 +118,7 @@ struct socket_server {
int sendctrl_fd;
int checkctrl;
poll_fd event_fd;
int alloc_id;
ATOM_INT alloc_id;
int event_n;
int event_index;
struct socket_object_interface soi;
@@ -276,6 +278,11 @@ socket_unlock(struct socket_lock *sl) {
}
}
static inline int
socket_invalid(struct socket *s, int id) {
return (s->id != id || ATOM_LOAD(&s->type) == SOCKET_TYPE_INVALID);
}
static inline bool
send_object_init(struct socket_server *ss, struct send_object *so, const void *object, size_t sz) {
if (sz == USEROBJECT) {
@@ -300,13 +307,13 @@ static inline void
send_object_init_from_sendbuffer(struct socket_server *ss, struct send_object *so, struct socket_sendbuffer *buf) {
switch (buf->type) {
case SOCKET_BUFFER_MEMORY:
send_object_init(ss, so, (void *)buf->buffer, buf->sz);
send_object_init(ss, so, buf->buffer, buf->sz);
break;
case SOCKET_BUFFER_OBJECT:
send_object_init(ss, so, (void *)buf->buffer, USEROBJECT);
send_object_init(ss, so, buf->buffer, USEROBJECT);
break;
case SOCKET_BUFFER_RAWPOINTER:
so->buffer = (void *)buf->buffer;
so->buffer = buf->buffer;
so->sz = buf->sz;
so->free_func = dummy_free;
break;
@@ -339,18 +346,19 @@ static int
reserve_id(struct socket_server *ss) {
int i;
for (i=0;i<MAX_SOCKET;i++) {
int id = ATOM_INC(&(ss->alloc_id));
int id = ATOM_FINC(&(ss->alloc_id))+1;
if (id < 0) {
id = ATOM_AND(&(ss->alloc_id), 0x7fffffff);
id = ATOM_FAND(&(ss->alloc_id), 0x7fffffff) & 0x7fffffff;
}
struct socket *s = &ss->slot[HASH_ID(id)];
if (s->type == SOCKET_TYPE_INVALID) {
if (ATOM_CAS(&s->type, SOCKET_TYPE_INVALID, SOCKET_TYPE_RESERVE)) {
int type_invalid = ATOM_LOAD(&s->type);
if (type_invalid == SOCKET_TYPE_INVALID) {
if (ATOM_CAS(&s->type, type_invalid, SOCKET_TYPE_RESERVE)) {
s->id = id;
s->protocol = PROTOCOL_UNKNOWN;
// socket_server_udp_connect may inc s->udpconncting directly (from other thread, before new_fd),
// so reset it to 0 here rather than in new_fd.
s->udpconnecting = 0;
ATOM_INIT(&s->udpconnecting, 0);
s->fd = -1;
return id;
} else {
@@ -400,12 +408,12 @@ socket_server_create(uint64_t time) {
for (i=0;i<MAX_SOCKET;i++) {
struct socket *s = &ss->slot[i];
s->type = SOCKET_TYPE_INVALID;
ATOM_INIT(&s->type, SOCKET_TYPE_INVALID);
clear_wb_list(&s->high);
clear_wb_list(&s->low);
spinlock_init(&s->dw_lock);
}
ss->alloc_id = 0;
ATOM_INIT(&ss->alloc_id , 0);
ss->event_n = 0;
ss->event_index = 0;
memset(&ss->soi, 0, sizeof(ss->soi));
@@ -437,7 +445,7 @@ free_buffer(struct socket_server *ss, struct socket_sendbuffer *buf) {
void *buffer = (void *)buf->buffer;
switch (buf->type) {
case SOCKET_BUFFER_MEMORY:
FREE((void *)buffer);
FREE(buffer);
break;
case SOCKET_BUFFER_OBJECT:
ss->soi.free(buffer);
@@ -474,20 +482,21 @@ force_close(struct socket_server *ss, struct socket *s, struct socket_lock *l, s
result->ud = 0;
result->data = NULL;
result->opaque = s->opaque;
if (s->type == SOCKET_TYPE_INVALID) {
uint8_t type = ATOM_LOAD(&s->type);
if (type == SOCKET_TYPE_INVALID) {
return;
}
assert(s->type != SOCKET_TYPE_RESERVE);
assert(type != SOCKET_TYPE_RESERVE);
free_wb_list(ss,&s->high);
free_wb_list(ss,&s->low);
sp_del(ss->event_fd, s->fd);
socket_lock(l);
if (s->type != SOCKET_TYPE_BIND) {
if (type != SOCKET_TYPE_BIND) {
if (close(s->fd) < 0) {
perror("close socket:");
}
}
s->type = SOCKET_TYPE_INVALID;
ATOM_STORE(&s->type, SOCKET_TYPE_INVALID);
if (s->dw_buffer) {
struct socket_sendbuffer tmp;
tmp.buffer = s->dw_buffer;
@@ -508,7 +517,7 @@ socket_server_release(struct socket_server *ss) {
struct socket *s = &ss->slot[i];
struct socket_lock l;
socket_lock_init(s, &l);
if (s->type != SOCKET_TYPE_RESERVE) {
if (ATOM_LOAD(&s->type) != SOCKET_TYPE_RESERVE) {
force_close(ss, s, &l, &dummy);
}
spinlock_destroy(&s->dw_lock);
@@ -546,10 +555,10 @@ enable_read(struct socket_server *ss, struct socket *s, bool enable) {
static struct socket *
new_fd(struct socket_server *ss, int id, int fd, int protocol, uintptr_t opaque, bool reading) {
struct socket * s = &ss->slot[HASH_ID(id)];
assert(s->type == SOCKET_TYPE_RESERVE);
assert(ATOM_LOAD(&s->type) == SOCKET_TYPE_RESERVE);
if (sp_add(ss->event_fd, fd, s)) {
s->type = SOCKET_TYPE_INVALID;
ATOM_STORE(&s->type, SOCKET_TYPE_INVALID);
return NULL;
}
@@ -557,7 +566,8 @@ new_fd(struct socket_server *ss, int id, int fd, int protocol, uintptr_t opaque,
s->fd = fd;
s->reading = true;
s->writing = false;
s->sending = ID_TAG16(id) << 16 | 0;
s->closing = false;
ATOM_INIT(&s->sending , ID_TAG16(id) << 16 | 0);
s->protocol = protocol;
s->p.size = MIN_READ_BUFFER;
s->opaque = opaque;
@@ -569,7 +579,7 @@ new_fd(struct socket_server *ss, int id, int fd, int protocol, uintptr_t opaque,
s->dw_size = 0;
memset(&s->stat, 0, sizeof(s->stat));
if (enable_read(ss, s, reading)) {
s->type = SOCKET_TYPE_INVALID;
ATOM_STORE(&s->type , SOCKET_TYPE_INVALID);
return NULL;
}
return s;
@@ -642,7 +652,7 @@ open_socket(struct socket_server *ss, struct request_open * request, struct sock
}
if(status == 0) {
ns->type = SOCKET_TYPE_CONNECTED;
ATOM_STORE(&ns->type , SOCKET_TYPE_CONNECTED);
struct sockaddr * addr = ai_ptr->ai_addr;
void * sin_addr = (ai_ptr->ai_family == AF_INET) ? (void*)&((struct sockaddr_in *)addr)->sin_addr : (void*)&((struct sockaddr_in6 *)addr)->sin6_addr;
if (inet_ntop(ai_ptr->ai_family, sin_addr, ss->buffer, sizeof(ss->buffer))) {
@@ -651,7 +661,7 @@ open_socket(struct socket_server *ss, struct request_open * request, struct sock
freeaddrinfo( ai_list );
return SOCKET_OPEN;
} else {
ns->type = SOCKET_TYPE_CONNECTING;
ATOM_STORE(&ns->type , SOCKET_TYPE_CONNECTING);
if (enable_write(ss, ns, true)) {
result->data = "enable write failed";
goto _failed;
@@ -667,6 +677,38 @@ _failed_getaddrinfo:
return SOCKET_ERR;
}
static int
report_error(struct socket *s, struct socket_message *result, const char *err) {
result->id = s->id;
result->ud = 0;
result->opaque = s->opaque;
result->data = (char *)err;
return SOCKET_ERR;
}
static int
close_write(struct socket_server *ss, struct socket *s, struct socket_lock *l, struct socket_message *result) {
if (s->closing) {
force_close(ss,s,l,result);
return SOCKET_RST;
} else {
int t = ATOM_LOAD(&s->type);
if (t == SOCKET_TYPE_HALFCLOSE_READ) {
// recv 0 before, ignore the error and close fd
force_close(ss,s,l,result);
return SOCKET_RST;
}
if (t == SOCKET_TYPE_HALFCLOSE_WRITE) {
// already raise SOCKET_ERR
return SOCKET_RST;
}
ATOM_STORE(&s->type, SOCKET_TYPE_HALFCLOSE_WRITE);
shutdown(s->fd, SHUT_WR);
enable_write(ss, s, false);
return report_error(s, result, strerror(errno));
}
}
static int
send_list_tcp(struct socket_server *ss, struct socket *s, struct wb_list *list, struct socket_lock *l, struct socket_message *result) {
while (list->head) {
@@ -680,8 +722,7 @@ send_list_tcp(struct socket_server *ss, struct socket *s, struct wb_list *list,
case AGAIN_WOULDBLOCK:
return -1;
}
force_close(ss,s,l,result);
return SOCKET_CLOSE;
return close_write(ss, s, l, result);
}
stat_write(ss,s,(int)sz);
s->wb_size -= sz;
@@ -737,8 +778,9 @@ static int
send_list_udp(struct socket_server *ss, struct socket *s, struct wb_list *list, struct socket_message *result) {
while (list->head) {
struct write_buffer * tmp = list->head;
struct write_buffer_udp * udp = (struct write_buffer_udp *)tmp;
union sockaddr_all sa;
socklen_t sasz = udp_socket_address(s, tmp->udp_address, &sa);
socklen_t sasz = udp_socket_address(s, udp->udp_address, &sa);
if (sasz == 0) {
skynet_error(NULL, "socket-server : udp (%d) type mismatch.", s->id);
drop_udp(ss, s, list, tmp);
@@ -817,14 +859,26 @@ static int
send_buffer_(struct socket_server *ss, struct socket *s, struct socket_lock *l, struct socket_message *result) {
assert(!list_uncomplete(&s->low));
// step 1
if (send_list(ss,s,&s->high,l,result) == SOCKET_CLOSE) {
return SOCKET_CLOSE;
int ret = send_list(ss,s,&s->high,l,result);
if (ret != -1) {
if (ret == SOCKET_ERR) {
// HALFCLOSE_WRITE
return SOCKET_ERR;
}
// SOCKET_RST (ignore)
return -1;
}
if (s->high.head == NULL) {
// step 2
if (s->low.head != NULL) {
if (send_list(ss,s,&s->low,l,result) == SOCKET_CLOSE) {
return SOCKET_CLOSE;
int ret = send_list(ss,s,&s->low,l,result);
if (ret != -1) {
if (ret == SOCKET_ERR) {
// HALFCLOSE_WRITE
return SOCKET_ERR;
}
// SOCKET_RST (ignore)
return -1;
}
// step 3
if (list_uncomplete(&s->low)) {
@@ -836,28 +890,26 @@ send_buffer_(struct socket_server *ss, struct socket *s, struct socket_lock *l,
}
// step 4
assert(send_buffer_empty(s) && s->wb_size == 0);
int err = enable_write(ss, s, false);
if (s->type == SOCKET_TYPE_HALFCLOSE) {
force_close(ss, s, l, result);
return SOCKET_CLOSE;
if (s->closing) {
// finish writing
force_close(ss, s, l, result);
return -1;
}
int err = enable_write(ss, s, false);
if (err) {
result->opaque = s->opaque;
result->id = s->id;
result->ud = 0;
result->data = "disable write failed";
return SOCKET_ERR;
return report_error(s, result, "disable write failed");
}
if(s->warn_size > 0){
s->warn_size = 0;
result->opaque = s->opaque;
result->id = s->id;
result->ud = 0;
result->data = NULL;
return SOCKET_WARNING;
s->warn_size = 0;
result->opaque = s->opaque;
result->id = s->id;
result->ud = 0;
result->data = NULL;
return SOCKET_WARNING;
}
}
@@ -870,7 +922,7 @@ send_buffer(struct socket_server *ss, struct socket *s, struct socket_lock *l, s
return -1; // blocked by direct write, send later.
if (s->dw_buffer) {
// add direct write buffer before high.head
struct write_buffer * buf = MALLOC(SIZEOF_TCPBUFFER);
struct write_buffer * buf = MALLOC(sizeof(*buf));
struct send_object so;
buf->userobject = send_object_init(ss, &so, (void *)s->dw_buffer, s->dw_size);
buf->ptr = (char*)so.buffer+s->dw_offset;
@@ -915,20 +967,20 @@ append_sendbuffer_(struct socket_server *ss, struct wb_list *s, struct request_s
static inline void
append_sendbuffer_udp(struct socket_server *ss, struct socket *s, int priority, struct request_send * request, const uint8_t udp_address[UDP_ADDRESS_SIZE]) {
struct wb_list *wl = (priority == PRIORITY_HIGH) ? &s->high : &s->low;
struct write_buffer *buf = append_sendbuffer_(ss, wl, request, SIZEOF_UDPBUFFER);
struct write_buffer_udp *buf = (struct write_buffer_udp *)append_sendbuffer_(ss, wl, request, sizeof(*buf));
memcpy(buf->udp_address, udp_address, UDP_ADDRESS_SIZE);
s->wb_size += buf->sz;
s->wb_size += buf->buffer.sz;
}
static inline void
append_sendbuffer(struct socket_server *ss, struct socket *s, struct request_send * request) {
struct write_buffer *buf = append_sendbuffer_(ss, &s->high, request, SIZEOF_TCPBUFFER);
struct write_buffer *buf = append_sendbuffer_(ss, &s->high, request, sizeof(*buf));
s->wb_size += buf->sz;
}
static inline void
append_sendbuffer_low(struct socket_server *ss,struct socket *s, struct request_send * request) {
struct write_buffer *buf = append_sendbuffer_(ss, &s->low, request, SIZEOF_TCPBUFFER);
struct write_buffer *buf = append_sendbuffer_(ss, &s->low, request, sizeof(*buf));
s->wb_size += buf->sz;
}
@@ -936,14 +988,10 @@ static int
trigger_write(struct socket_server *ss, struct request_send * request, struct socket_message *result) {
int id = request->id;
struct socket * s = &ss->slot[HASH_ID(id)];
if (s->type == SOCKET_TYPE_INVALID || s->id != id)
if (socket_invalid(s, id))
return -1;
if (enable_write(ss, s, true)) {
result->opaque = s->opaque;
result->id = s->id;
result->ud = 0;
result->data = "enable write failed";
return SOCKET_ERR;
return report_error(s, result, "enable write failed");
}
return -1;
}
@@ -961,18 +1009,20 @@ send_socket(struct socket_server *ss, struct request_send * request, struct sock
struct socket * s = &ss->slot[HASH_ID(id)];
struct send_object so;
send_object_init(ss, &so, request->buffer, request->sz);
if (s->type == SOCKET_TYPE_INVALID || s->id != id
|| s->type == SOCKET_TYPE_HALFCLOSE
|| s->type == SOCKET_TYPE_PACCEPT) {
uint8_t type = ATOM_LOAD(&s->type);
if (type == SOCKET_TYPE_INVALID || s->id != id
|| type == SOCKET_TYPE_HALFCLOSE_WRITE
|| type == SOCKET_TYPE_PACCEPT
|| s->closing) {
so.free_func((void *)request->buffer);
return -1;
}
if (s->type == SOCKET_TYPE_PLISTEN || s->type == SOCKET_TYPE_LISTEN) {
if (type == SOCKET_TYPE_PLISTEN || type == SOCKET_TYPE_LISTEN) {
skynet_error(NULL, "socket-server: write to listen fd %d.", id);
so.free_func((void *)request->buffer);
return -1;
}
if (send_buffer_empty(s) && s->type == SOCKET_TYPE_CONNECTED) {
if (send_buffer_empty(s)) {
if (s->protocol == PROTOCOL_TCP) {
append_sendbuffer(ss, s, request); // add to high priority list, even priority == PRIORITY_LOW
} else {
@@ -984,7 +1034,7 @@ send_socket(struct socket_server *ss, struct request_send * request, struct sock
socklen_t sasz = udp_socket_address(s, udp_address, &sa);
if (sasz == 0) {
// udp type mismatch, just drop it.
skynet_error(NULL, "socket-server: udp socket (%d) type mistach.", id);
skynet_error(NULL, "socket-server: udp socket (%d) type mismatch.", id);
so.free_func((void *)request->buffer);
return -1;
}
@@ -998,11 +1048,7 @@ send_socket(struct socket_server *ss, struct request_send * request, struct sock
}
}
if (enable_write(ss, s, true)) {
result->opaque = s->opaque;
result->id = s->id;
result->ud = 0;
result->data = "enable write failed";
return SOCKET_ERR;
return report_error(s, result, "enable write failed");
}
} else {
if (s->protocol == PROTOCOL_TCP) {
@@ -1037,7 +1083,7 @@ listen_socket(struct socket_server *ss, struct request_listen * request, struct
if (s == NULL) {
goto _failed;
}
s->type = SOCKET_TYPE_PLISTEN;
ATOM_STORE(&s->type , SOCKET_TYPE_PLISTEN);
return -1;
_failed:
close(listen_fd);
@@ -1052,38 +1098,58 @@ _failed:
static inline int
nomore_sending_data(struct socket *s) {
return send_buffer_empty(s) && s->dw_buffer == NULL && (s->sending & 0xffff) == 0;
return (send_buffer_empty(s) && s->dw_buffer == NULL && (ATOM_LOAD(&s->sending) & 0xffff) == 0)
|| (ATOM_LOAD(&s->type) == SOCKET_TYPE_HALFCLOSE_WRITE);
}
static void
close_read(struct socket_server *ss, struct socket * s, struct socket_message *result) {
// Don't read socket later
ATOM_STORE(&s->type , SOCKET_TYPE_HALFCLOSE_READ);
enable_read(ss,s,false);
shutdown(s->fd, SHUT_RD);
result->id = s->id;
result->ud = 0;
result->data = NULL;
result->opaque = s->opaque;
}
static inline int
halfclose_read(struct socket *s) {
return ATOM_LOAD(&s->type) == SOCKET_TYPE_HALFCLOSE_READ;
}
// SOCKET_CLOSE can be raised (only once) in one of two conditions.
// See https://github.com/cloudwu/skynet/issues/1346 for more discussion.
// 1. close socket by self, See close_socket()
// 2. recv 0 or eof event (close socket by remote), See forward_message_tcp()
// It's able to write data after SOCKET_CLOSE (In condition 2), but if remote is closed, SOCKET_ERR may raised.
static int
close_socket(struct socket_server *ss, struct request_close *request, struct socket_message *result) {
int id = request->id;
struct socket * s = &ss->slot[HASH_ID(id)];
if (s->type == SOCKET_TYPE_INVALID || s->id != id) {
result->id = id;
result->opaque = request->opaque;
result->ud = 0;
result->data = NULL;
return SOCKET_CLOSE;
if (socket_invalid(s, id)) {
// The socket is closed, ignore
return -1;
}
struct socket_lock l;
socket_lock_init(s, &l);
if (!nomore_sending_data(s)) {
enable_read(ss,s,true);
int type = send_buffer(ss,s,&l,result);
// type : -1 or SOCKET_WARNING or SOCKET_CLOSE, SOCKET_WARNING means nomore_sending_data
if (type != -1 && type != SOCKET_WARNING)
return type;
}
int shutdown_read = halfclose_read(s);
if (request->shutdown || nomore_sending_data(s)) {
// If socket is SOCKET_TYPE_HALFCLOSE_READ, Do not raise SOCKET_CLOSE again.
int r = shutdown_read ? -1 : SOCKET_CLOSE;
force_close(ss,s,&l,result);
result->id = id;
result->opaque = request->opaque;
return r;
}
s->closing = true;
if (!shutdown_read) {
// don't read socket after socket.close()
close_read(ss, s, result);
return SOCKET_CLOSE;
}
s->type = SOCKET_TYPE_HALFCLOSE;
shutdown(s->fd, SHUT_RD);
// recv 0 before (socket is SOCKET_TYPE_HALFCLOSE_READ) and waiting for sending data out.
return -1;
}
@@ -1099,7 +1165,7 @@ bind_socket(struct socket_server *ss, struct request_bind *request, struct socke
return SOCKET_ERR;
}
sp_nonblocking(request->fd);
s->type = SOCKET_TYPE_BIND;
ATOM_STORE(&s->type , SOCKET_TYPE_BIND);
result->data = "binding";
return SOCKET_OPEN;
}
@@ -1112,28 +1178,34 @@ resume_socket(struct socket_server *ss, struct request_resumepause *request, str
result->ud = 0;
result->data = NULL;
struct socket *s = &ss->slot[HASH_ID(id)];
if (s->type == SOCKET_TYPE_INVALID || s->id !=id) {
if (socket_invalid(s, id)) {
result->data = "invalid socket";
return SOCKET_ERR;
}
if (halfclose_read(s)) {
// The closing socket may be in transit, so raise an error. See https://github.com/cloudwu/skynet/issues/1374
result->data = "socket closed";
return SOCKET_ERR;
}
struct socket_lock l;
socket_lock_init(s, &l);
if (enable_read(ss, s, true)) {
result->data = "enable read failed";
return SOCKET_ERR;
}
if (s->type == SOCKET_TYPE_PACCEPT || s->type == SOCKET_TYPE_PLISTEN) {
s->type = (s->type == SOCKET_TYPE_PACCEPT) ? SOCKET_TYPE_CONNECTED : SOCKET_TYPE_LISTEN;
uint8_t type = ATOM_LOAD(&s->type);
if (type == SOCKET_TYPE_PACCEPT || type == SOCKET_TYPE_PLISTEN) {
ATOM_STORE(&s->type , (type == SOCKET_TYPE_PACCEPT) ? SOCKET_TYPE_CONNECTED : SOCKET_TYPE_LISTEN);
s->opaque = request->opaque;
result->data = "start";
return SOCKET_OPEN;
} else if (s->type == SOCKET_TYPE_CONNECTED) {
} else if (type == SOCKET_TYPE_CONNECTED) {
// todo: maybe we should send a message SOCKET_TRANSFER to s->opaque
s->opaque = request->opaque;
result->data = "transfer";
return SOCKET_OPEN;
}
// if s->type == SOCKET_TYPE_HALFCLOSE , SOCKET_CLOSE message will send later
// if s->type == SOCKET_TYPE_HALFCLOSE_WRITE , SOCKET_CLOSE message will send later
return -1;
}
@@ -1141,15 +1213,11 @@ static int
pause_socket(struct socket_server *ss, struct request_resumepause *request, struct socket_message *result) {
int id = request->id;
struct socket *s = &ss->slot[HASH_ID(id)];
if (s->type == SOCKET_TYPE_INVALID || s->id !=id) {
if (socket_invalid(s, id)) {
return -1;
}
if (enable_read(ss, s, false)) {
result->id = id;
result->opaque = request->opaque;
result->ud = 0;
result->data = "enable read failed";
return SOCKET_ERR;
return report_error(s, result, "enable read failed");
}
return -1;
}
@@ -1158,7 +1226,7 @@ static void
setopt_socket(struct socket_server *ss, struct request_setopt *request) {
int id = request->id;
struct socket *s = &ss->slot[HASH_ID(id)];
if (s->type == SOCKET_TYPE_INVALID || s->id !=id) {
if (socket_invalid(s, id)) {
return;
}
int v = request->value;
@@ -1210,7 +1278,7 @@ add_udp_socket(struct socket_server *ss, struct request_udp *udp) {
ss->slot[HASH_ID(id)].type = SOCKET_TYPE_INVALID;
return;
}
ns->type = SOCKET_TYPE_CONNECTED;
ATOM_STORE(&ns->type , SOCKET_TYPE_CONNECTED);
memset(ns->p.udp_address, 0, sizeof(ns->p.udp_address));
}
@@ -1218,25 +1286,20 @@ static int
set_udp_address(struct socket_server *ss, struct request_setudp *request, struct socket_message *result) {
int id = request->id;
struct socket *s = &ss->slot[HASH_ID(id)];
if (s->type == SOCKET_TYPE_INVALID || s->id !=id) {
if (socket_invalid(s, id)) {
return -1;
}
int type = request->address[0];
if (type != s->protocol) {
// protocol mismatch
result->opaque = s->opaque;
result->id = s->id;
result->ud = 0;
result->data = "protocol mismatch";
return SOCKET_ERR;
return report_error(s, result, "protocol mismatch");
}
if (type == PROTOCOL_UDP) {
memcpy(s->p.udp_address, request->address, 1+2+4); // 1 type, 2 port, 4 ipv4
} else {
memcpy(s->p.udp_address, request->address, 1+2+16); // 1 type, 2 port, 16 ipv6
}
ATOM_DEC(&s->udpconnecting);
ATOM_FDEC(&s->udpconnecting);
return -1;
}
@@ -1245,7 +1308,7 @@ inc_sending_ref(struct socket *s, int id) {
if (s->protocol != PROTOCOL_TCP)
return;
for (;;) {
uint32_t sending = s->sending;
unsigned long sending = ATOM_LOAD(&s->sending);
if ((sending >> 16) == ID_TAG16(id)) {
if ((sending & 0xffff) == 0xffff) {
// s->sending may overflow (rarely), so busy waiting here for socket thread dec it. see issue #794
@@ -1267,8 +1330,8 @@ dec_sending_ref(struct socket_server *ss, int id) {
struct socket * s = &ss->slot[HASH_ID(id)];
// Notice: udp may inc sending while type == SOCKET_TYPE_RESERVE
if (s->id == id && s->protocol == PROTOCOL_TCP) {
assert((s->sending & 0xffff) != 0);
ATOM_DEC(&s->sending);
assert((ATOM_LOAD(&s->sending) & 0xffff) != 0);
ATOM_FDEC(&s->sending);
}
}
@@ -1343,48 +1406,56 @@ forward_message_tcp(struct socket_server *ss, struct socket *s, struct socket_lo
FREE(buffer);
switch(errno) {
case EINTR:
break;
case AGAIN_WOULDBLOCK:
skynet_error(NULL, "socket-server: EAGAIN capture.");
break;
default:
// close when error
force_close(ss, s, l, result);
result->data = strerror(errno);
return SOCKET_ERR;
return report_error(s, result, strerror(errno));
}
return -1;
}
if (n==0) {
FREE(buffer);
if (nomore_sending_data(s)) {
force_close(ss,s,l,result);
return SOCKET_CLOSE;
} else {
s->type = SOCKET_TYPE_HALFCLOSE;
shutdown(s->fd, SHUT_RD);
return -1;
if (s->closing) {
// Rare case : if s->closing is true, reading event is disable, and SOCKET_CLOSE is raised.
if (nomore_sending_data(s)) {
force_close(ss,s,l,result);
}
return -1;
}
int t = ATOM_LOAD(&s->type);
if (t == SOCKET_TYPE_HALFCLOSE_READ) {
// Rare case : Already shutdown read.
return -1;
}
if (t == SOCKET_TYPE_HALFCLOSE_WRITE) {
// Remote shutdown read (write error) before.
force_close(ss,s,l,result);
} else {
close_read(ss, s, result);
}
return SOCKET_CLOSE;
}
if (s->type == SOCKET_TYPE_HALFCLOSE) {
// discard recv data
if (halfclose_read(s)) {
// discard recv data (Rare case : if socket is HALFCLOSE_READ, reading event is disable.)
FREE(buffer);
return -1;
}
stat_read(ss,s,n);
if (n == sz) {
s->p.size *= 2;
} else if (sz > MIN_READ_BUFFER && n*2 < sz) {
s->p.size /= 2;
}
result->opaque = s->opaque;
result->id = s->id;
result->ud = n;
result->data = buffer;
if (n == sz) {
s->p.size *= 2;
return SOCKET_MORE;
} else if (sz > MIN_READ_BUFFER && n*2 < sz) {
s->p.size /= 2;
}
return SOCKET_DATA;
}
@@ -1415,14 +1486,13 @@ forward_message_udp(struct socket_server *ss, struct socket *s, struct socket_lo
switch(errno) {
case EINTR:
case AGAIN_WOULDBLOCK:
break;
default:
// close when error
force_close(ss, s, l, result);
result->data = strerror(errno);
return SOCKET_ERR;
return -1;
}
return -1;
int error = errno;
// close when error
force_close(ss, s, l, result);
result->data = strerror(error);
return SOCKET_ERR;
}
stat_read(ss,s,n);
@@ -1454,14 +1524,12 @@ report_connect(struct socket_server *ss, struct socket *s, struct socket_lock *l
socklen_t len = sizeof(error);
int code = getsockopt(s->fd, SOL_SOCKET, SO_ERROR, &error, &len);
if (code < 0 || error) {
force_close(ss,s,l, result);
if (code >= 0)
result->data = strerror(error);
else
result->data = strerror(errno);
error = code < 0 ? errno : error;
force_close(ss, s, l, result);
result->data = strerror(error);
return SOCKET_ERR;
} else {
s->type = SOCKET_TYPE_CONNECTED;
ATOM_STORE(&s->type , SOCKET_TYPE_CONNECTED);
result->opaque = s->opaque;
result->id = s->id;
result->ud = 0;
@@ -1490,8 +1558,8 @@ static int
getname(union sockaddr_all *u, char *buffer, size_t sz) {
char tmp[INET6_ADDRSTRLEN];
void * sin_addr = (u->s.sa_family == AF_INET) ? (void*)&u->v4.sin_addr : (void *)&u->v6.sin6_addr;
int sin_port = ntohs((u->s.sa_family == AF_INET) ? u->v4.sin_port : u->v6.sin6_port);
if (inet_ntop(u->s.sa_family, sin_addr, tmp, sizeof(tmp))) {
int sin_port = ntohs((u->s.sa_family == AF_INET) ? u->v4.sin_port : u->v6.sin6_port);
snprintf(buffer, sz, "%s:%d", tmp, sin_port);
return 1;
} else {
@@ -1532,7 +1600,7 @@ report_accept(struct socket_server *ss, struct socket *s, struct socket_message
// accept new one connection
stat_read(ss,s,1);
ns->type = SOCKET_TYPE_PACCEPT;
ATOM_STORE(&ns->type , SOCKET_TYPE_PACCEPT);
result->opaque = s->opaque;
result->id = s->id;
result->ud = id;
@@ -1554,7 +1622,7 @@ clear_closed_event(struct socket_server *ss, struct socket_message * result, int
struct event *e = &ss->ev[i];
struct socket *s = e->s;
if (s) {
if (s->type == SOCKET_TYPE_INVALID && s->id == id) {
if (socket_invalid(s, id) && s->id == id) {
e->s = NULL;
break;
}
@@ -1588,10 +1656,11 @@ socket_server_poll(struct socket_server *ss, struct socket_message * result, int
ss->event_index = 0;
if (ss->event_n <= 0) {
ss->event_n = 0;
if (errno == EINTR) {
continue;
int err = errno;
if (err != EINTR) {
skynet_error(NULL, "socket-server: %s", strerror(err));
}
return -1;
continue;
}
}
struct event *e = &ss->ev[ss->event_index++];
@@ -1602,7 +1671,7 @@ socket_server_poll(struct socket_server *ss, struct socket_message * result, int
}
struct socket_lock l;
socket_lock_init(s, &l);
switch (s->type) {
switch (ATOM_LOAD(&s->type)) {
case SOCKET_TYPE_CONNECTING:
return report_connect(ss, s, &l, result);
case SOCKET_TYPE_LISTEN: {
@@ -1623,6 +1692,10 @@ socket_server_poll(struct socket_server *ss, struct socket_message * result, int
int type;
if (s->protocol == PROTOCOL_TCP) {
type = forward_message_tcp(ss, s, &l, result);
if (type == SOCKET_MORE) {
--ss->event_index;
return SOCKET_DATA;
}
} else {
type = forward_message_udp(ss, s, &l, result);
if (type == SOCKET_UDP) {
@@ -1647,7 +1720,6 @@ socket_server_poll(struct socket_server *ss, struct socket_message * result, int
return type;
}
if (e->error) {
// close when error
int error;
socklen_t len = sizeof(error);
int code = getsockopt(s->fd, SOL_SOCKET, SO_ERROR, &error, &len);
@@ -1659,13 +1731,16 @@ socket_server_poll(struct socket_server *ss, struct socket_message * result, int
} else {
err = "Unknown error";
}
force_close(ss, s, &l, result);
result->data = (char *)err;
return SOCKET_ERR;
return report_error(s, result, err);
}
if(e->eof) {
if (e->eof) {
// For epoll (at least), FIN packets are exchanged both ways.
// See: https://stackoverflow.com/questions/52976152/tcp-when-is-epollhup-generated
int halfclose = halfclose_read(s);
force_close(ss, s, &l, result);
return SOCKET_CLOSE;
if (!halfclose) {
return SOCKET_CLOSE;
}
}
break;
}
@@ -1676,8 +1751,9 @@ static void
send_request(struct socket_server *ss, struct request_package *request, char type, int len) {
request->header[6] = (uint8_t)type;
request->header[7] = (uint8_t)len;
const char * req = (const char *)request + offsetof(struct request_package, header[6]);
for (;;) {
ssize_t n = write(ss->sendctrl_fd, &request->header[6], len+2);
ssize_t n = write(ss->sendctrl_fd, req, len+2);
if (n<0) {
if (errno != EINTR) {
skynet_error(NULL, "socket-server : send ctrl command error %s.", strerror(errno));
@@ -1720,7 +1796,7 @@ socket_server_connect(struct socket_server *ss, uintptr_t opaque, const char * a
static inline int
can_direct_write(struct socket *s, int id) {
return s->id == id && nomore_sending_data(s) && s->type == SOCKET_TYPE_CONNECTED && s->udpconnecting == 0;
return s->id == id && nomore_sending_data(s) && ATOM_LOAD(&s->type) == SOCKET_TYPE_CONNECTED && ATOM_LOAD(&s->udpconnecting) == 0;
}
// return -1 when error, 0 when success
@@ -1728,7 +1804,7 @@ int
socket_server_send(struct socket_server *ss, struct socket_sendbuffer *buf) {
int id = buf->id;
struct socket * s = &ss->slot[HASH_ID(id)];
if (s->id != id || s->type == SOCKET_TYPE_INVALID) {
if (socket_invalid(s, id) || s->closing) {
free_buffer(ss, buf);
return -1;
}
@@ -1773,8 +1849,6 @@ socket_server_send(struct socket_server *ss, struct socket_sendbuffer *buf) {
socket_unlock(&l);
inc_sending_ref(s, id);
struct request_package request;
request.u.send.id = id;
request.u.send.sz = 0;
@@ -1804,7 +1878,7 @@ socket_server_send_lowpriority(struct socket_server *ss, struct socket_sendbuffe
int id = buf->id;
struct socket * s = &ss->slot[HASH_ID(id)];
if (s->id != id || s->type == SOCKET_TYPE_INVALID) {
if (socket_invalid(s, id)) {
free_buffer(ss, buf);
return -1;
}
@@ -2009,7 +2083,7 @@ int
socket_server_udp_send(struct socket_server *ss, const struct socket_udp_address *addr, struct socket_sendbuffer *buf) {
int id = buf->id;
struct socket * s = &ss->slot[HASH_ID(id)];
if (s->id != id || s->type == SOCKET_TYPE_INVALID) {
if (socket_invalid(s, id)) {
free_buffer(ss, buf);
return -1;
}
@@ -2070,17 +2144,17 @@ socket_server_udp_send(struct socket_server *ss, const struct socket_udp_address
int
socket_server_udp_connect(struct socket_server *ss, int id, const char * addr, int port) {
struct socket * s = &ss->slot[HASH_ID(id)];
if (s->id != id || s->type == SOCKET_TYPE_INVALID) {
if (socket_invalid(s, id)) {
return -1;
}
struct socket_lock l;
socket_lock_init(s, &l);
socket_lock(&l);
if (s->id != id || s->type == SOCKET_TYPE_INVALID) {
if (socket_invalid(s, id)) {
socket_unlock(&l);
return -1;
}
ATOM_INC(&s->udpconnecting);
ATOM_FINC(&s->udpconnecting);
socket_unlock(&l);
int status;
@@ -2158,7 +2232,8 @@ static int
query_info(struct socket *s, struct socket_info *si) {
union sockaddr_all u;
socklen_t slen = sizeof(u);
switch (s->type) {
int closing = 0;
switch (ATOM_LOAD(&s->type)) {
case SOCKET_TYPE_BIND:
si->type = SOCKET_INFO_BIND;
si->name[0] = '\0';
@@ -2169,9 +2244,12 @@ query_info(struct socket *s, struct socket_info *si) {
getname(&u, si->name, sizeof(si->name));
}
break;
case SOCKET_TYPE_HALFCLOSE_READ:
case SOCKET_TYPE_HALFCLOSE_WRITE:
closing = 1;
case SOCKET_TYPE_CONNECTED:
if (s->protocol == PROTOCOL_TCP) {
si->type = SOCKET_INFO_TCP;
si->type = closing ? SOCKET_INFO_CLOSING : SOCKET_INFO_TCP;
if (getpeername(s->fd, &u.s, &slen) == 0) {
getname(&u, si->name, sizeof(si->name));
}

View File

@@ -14,6 +14,10 @@
#define SOCKET_UDP 6
#define SOCKET_WARNING 7
// Only for internal use
#define SOCKET_RST 8
#define SOCKET_MORE 9
struct socket_server;
struct socket_message {

View File

@@ -8,28 +8,36 @@
#ifndef USE_PTHREAD_LOCK
#ifdef __STDC_NO_ATOMICS__
#define atomic_flag_ int
#define ATOMIC_FLAG_INIT_ 0
#define atomic_flag_test_and_set_(ptr) __sync_lock_test_and_set(ptr, 1)
#define atomic_flag_clear_(ptr) __sync_lock_release(ptr)
struct spinlock {
int lock;
atomic_flag_ lock;
};
static inline void
spinlock_init(struct spinlock *lock) {
lock->lock = 0;
atomic_flag_ v = ATOMIC_FLAG_INIT_;
lock->lock = v;
}
static inline void
spinlock_lock(struct spinlock *lock) {
while (__sync_lock_test_and_set(&lock->lock,1)) {}
while (atomic_flag_test_and_set_(&lock->lock)) {}
}
static inline int
spinlock_trylock(struct spinlock *lock) {
return __sync_lock_test_and_set(&lock->lock,1) == 0;
return atomic_flag_test_and_set_(&lock->lock) == 0;
}
static inline void
spinlock_unlock(struct spinlock *lock) {
__sync_lock_release(&lock->lock);
atomic_flag_clear_(&lock->lock);
}
static inline void
@@ -37,6 +45,57 @@ spinlock_destroy(struct spinlock *lock) {
(void) lock;
}
#else // __STDC_NO_ATOMICS__
#include <stdatomic.h>
#define atomic_test_and_set_(ptr) atomic_exchange_explicit(ptr, 1, memory_order_acquire)
#define atomic_clear_(ptr) atomic_store_explicit(ptr, 0, memory_order_release);
#define atomic_load_relaxed_(ptr) atomic_load_explicit(ptr, memory_order_relaxed)
#if defined(__x86_64__)
#include <immintrin.h> // For _mm_pause
#define atomic_pause_() _mm_pause()
#else
#define atomic_pause_() ((void)0)
#endif
struct spinlock {
atomic_int lock;
};
static inline void
spinlock_init(struct spinlock *lock) {
atomic_init(&lock->lock, 0);
}
static inline void
spinlock_lock(struct spinlock *lock) {
for (;;) {
if (!atomic_test_and_set_(&lock->lock))
return;
while (atomic_load_relaxed_(&lock->lock))
atomic_pause_();
}
}
static inline int
spinlock_trylock(struct spinlock *lock) {
return !atomic_load_relaxed_(&lock->lock) &&
!atomic_test_and_set_(&lock->lock);
}
static inline void
spinlock_unlock(struct spinlock *lock) {
atomic_clear_(&lock->lock);
}
static inline void
spinlock_destroy(struct spinlock *lock) {
(void) lock;
}
#endif // __STDC_NO_ATOMICS__
#else
#include <pthread.h>

View File

@@ -50,4 +50,5 @@ skynet.start(function()
print("main step", f())
print("main step", f())
-- print("main thread time:", profile.stop())
print("close", coroutine.close(coroutine.create(main)))
end)

View File

@@ -25,11 +25,32 @@ local function http_test(protocol)
print(status)
end
local function http_stream_test()
for resp, stream in httpc.request_stream("GET", "http://baidu.com", "/") do
print("STATUS", stream.status)
for k,v in pairs(stream.header) do
print("HEADER",k,v)
end
print("BODY", resp)
end
end
local function http_head_test()
httpc.timeout = 100
local respheader = {}
local status = httpc.head("http://baidu.com", "/", respheader)
for k,v in pairs(respheader) do
print("HEAD", k, v)
end
end
local function main()
dns.server()
http_test("http")
http_stream_test()
http_head_test()
http_test("http")
if not pcall(require,"ltls.c") then
print "No ltls module, https is not supported"
else
@@ -41,3 +62,4 @@ skynet.start(function()
print(pcall(main))
skynet.exit()
end)

View File

@@ -168,6 +168,24 @@ function test_expire_index()
assert(false, "test expire index failed");
end
local function test_safe_batch_insert()
local ok, err, ret
local c = _create_client()
local db = c[db_name]
db.testcoll:drop()
local docs, length = {}, 10
for i = 1, length do
table.insert(docs, {test_key = i})
end
db.testcoll:safe_batch_insert(docs)
local ret = db.testcoll:find()
assert(length == ret:count(), "test safe batch insert failed")
end
skynet.start(function()
if username then
print("Test auth")
@@ -183,5 +201,7 @@ skynet.start(function()
test_runcommand()
print("Test expire index")
test_expire_index()
print("test safe batch insert")
test_safe_batch_insert()
print("mongodb test finish.");
end)