Compare commits

..

87 Commits

Author SHA1 Message Date
Cloud Wu
7f3ba76a41 update history 2014-06-02 21:10:58 +08:00
云风
4545ef149b Merge pull request #116 from cloudwu/dev
merge dev version
2014-06-02 21:05:15 +08:00
Cloud Wu
c130a93a91 bson : check string length 2014-06-02 11:53:55 +08:00
Cloud Wu
cd0a1a495e optimize timer 2014-05-28 11:30:39 +08:00
Cloud Wu
3597bb6d75 use jemalloc release 3.6.0 2014-05-27 21:04:22 +08:00
Cloud Wu
39c5e13d1a Merge branch 'dev' of github.com:cloudwu/skynet into dev 2014-05-26 13:42:07 +08:00
Cloud Wu
e1a72bd820 optimize message dispatch 2014-05-26 13:40:49 +08:00
Cloud Wu
f77482024e Merge branch 'master' into dev 2014-05-26 13:23:59 +08:00
Cloud Wu
866e59811e remove unused mq api 2014-05-26 13:23:57 +08:00
Cloud Wu
f3b1f5be51 bugfix: check globalmq push is complete 2014-05-26 13:18:09 +08:00
Cloud
0fcab96431 daemon is deprecarted in macosx 2014-05-23 16:52:24 +08:00
Cloud Wu
e765d3c305 don't unlock pidfile after write pid 2014-05-23 14:06:41 +08:00
Cloud Wu
8005b55ef5 add pidfile for daemon 2014-05-23 13:24:02 +08:00
Cloud Wu
1ce90b7d17 daemon mode 2014-05-23 10:54:54 +08:00
Cloud Wu
37ea7a025c single node mode 2014-05-22 22:44:20 +08:00
Cloud Wu
88700c2c90 cluster support 2014-05-22 21:04:32 +08:00
Cloud Wu
90cd967333 concurrence bugfix 2014-05-22 11:24:36 +08:00
Cloud Wu
1fbee1ac0b update lua-bson to fix the 32bit signed int bug 2014-05-21 11:21:01 +08:00
云风
d6a4c5cc59 Merge pull request #114 from tangyi/minor-fix
fix the missing of 'Welcome to skynet' in example
2014-05-20 10:39:33 +08:00
tangyi
66a0a6e1db fix the missing of 'Welcome to skynet' in example 2014-05-19 18:13:34 +08:00
云风
57f37a55ba Merge pull request #113 from cloudwu/dev
bugfix
2014-05-19 10:53:08 +08:00
Cloud Wu
84e8abc09c update history 2014-05-19 10:51:20 +08:00
云风
dbfa17be09 Merge pull request #112 from zhoukk/master
remove services when launch error.
2014-05-19 10:50:12 +08:00
zhoukk
19729576f6 lua service launch错误时,移除services中记录
lua service launch错误后,没有移除 launcher.lua里services中的服务记录,导致debug_console中的list mem等命令返回数据不正确
2014-05-18 12:36:38 +08:00
Cloud Wu
a5df196e92 use skynet_error for memory dump 2014-05-16 17:36:49 +08:00
Cloud Wu
a2ad158dbc update PTYPE_* macro 2014-05-16 15:01:25 +08:00
Cloud Wu
ff155c623e bugfix: stdin is 0 2014-05-16 14:31:27 +08:00
Cloud Wu
9fe6a0a954 Merge branch 'master' into dev 2014-05-15 19:33:30 +08:00
Cloud Wu
c2ed2f8603 bugfix. the harbor and master service need to rewrite in future 2014-05-15 19:32:54 +08:00
Cloud Wu
a67e561a64 Merge branch 'master' into dev 2014-05-14 11:55:53 +08:00
Cloud Wu
ef2da264f1 bugfix: watchdog socket data 2014-05-14 11:55:40 +08:00
Cloud Wu
b397c70c34 remove unused pingqueue 2014-05-13 17:00:43 +08:00
Cloud Wu
02f2f88b6d Fix issue #109 2014-05-13 13:35:37 +08:00
Cloud Wu
29917efad7 add MAX_SLOT_SIZE limit 2014-05-13 11:09:15 +08:00
Cloud Wu
9715ddd2c5 update history and release v0.2.0 2014-05-12 11:14:04 +08:00
云风
fe2b68686d Merge pull request #107 from cloudwu/dev
v0.2.0-rc
2014-05-12 11:11:49 +08:00
Cloud Wu
7dff50d21d merge pr 103 2014-05-12 11:04:58 +08:00
Cloud Wu
8d8cb17e0a launch all the bootstrap services in bootstrap.lua 2014-05-08 12:06:53 +08:00
Cloud Wu
4e00fce360 remove the limit of global queue size 2014-05-07 14:57:19 +08:00
Cloud Wu
2d19b6cac1 add comments for flags array 2014-05-07 13:51:32 +08:00
Cloud Wu
18c8a9d20c set global SERVICE_PATH for compatibility 2014-05-06 17:42:34 +08:00
Cloud Wu
82c7cbc2f0 update the history 2014-05-06 15:17:57 +08:00
Cloud Wu
915c28c522 drop message not report to skynet_error at all 2014-05-06 15:06:59 +08:00
Cloud Wu
a700abcdb3 remove skynet.watch, move simplemonitor to examples 2014-05-06 14:57:01 +08:00
Cloud Wu
0c4e2450a7 bugfix: ud is the second parm 2014-05-06 14:45:24 +08:00
Cloud Wu
6a6d9db54d remove blockcall 2014-05-06 13:41:44 +08:00
Cloud Wu
8c886023c9 avoid warning 2014-05-05 19:18:09 +08:00
Cloud Wu
465584dca2 update the notes 2014-04-30 20:05:07 +08:00
Cloud Wu
f3f46923d4 add release note 2014-04-30 19:58:56 +08:00
Cloud Wu
9033c5cdd9 bugfix: double check in SUB 2014-04-30 17:34:14 +08:00
Cloud Wu
5d0da82d9a We don't need launch multicastd in bootstrap 2014-04-30 14:59:28 +08:00
Cloud Wu
091f91ede3 add test for multicast 2014-04-30 12:29:11 +08:00
Cloud Wu
4a92dfaa65 multicast dispatch map should be weak 2014-04-30 12:23:15 +08:00
Cloud Wu
1c2654e117 change multicast api 2014-04-30 12:17:27 +08:00
Cloud Wu
5ce65055d2 multicast support remote publish/subscribe 2014-04-29 21:56:58 +08:00
Cloud Wu
fa6191d9ed preload script example 2014-04-29 20:06:59 +08:00
Cloud Wu
12f46d21fe add preload 2014-04-29 20:04:06 +08:00
Cloud Wu
d16159c166 add lua loader 2014-04-29 19:59:04 +08:00
Cloud Wu
a4a21bd793 move lualib/snax_* to lualib/snax/ 2014-04-29 17:05:27 +08:00
Cloud Wu
f161dbb0ea move datacenter api out of skynet 2014-04-29 15:57:11 +08:00
Cloud Wu
61e3904de8 merge servicecache branch 2014-04-29 14:51:34 +08:00
Cloud Wu
4d536f7f86 add datacenter 2014-04-29 14:20:22 +08:00
Cloud Wu
f6e67b7493 add bootstrap script 2014-04-29 13:44:30 +08:00
Cloud Wu
be948c6675 multicast.bind channel 2014-04-28 21:10:24 +08:00
Cloud Wu
0a40d9c5c6 local multicast support 2014-04-28 21:10:24 +08:00
Cloud Wu
34771ff7c7 use malloc symbol for hook 2014-04-28 21:09:59 +08:00
Cloud Wu
c17ec76e94 snax now use independent protocol PTYPE_SNAX 11 2014-04-28 15:52:54 +08:00
Cloud Wu
45df4f2e71 rebase v0.1.1 and remove malloc_inithook 2014-04-28 15:41:40 +08:00
Cloud Wu
4f10610e26 debug command reload and timing removed 2014-04-28 15:32:46 +08:00
Cloud Wu
904308bc72 add snax command to debug console 2014-04-28 15:31:38 +08:00
Cloud Wu
bad9a5fc54 use pthread_getspecific instead of __thread 2014-04-28 15:31:38 +08:00
Cloud Wu
bd3aa3f3c5 remove service_lua.h 2014-04-28 15:29:15 +08:00
Cloud Wu
2eb3174b0b support abs path for LUA_CLIB_PATH 2014-04-28 15:29:15 +08:00
Cloud Wu
518017bd34 preload support 2014-04-28 15:29:14 +08:00
Cloud Wu
1a169ec413 remove malloc hook when disable jemalloc 2014-04-28 15:23:42 +08:00
云风
009e15556d Merge pull request #100 from cloudwu/notusejemalloc
fix makefile for better platform support
2014-04-28 14:34:47 +08:00
Cloud Wu
1170acc792 fix makefile for better platform support 2014-04-28 14:31:51 +08:00
Cloud
37cb126812 Not use jemalloc on macosx, See Issue #99 2014-04-28 13:48:50 +08:00
Cloud Wu
aa65dac9ed Rewrite socketchannel, make it clear. 2014-04-25 11:53:30 +08:00
云风
672e218311 Merge pull request #98 from cloudwu/bugfix
Bugfix: socket channel reconnect
2014-04-24 23:14:21 +08:00
Cloud Wu
f7783d3881 bugfix: socket channel reconnect 2014-04-24 23:08:35 +08:00
Cloud Wu
b65757d8bf Clear request queue when channel disconnected 2014-04-24 19:59:50 +08:00
Cloud Wu
8840c0fe3e service_mgr support list, add a debug command to debug_console 2014-04-19 20:45:40 +08:00
Cloud Wu
ddd0b13013 add snax.uniqueservice (and others) 2014-04-19 17:53:50 +08:00
Cloud Wu
ba27feee35 make service mgr simple 2014-04-19 17:11:51 +08:00
Cloud Wu
93cb091894 assert service name is string 2014-04-19 16:51:12 +08:00
Cloud Wu
2d56a2215a optimize global unique service query 2014-04-19 16:47:52 +08:00
73 changed files with 2772 additions and 1203 deletions

1
.gitignore vendored
View File

@@ -1,6 +1,7 @@
*.o
*.a
skynet
skynet.pid
3rd/lua/lua
3rd/lua/luac
cservice

50
HISTORY.md Normal file
View File

@@ -0,0 +1,50 @@
v0.3.0 (2014-6-2)
-----------
* Add cluster support
* Add single node mode
* Add daemon mode
* Bugfix: update lua-bson (signed 32bit int bug / check string length)
* Optimize timer
* Simplify message queue and optimize message dispatch
* Use jemalloc release 3.6.0
v0.2.1 (2014-5-19)
-----------
* Bugfix: check all the events already read after socket close
* Bugfix: socket data in gate service
* Bugfix: boundary problem in harbor service
* Bugfix: stdin handle is 0
v0.2.0 (2014-5-12)
-----------
* Rewrite malloc hook , use `pthread_getspecific` instead of `__thread` to get current service handle.
* Optimize global unique service query, rewrite `service_mgr` .
* Add some snax api, snax.uniqueservice (etc.) , use independent protocol `PTYPE_SNAX` .
* Add bootstrap lua script , remove some code in C .
* Use a lua loader to load lua service code (and set the lua environment), remove some code in C.
* Support preload a file before each lua serivce start.
* Add datacenter serivce.
* Add multicast api.
* Remove skynet.blockcall , simplify the implement of message queue.
* When dropping message queue (at service exit) , dispatcher will post an error back to the source of each message.
* Remove skynet.watch , monitor is not necessary for watching skynet.call . so simplemonitor.lua is move to examples.
* Remove the limit of global queue size (64K actived service limit before).
* Refactoring `skynet_command`.
v0.1.1 (2014-4-28)
------------------
* Socket channel should clear request queue when reconnect.
* Fix the issue that socket close may block the coroutine.
* Fix the issue that jemalloc api may crash on macosx (disable jemalloc on macosx).
v0.1.0 (2014-4-23)
------------------
* First release version.
First public version (2012-8-1)
------------------
* Make skynet from a private project to public.

View File

@@ -5,7 +5,7 @@ CSERVICE_PATH ?= cservice
SKYNET_BUILD_PATH ?= .
CFLAGS = -g -O2 -Wall -I$(LUA_INC) $(MYCFLAGS)
CFLAGS = -g -O2 -Wall -I$(LUA_INC) $(MYCFLAGS)
# lua
@@ -22,8 +22,11 @@ JEMALLOC_STATICLIB := 3rd/jemalloc/lib/libjemalloc_pic.a
JEMALLOC_INC := 3rd/jemalloc/include/jemalloc
all : jemalloc
.PHONY : jemalloc
MALLOC_STATICLIB := $(JEMALLOC_STATICLIB)
$(JEMALLOC_STATICLIB) : 3rd/jemalloc/Makefile
cd 3rd/jemalloc && $(MAKE) CC=$(CC)
@@ -33,25 +36,27 @@ $(JEMALLOC_STATICLIB) : 3rd/jemalloc/Makefile
3rd/jemalloc/Makefile : | 3rd/jemalloc/autogen.sh
cd 3rd/jemalloc && ./autogen.sh --with-jemalloc-prefix=je_ --disable-valgrind
jemalloc : $(JEMALLOC_STATICLIB)
jemalloc : $(MALLOC_STATICLIB)
# skynet
CSERVICE = snlua logger gate master harbor
LUA_CLIB = skynet socketdriver int64 bson mongo md5 netpack cjson clientsocket memory profile
CSERVICE = snlua logger gate master harbor dummy
LUA_CLIB = skynet socketdriver int64 bson mongo md5 netpack \
cjson clientsocket memory profile multicast \
cluster
SKYNET_SRC = skynet_main.c skynet_handle.c skynet_module.c skynet_mq.c \
skynet_server.c skynet_start.c skynet_timer.c skynet_error.c \
skynet_harbor.c skynet_env.c skynet_monitor.c skynet_socket.c socket_server.c \
malloc_hook.c
malloc_hook.c skynet_daemon.c
all : \
$(SKYNET_BUILD_PATH)/skynet \
$(foreach v, $(CSERVICE), $(CSERVICE_PATH)/$(v).so) \
$(foreach v, $(LUA_CLIB), $(LUA_CLIB_PATH)/$(v).so)
$(SKYNET_BUILD_PATH)/skynet : $(foreach v, $(SKYNET_SRC), skynet-src/$(v)) $(LUA_LIB) $(JEMALLOC_STATICLIB)
$(CC) $(CFLAGS) -o $@ $^ -Iskynet-src -I$(JEMALLOC_INC) $(LDFLAGS) $(EXPORT) $(LIBS)
$(SKYNET_BUILD_PATH)/skynet : $(foreach v, $(SKYNET_SRC), skynet-src/$(v)) $(LUA_LIB) $(MALLOC_STATICLIB)
$(CC) $(CFLAGS) -o $@ $^ -Iskynet-src -I$(JEMALLOC_INC) $(LDFLAGS) $(EXPORT) $(SKYNET_LIBS) $(SKYNET_DEFINES)
$(LUA_CLIB_PATH) :
mkdir $(LUA_CLIB_PATH)
@@ -88,7 +93,7 @@ $(LUA_CLIB_PATH)/netpack.so : lualib-src/lua-netpack.c | $(LUA_CLIB_PATH)
$(CC) $(CFLAGS) $(SHARED) $^ -Iskynet-src -o $@
$(LUA_CLIB_PATH)/cjson.so : | $(LUA_CLIB_PATH)
cd 3rd/lua-cjson && $(MAKE) LUA_INCLUDE_DIR=../../$(LUA_INC) CC=$(CC) CJSON_LDFLAGS="$(SHARED)" && cp cjson.so ../../$@
cd 3rd/lua-cjson && $(MAKE) LUA_INCLUDE_DIR=../../$(LUA_INC) CC=$(CC) CJSON_LDFLAGS="$(SHARED)" && cd ../.. && cp 3rd/lua-cjson/cjson.so $@
$(LUA_CLIB_PATH)/clientsocket.so : lualib-src/lua-clientsocket.c | $(LUA_CLIB_PATH)
$(CC) $(CFLAGS) $(SHARED) $^ -o $@ -lpthread
@@ -99,6 +104,12 @@ $(LUA_CLIB_PATH)/memory.so : lualib-src/lua-memory.c | $(LUA_CLIB_PATH)
$(LUA_CLIB_PATH)/profile.so : lualib-src/lua-profile.c | $(LUA_CLIB_PATH)
$(CC) $(CFLAGS) $(SHARED) $^ -o $@
$(LUA_CLIB_PATH)/multicast.so : lualib-src/lua-multicast.c | $(LUA_CLIB_PATH)
$(CC) $(CFLAGS) $(SHARED) -Iskynet-src $^ -o $@
$(LUA_CLIB_PATH)/cluster.so : lualib-src/lua-cluster.c | $(LUA_CLIB_PATH)
$(CC) $(CFLAGS) $(SHARED) -Iskynet-src $^ -o $@
clean :
rm -f $(SKYNET_BUILD_PATH)/skynet $(CSERVICE_PATH)/*.so $(LUA_CLIB_PATH)/*.so

View File

@@ -8,7 +8,7 @@ local CMD = {}
local client_fd
local function send_client(v)
socket.write(client_fd, netpack.pack(jsonpack.pack(0,v)))
socket.write(client_fd, netpack.pack(jsonpack.pack(0, {true, v})))
end
local function response_client(session,v)

9
examples/cluster1.lua Normal file
View File

@@ -0,0 +1,9 @@
local skynet = require "skynet"
local cluster = require "cluster"
skynet.start(function()
skynet.newservice("simpledb")
print(skynet.call("SIMPLEDB", "lua", "SET", "a", "foobar"))
print(skynet.call("SIMPLEDB", "lua", "GET", "a"))
cluster.open(2528)
end)

6
examples/cluster2.lua Normal file
View File

@@ -0,0 +1,6 @@
local skynet = require "skynet"
local cluster = require "cluster"
skynet.start(function()
print(cluster.call("db", "SIMPLEDB", "GET", "a"))
end)

1
examples/clustername.lua Normal file
View File

@@ -0,0 +1 @@
db = "127.0.0.1:2528"

View File

@@ -4,8 +4,12 @@ logger = nil
harbor = 1
address = "127.0.0.1:2526"
master = "127.0.0.1:2013"
start = "main"
start = "main" -- main script
bootstrap = "snlua bootstrap" -- The service for bootstrap
standalone = "0.0.0.0:2013"
luaservice = root.."service/?.lua;"..root.."test/?.lua;"..root.."examples/?.lua"
lualoader = "lualib/loader.lua"
-- preload = "./examples/preload.lua" -- run preload.lua before every lua service run
snax = root.."examples/?.lua;"..root.."test/?.lua"
cpath = root.."cservice/?.so"
-- daemon = "./skynet.pid"

9
examples/config.c1 Normal file
View File

@@ -0,0 +1,9 @@
thread = 8
logger = nil
harbor = 0
start = "cluster1"
bootstrap = "snlua bootstrap" -- The service for bootstrap
luaservice = "./service/?.lua;./test/?.lua;./examples/?.lua"
lualoader = "lualib/loader.lua"
cpath = "./cservice/?.so"
cluster = "./examples/clustername.lua"

9
examples/config.c2 Normal file
View File

@@ -0,0 +1,9 @@
thread = 8
logger = nil
harbor = 0
start = "cluster2"
bootstrap = "snlua bootstrap" -- The service for bootstrap
luaservice = "./service/?.lua;./test/?.lua;./examples/?.lua"
lualoader = "lualib/loader.lua"
cpath = "./cservice/?.so"
cluster = "./examples/clustername.lua"

14
examples/config.mc Normal file
View File

@@ -0,0 +1,14 @@
root = "./"
thread = 8
logger = nil
harbor = 2
address = "127.0.0.1:2527"
master = "127.0.0.1:2013"
start = "testmulticast2" -- main script
bootstrap = "snlua bootstrap" -- The service for bootstrap
--standalone = "0.0.0.0:2013"
luaservice = root.."service/?.lua;"..root.."test/?.lua;"..root.."examples/?.lua"
lualoader = "lualib/loader.lua"
-- preload = "./examples/preload.lua" -- run preload.lua before every lua service run
snax = root.."examples/?.lua;"..root.."test/?.lua"
cpath = root.."cservice/?.so"

View File

@@ -7,3 +7,4 @@ address = "127.0.0.1:2527"
master = "127.0.0.1:2013"
start = "main_log"
luaservice ="./service/?.lua;./test/?.lua;./examples/?.lua"
snax = "./examples/?.lua;./test/?.lua"

View File

@@ -4,10 +4,8 @@ local max_client = 64
skynet.start(function()
print("Server start")
local service = skynet.newservice("service_mgr")
skynet.monitor "simplemonitor"
local console = skynet.newservice("console")
-- skynet.newservice("debug_console",8000)
skynet.newservice("debug_console",8000)
skynet.newservice("simpledb")
local watchdog = skynet.newservice("watchdog")
skynet.call(watchdog, "lua", "start", {

5
examples/preload.lua Normal file
View File

@@ -0,0 +1,5 @@
-- This file will execute before every lua service start
-- See config
print("PRELOAD", ...)

View File

@@ -12,11 +12,10 @@ skynet.register_protocol {
local w = service_map[address]
if w then
for watcher in pairs(w) do
skynet.send(watcher, "error", address)
skynet.redirect(watcher, address, "error", "")
end
service_map[address] = false
end
print(string.format("[:%x] exit", address))
end
}

View File

@@ -1,5 +1,3 @@
#include "skynet_malloc.h"
#include <lua.h>
#include <lauxlib.h>
@@ -73,7 +71,7 @@ struct bson_reader {
static inline void
bson_destroy(struct bson *b) {
if (b->ptr != b->buffer) {
skynet_free(b->ptr);
free(b->ptr);
}
}
@@ -93,10 +91,10 @@ bson_reserve(struct bson *b, int sz) {
} while (b->cap <= b->size + sz);
if (b->ptr == b->buffer) {
b->ptr = skynet_malloc(b->cap);
b->ptr = malloc(b->cap);
memcpy(b->ptr, b->buffer, b->size);
} else {
b->ptr = skynet_realloc(b->ptr, b->cap);
b->ptr = realloc(b->ptr, b->cap);
}
}
@@ -258,13 +256,13 @@ append_key(struct bson *bs, int type, const char *key, size_t sz) {
static void
append_number(struct bson *bs, lua_State *L, const char *key, size_t sz) {
lua_Integer i = lua_tointeger(L, -1);
int64_t i = lua_tointeger(L, -1);
lua_Number d = lua_tonumber(L,-1);
if (i != d) {
append_key(bs, BSON_REAL, key, sz);
write_double(bs, d);
} else {
int si = (int64_t)i >> 32;
int si = i >> 31;
if (si == 0 || si == -1) {
append_key(bs, BSON_INT32, key, sz);
write_int32(bs, i);
@@ -459,7 +457,7 @@ pack_dict(lua_State *L, struct bson *b, bool isarray) {
}
static void
pack_sorted_dict(lua_State *L, struct bson *b, int n) {
pack_ordered_dict(lua_State *L, struct bson *b, int n) {
int length = reserve_length(b);
int i;
for (i=0;i<n;i+=2) {
@@ -534,6 +532,9 @@ unpack_dict(lua_State *L, struct bson_reader *br, bool array) {
break;
case BSON_STRING: {
int sz = read_int32(L, &t);
if (sz == 0) {
luaL_error(L, "Invalid bson string , length = 0");
}
lua_pushlstring(L, read_bytes(L, &t, sz), sz-1);
break;
}
@@ -806,6 +807,9 @@ lreplace(lua_State *L) {
write_int64(&b, i);
break;
}
default:
luaL_error(L, "Can't replace type %d", type);
break;
}
return 0;
}
@@ -865,7 +869,7 @@ lencode_order(lua_State *L) {
if (n%2 != 0) {
return luaL_error(L, "Invalid ordered dict");
}
pack_sorted_dict(L, &b, n);
pack_ordered_dict(L, &b, n);
lua_settop(L,1);
void * ud = lua_newuserdata(L, b.size);
memcpy(ud, b.ptr, b.size);
@@ -1190,3 +1194,4 @@ luaopen_bson(lua_State *L) {
return 1;
}

205
lualib-src/lua-cluster.c Normal file
View File

@@ -0,0 +1,205 @@
#include <lua.h>
#include <lauxlib.h>
#include <string.h>
#include "skynet.h"
/*
uint32_t/string addr
uint32_t/session session
lightuserdata msg
uint32_t sz
return
string request
uint32_t next_session
*/
#define TEMP_LENGTH 0x10002
static void
fill_uint32(uint8_t * buf, uint32_t n) {
buf[0] = n & 0xff;
buf[1] = (n >> 8) & 0xff;
buf[2] = (n >> 16) & 0xff;
buf[3] = (n >> 24) & 0xff;
}
static void
fill_header(lua_State *L, uint8_t *buf, int sz, void *msg) {
if (sz >= 0x10000) {
skynet_free(msg);
luaL_error(L, "request message is too long %d", sz);
}
buf[0] = (sz >> 8) & 0xff;
buf[1] = sz & 0xff;
}
static void
packreq_number(lua_State *L, int session, void * msg, size_t sz) {
uint32_t addr = lua_tounsigned(L,1);
uint8_t buf[TEMP_LENGTH];
fill_header(L, buf, sz+9, msg);
buf[2] = 0;
fill_uint32(buf+3, addr);
fill_uint32(buf+7, (uint32_t)session);
memcpy(buf+11,msg,sz);
lua_pushlstring(L, (const char *)buf, sz+11);
}
static void
packreq_string(lua_State *L, int session, void * msg, size_t sz) {
size_t namelen = 0;
const char *name = lua_tolstring(L, 1, &namelen);
if (name == NULL || namelen < 1 || namelen > 255) {
skynet_free(msg);
luaL_error(L, "name is too long %s", name);
}
uint8_t buf[TEMP_LENGTH];
fill_header(L, buf, sz+5+namelen, msg);
buf[2] = (uint8_t)namelen;
memcpy(buf+3, name, namelen);
fill_uint32(buf+3+namelen, (uint32_t)session);
memcpy(buf+7+namelen,msg,sz);
lua_pushlstring(L, (const char *)buf, sz+7+namelen);
}
static int
lpackrequest(lua_State *L) {
void *msg = lua_touserdata(L,3);
if (msg == NULL) {
return luaL_error(L, "Invalid request message");
}
size_t sz = luaL_checkunsigned(L,4);
int session = luaL_checkinteger(L,2);
if (session <= 0) {
return luaL_error(L, "Invalid request session %d", session);
}
int addr_type = lua_type(L,1);
if (addr_type == LUA_TNUMBER) {
packreq_number(L, session, msg, sz);
} else {
packreq_string(L, session, msg, sz);
}
if (++session < 0) {
session = 1;
}
skynet_free(msg);
lua_pushinteger(L, session);
return 2;
}
/*
string packed message
return
uint32_t or string addr
int session
string msg
*/
static inline uint32_t
unpack_uint32(const uint8_t * buf) {
return buf[0] | buf[1]<<8 | buf[2]<<16 | buf[3]<<24;
}
static int
unpackreq_number(lua_State *L, const uint8_t * buf, size_t sz) {
if (sz < 9) {
return luaL_error(L, "Invalid cluster message");
}
uint32_t address = unpack_uint32(buf+1);
uint32_t session = unpack_uint32(buf+5);
lua_pushunsigned(L, address);
lua_pushunsigned(L, session);
lua_pushlstring(L, (const char *)buf+9, sz-9);
return 3;
}
static int
unpackreq_string(lua_State *L, const uint8_t * buf, size_t sz) {
size_t namesz = buf[0];
if (sz < namesz + 5) {
return luaL_error(L, "Invalid cluster message");
}
lua_pushlstring(L, (const char *)buf+1, namesz);
uint32_t session = unpack_uint32(buf + namesz + 1);
lua_pushunsigned(L, session);
lua_pushlstring(L, (const char *)buf+1+namesz+4, sz - namesz - 5);
return 3;
}
static int
lunpackrequest(lua_State *L) {
size_t sz;
const char *msg = luaL_checklstring(L,1,&sz);
if (msg[0] == 0) {
return unpackreq_number(L, (const uint8_t *)msg, sz);
} else {
return unpackreq_string(L, (const uint8_t *)msg, sz);
}
}
/*
int session
lightuserdata msg
int sz
return string response
*/
static int
lpackresponse(lua_State *L) {
uint32_t session = luaL_checkunsigned(L,1);
void * msg = lua_touserdata(L,2);
size_t sz = luaL_checkunsigned(L, 3);
uint8_t buf[TEMP_LENGTH];
fill_header(L, buf, sz+4, msg);
fill_uint32(buf+2, session);
memcpy(buf+6,msg,sz);
skynet_free(msg);
lua_pushlstring(L, (const char *)buf, sz+6);
return 1;
}
/*
string packed response
return integer session
boolean ok
string msg
*/
static int
lunpackresponse(lua_State *L) {
size_t sz;
const char * buf = luaL_checklstring(L, 1, &sz);
if (sz < 4) {
return 0;
}
uint32_t session = unpack_uint32((const uint8_t *)buf);
lua_pushunsigned(L, session);
lua_pushboolean(L, 1);
lua_pushlstring(L, buf+4, sz-4);
return 3;
}
int
luaopen_cluster_c(lua_State *L) {
luaL_Reg l[] = {
{ "packrequest", lpackrequest },
{ "unpackrequest", lunpackrequest },
{ "packresponse", lpackresponse },
{ "unpackresponse", lunpackresponse },
{ NULL, NULL },
};
luaL_checkversion(L);
luaL_newlib(L,l);
return 1;
}

View File

@@ -55,7 +55,7 @@ little_endian(uint32_t v) {
typedef void * document;
static inline uint32_t
get_length(const document buffer) {
get_length(document buffer) {
union {
uint32_t v;
uint8_t b[4];
@@ -243,7 +243,7 @@ op_reply(lua_State *L) {
lua_pushlightuserdata(L, (void *)doc);
lua_rawseti(L, 2, i);
int32_t doc_len = get_length((const document)doc);
int32_t doc_len = get_length((document)doc);
doc += doc_len;
sz -= doc_len;

167
lualib-src/lua-multicast.c Normal file
View File

@@ -0,0 +1,167 @@
#include "skynet.h"
#include <lua.h>
#include <lauxlib.h>
#include <stdint.h>
#include <string.h>
struct mc_package {
int reference;
uint32_t size;
void *data;
};
static int
pack(lua_State *L, void *data, size_t size) {
struct mc_package * pack = skynet_malloc(sizeof(struct mc_package));
pack->reference = 0;
pack->size = (uint32_t)size;
pack->data = data;
struct mc_package ** ret = skynet_malloc(sizeof(*ret));
*ret = pack;
lua_pushlightuserdata(L, ret);
lua_pushinteger(L, sizeof(ret));
return 2;
}
/*
lightuserdata
integer size
return lightuserdata, sizeof(struct mc_package *)
*/
static int
mc_packlocal(lua_State *L) {
void * data = lua_touserdata(L, 1);
size_t size = luaL_checkunsigned(L, 2);
if (size != (uint32_t)size) {
return luaL_error(L, "Size should be 32bit integer");
}
return pack(L, data, size);
}
/*
lightuserdata
integer size
return lightuserdata, sizeof(struct mc_package *)
*/
static int
mc_packremote(lua_State *L) {
void * data = lua_touserdata(L, 1);
size_t size = luaL_checkunsigned(L, 2);
if (size != (uint32_t)size) {
return luaL_error(L, "Size should be 32bit integer");
}
void * msg = skynet_malloc(size);
memcpy(msg, data, size);
return pack(L, msg, size);
}
static int
mc_packstring(lua_State *L) {
size_t size;
const char * msg = luaL_checklstring(L, 1, &size);
if (size != (uint32_t)size) {
return luaL_error(L, "string is too long");
}
void * data = skynet_malloc(size);
memcpy(data, msg, size);
return pack(L, data, size);
}
/*
lightuserdata struct mc_package **
integer size (must be sizeof(struct mc_package *)
return package, lightuserdata, size
*/
static int
mc_unpacklocal(lua_State *L) {
struct mc_package ** pack = lua_touserdata(L,1);
int sz = luaL_checkinteger(L,2);
if (sz != sizeof(*pack)) {
return luaL_error(L, "Invalid multicast package size %d", sz);
}
lua_settop(L, 1);
lua_pushlightuserdata(L, (*pack)->data);
lua_pushunsigned(L, (*pack)->size);
return 3;
}
/*
lightuserdata struct mc_package **
integer reference
*/
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) {
return luaL_error(L, "Can't bind a multicast package more than once");
}
(*pack)->reference = ref;
return 0;
}
/*
lightuserdata struct mc_package **
*/
static int
mc_closelocal(lua_State *L) {
struct mc_package **ptr = lua_touserdata(L,1);
struct mc_package *pack = *ptr;
int ref = __sync_sub_and_fetch(&pack->reference, 1);
if (ref <= 0) {
skynet_free(pack->data);
skynet_free(pack);
if (ref < 0) {
return luaL_error(L, "Invalid multicast package reference %d", ref);
}
}
return 0;
}
/*
lightuserdata struct mc_package **
return lightuserdata/size
*/
static int
mc_remote(lua_State *L) {
struct mc_package **ptr = lua_touserdata(L,1);
struct mc_package *pack = *ptr;
lua_pushlightuserdata(L, pack->data);
lua_pushunsigned(L, pack->size);
skynet_free(pack);
return 2;
}
static int
mc_nextid(lua_State *L) {
uint32_t id = luaL_checkunsigned(L, 1);
id += 256;
lua_pushunsigned(L, id);
return 1;
}
int
luaopen_multicast_c(lua_State *L) {
luaL_Reg l[] = {
{ "pack", mc_packlocal },
{ "unpack", mc_unpacklocal },
{ "bind", mc_bindrefer },
{ "close", mc_closelocal },
{ "remote", mc_remote },
{ "packstring", mc_packstring },
{ "packremote", mc_packremote },
{ "nextid", mc_nextid },
{ NULL, NULL },
};
luaL_checkversion(L);
luaL_newlib(L,l);
return 1;
}

View File

@@ -1,6 +1,5 @@
#include "skynet.h"
#include "lua-seri.h"
#include "service_lua.h"
#define KNRM "\x1B[0m"
#define KRED "\x1B[31m"
@@ -11,18 +10,36 @@
#include <string.h>
#include <assert.h>
struct snlua {
lua_State * L;
struct skynet_context * ctx;
const char * preload;
};
static int
traceback (lua_State *L) {
const char *msg = lua_tostring(L, 1);
if (msg)
luaL_traceback(L, L, msg, 1);
else {
lua_pushliteral(L, "(no error message)");
}
return 1;
}
static int
_cb(struct skynet_context * context, void * ud, int type, int session, uint32_t source, const void * msg, size_t sz) {
lua_State *L = ud;
int trace = 1;
int r;
int top = lua_gettop(L);
if (top == 1) {
if (top == 0) {
lua_pushcfunction(L, traceback);
lua_rawgetp(L, LUA_REGISTRYINDEX, _cb);
} else {
assert(top == 2);
lua_pushvalue(L,2);
}
lua_pushvalue(L,2);
lua_pushinteger(L, type);
lua_pushlightuserdata(L, (void *)msg);
@@ -290,15 +307,14 @@ luaopen_skynet_c(lua_State *L) {
{ NULL, NULL },
};
lua_getfield(L, LUA_REGISTRYINDEX, "skynet_lua");
struct snlua *lua = lua_touserdata(L,-1);
if (lua == NULL || lua->ctx == NULL) {
luaL_newlibtable(L, l);
lua_getfield(L, LUA_REGISTRYINDEX, "skynet_context");
struct skynet_context *ctx = lua_touserdata(L,-1);
if (ctx == NULL) {
return luaL_error(L, "Init skynet context first");
}
assert(lua->L == L);
luaL_newlibtable(L, l);
lua_pushlightuserdata(L, lua->ctx);
luaL_setfuncs(L,l,1);
return 1;

View File

@@ -10,7 +10,6 @@
#include <lauxlib.h>
#include "skynet_socket.h"
#include "service_lua.h"
#define BACKLOG 32
// 2 ** 12 == 4096
@@ -192,7 +191,7 @@ pop_lstring(lua_State *L, struct socket_buffer *sb, int sz, int skip) {
/*
userdata send_buffer
table pool
integer sz
integer sz or string (big endian)
*/
static int
lpopbuffer(lua_State *L) {
@@ -201,7 +200,23 @@ lpopbuffer(lua_State *L) {
return luaL_error(L, "Need buffer object at param 1");
}
luaL_checktype(L,2,LUA_TTABLE);
int sz = luaL_checkinteger(L,3);
int type = lua_type(L,3);
int sz;
if (type == LUA_TNUMBER) {
sz = lua_tointeger(L,3);
} else {
size_t len;
const uint8_t * s = (const uint8_t *)luaL_checklstring(L, 3, &len);
if (len > 4 || len < 1) {
return luaL_error(L, "Invalid read %s", s);
}
int i;
sz = 0;
for (i=0;i<(int)len;i++) {
sz <<= 8;
sz |= s[i];
}
}
if (sb->size < sz || sz == 0) {
lua_pushnil(L);
} else {
@@ -485,15 +500,12 @@ luaopen_socketdriver(lua_State *L) {
{ "start", lstart },
{ NULL, NULL },
};
lua_getfield(L, LUA_REGISTRYINDEX, "skynet_lua");
struct snlua *lua = lua_touserdata(L,-1);
if (lua == NULL || lua->ctx == NULL) {
lua_getfield(L, LUA_REGISTRYINDEX, "skynet_context");
struct skynet_context *ctx = lua_touserdata(L,-1);
if (ctx == NULL) {
return luaL_error(L, "Init skynet context first");
}
// assert(lua->L == L);
lua_pop(L,1);
lua_pushlightuserdata(L, lua->ctx);
luaL_setfuncs(L,l2,1);
return 1;

19
lualib/cluster.lua Normal file
View File

@@ -0,0 +1,19 @@
local skynet = require "skynet"
local clusterd
local cluster = {}
function cluster.call(node, address, ...)
-- skynet.pack(...) will free by cluster.c.packrequest
return skynet.call(clusterd, "lua", "req", node, address, skynet.pack(...))
end
function cluster.open(port)
skynet.call(clusterd, "lua", "listen", "0.0.0.0", port)
end
skynet.init(function()
clusterd = skynet.uniqueservice("clusterd")
end)
return cluster

14
lualib/datacenter.lua Normal file
View File

@@ -0,0 +1,14 @@
local skynet = require "skynet"
local datacenter = {}
function datacenter.get(...)
return skynet.call("DATACENTER", "lua", "QUERY", ...)
end
function datacenter.set(...)
return skynet.call("DATACENTER", "lua", "UPDATE", ...)
end
return datacenter

46
lualib/loader.lua Normal file
View File

@@ -0,0 +1,46 @@
local args = {}
for word in string.gmatch(..., "%S+") do
table.insert(args, word)
end
local main, pattern
local err = {}
for pat in string.gmatch(LUA_SERVICE, "([^;]+);*") do
local filename = string.gsub(pat, "?", args[1])
local f, msg = loadfile(filename)
if not f then
table.insert(err, msg)
else
pattern = pat
main = f
break
end
end
if not main then
error(table.concat(err, "\n"))
end
LUA_SERVICE = nil
package.path , LUA_PATH = LUA_PATH
package.cpath , LUA_CPATH = LUA_CPATH
local service_path = string.match(pattern, "(.*/)[^/?]+$")
if service_path then
service_path = string.gsub(service_path, "?", args[1])
package.path = service_path .. "?.lua;" .. package.path
SERVICE_PATH = service_path
else
local p = string.match(pattern, "(.*/).+$")
SERVICE_PATH = p
end
if LUA_PRELOAD then
local f = assert(loadfile(LUA_PRELOAD))
f(table.unpack(args))
LUA_PRELOAD = nil
end
main(select(2, table.unpack(args)))

View File

@@ -39,9 +39,7 @@ local client_meta = {
return "[mongo client : " .. self.host .. port_string .."]"
end,
__gc = function(self)
self:disconnect()
end
-- DO NOT need disconnect, because channel will shutdown during gc
}
local mongo_db = {}

102
lualib/multicast.lua Normal file
View File

@@ -0,0 +1,102 @@
local skynet = require "skynet"
local mc = require "multicast.c"
local multicastd
local multicast = {}
local dispatch = setmetatable({} , {__mode = "kv" })
local chan = {}
local chan_meta = {
__index = chan,
__gc = unsubscribe,
__tostring = function (self)
return string.format("[Multicast:%x]",self.channel)
end,
}
local function default_conf(conf)
conf = conf or {}
conf.pack = conf.pack or skynet.pack
conf.unpack = conf.unpack or skynet.unpack
return conf
end
function multicast.new(conf)
assert(multicastd, "Init first")
local self = {}
conf = conf or self
self.channel = conf.channel
if self.channel == nil then
self.channel = skynet.call(multicastd, "lua", "NEW")
end
self.__pack = conf.pack or skynet.pack
self.__unpack = conf.unpack or skynet.unpack
self.__dispatch = conf.dispatch
return setmetatable(self, chan_meta)
end
function chan:delete()
local c = assert(self.channel)
skynet.send(multicastd, "lua", "DEL", c)
self.channel = nil
self.__subscribe = nil
end
function chan:publish(...)
local c = assert(self.channel)
skynet.call(multicastd, "lua", "PUB", c, mc.pack(self.__pack(...)))
end
function chan:subscribe()
local c = assert(self.channel)
if self.__subscribe then
-- already subscribe
return
end
skynet.call(multicastd, "lua", "SUB", c)
self.__subscribe = true
dispatch[c] = self
end
function chan:unsubscribe()
if not self.__subscribe then
-- already unsubscribe
return
end
local c = assert(self.channel)
skynet.send(multicastd, "lua", "USUB", c)
self.__subscribe = nil
end
local function dispatch_subscribe(channel, source, pack, msg, sz)
local self = dispatch[channel]
if not self then
mc.close(pack)
error ("Unknown channel " .. channel)
end
if self.__subscribe then
local ok, err = pcall(self.__dispatch, self, source, self.__unpack(msg, sz))
mc.close(pack)
assert(ok, err)
else
-- maybe unsubscribe first, but the message is send out. drop the message unneed
mc.close(pack)
end
end
local function init()
multicastd = skynet.uniqueservice "multicastd"
skynet.register_protocol {
name = "multicast",
id = skynet.PTYPE_MULTICAST,
unpack = mc.unpack,
dispatch = dispatch_subscribe,
}
end
skynet.init(init, "multicast")
return multicast

View File

@@ -9,9 +9,7 @@ local redis = {}
local command = {}
local meta = {
__index = command,
__gc = function(self)
self[1]:close()
end,
-- DO NOT close channel in __gc
}
---------- redis response

View File

@@ -16,7 +16,7 @@ local skynet = {
-- read skynet.h
PTYPE_TEXT = 0,
PTYPE_RESPONSE = 1,
-- PTYPE_MULTICAST = 2, -- DEPRECATED
PTYPE_MULTICAST = 2,
PTYPE_CLIENT = 3,
PTYPE_SYSTEM = 4,
PTYPE_HARBOR = 5,
@@ -24,7 +24,8 @@ local skynet = {
PTYPE_ERROR = 7,
PTYPE_QUEUE = 8,
PTYPE_DEBUG = 9,
PTYPE_LUA = 10
PTYPE_LUA = 10,
PTYPE_SNAX = 11,
}
-- code cache
@@ -68,13 +69,13 @@ local function dispatch_error_queue()
end
end
local function _error_dispatch(error_session, monitor, service)
if service then
local function _error_dispatch(error_session, error_source)
if error_session == 0 then
-- service is down
-- Don't remove from watching_service , because user may call dead service
watching_service[service] = false
watching_service[error_source] = false
for session, srv in pairs(watching_session) do
if srv == service then
if srv == error_source then
table.insert(error_queue, session)
end
end
@@ -86,21 +87,6 @@ local function _error_dispatch(error_session, monitor, service)
end
end
local watch_monitor
function skynet.watch(service)
assert(type(service) == "number")
if watch_monitor == nil then
watch_monitor = string_to_handle(c.command("MONITOR"))
assert(watch_monitor, "Need a monitor")
end
if watching_service[service] == nil then
watching_service[service] = true
-- read lualib/simplemonitor.lua
assert(skynet.call(watch_monitor, "lua", "WATCH", service), "watch a dead service")
end
end
-- coroutine reuse
local coroutine_pool = {}
@@ -209,7 +195,7 @@ function skynet.register(name)
end
function skynet.name(name, handle)
c.command("NAME", name .. " " .. handle)
c.command("NAME", name .. " " .. skynet.address(handle))
end
local self_handle
@@ -254,7 +240,12 @@ function skynet.kill(name)
end
function skynet.getenv(key)
return c.command("GETENV",key)
local ret = c.command("GETENV",key)
if ret == "" then
return
else
return ret
end
end
function skynet.setenv(key, value)
@@ -283,7 +274,7 @@ local function yield_call(service, session)
watching_session[session] = service
local succ, msg, sz = coroutine_yield("CALL", session)
watching_session[session] = nil
assert(succ, "Capture an error")
assert(succ, debug.traceback())
return msg,sz
end
@@ -299,17 +290,6 @@ function skynet.call(addr, typename, ...)
return p.unpack(yield_call(addr, session))
end
function skynet.blockcall(addr, typename , ...)
local p = proto[typename]
c.command("LOCK")
local session = c.send(addr, p.id , nil , p.pack(...))
if session == nil then
c.command("UNLOCK")
error("call to invalid address " .. skynet.address(addr))
end
return p.unpack(yield_call(addr, session))
end
function skynet.rawcall(addr, typename, msg, sz)
local p = proto[typename]
local session = assert(c.send(addr, p.id , nil , msg, sz), "call to invalid address")
@@ -322,7 +302,7 @@ function skynet.ret(msg, sz)
end
function skynet.retpack(...)
return skynet.ret(skynet.pack(...))
return skynet.ret(skynet.pack(...))
end
function skynet.wakeup(co)
@@ -339,14 +319,14 @@ function skynet.dispatch(typename, func)
end
local function unknown_request(session, address, msg, sz)
print("Unknown request :" , c.tostring(msg,sz))
print("Unknown request :" , c.tostring(msg,sz))
error(string.format("Unknown session : %d from %x", session, address))
end
function skynet.dispatch_unknown_request(unknown)
local prev = unknown_request
unknown_request = unknown
return prev
local prev = unknown_request
unknown_request = unknown
return prev
end
local function unknown_response(session, address, msg, sz)
@@ -393,7 +373,7 @@ local function raw_dispatch_message(prototype, msg, sz, session, source, ...)
session_coroutine_address[co] = source
suspend(co, coroutine.resume(co, session,source, p.unpack(msg,sz, ...)))
else
unknown_request(session, source, msg, sz)
unknown_request(session, source, msg, sz)
end
end
end
@@ -420,8 +400,7 @@ local function dispatch_message(...)
end
function skynet.newservice(name, ...)
local param = table.concat({"snlua", name, ...}, " ")
local handle = skynet.call(".launcher", "text" , param)
local handle = skynet.tostring(skynet.rawcall(".launcher", "lua" , skynet.pack("LAUNCH", "snlua", name, ...)))
if handle == "" then
return nil
else
@@ -430,32 +409,18 @@ function skynet.newservice(name, ...)
end
function skynet.uniqueservice(global, ...)
local handle
if global == true then
handle = skynet.call("SERVICE", "lua", "LAUNCH", ...)
return assert(skynet.call(".service", "lua", "GLAUNCH", ...))
else
handle = skynet.call(".service", "lua", "LAUNCH", global, ...)
return assert(skynet.call(".service", "lua", "LAUNCH", global, ...))
end
assert(handle , "Unique service launch failed")
return handle
end
function skynet.queryservice(global, ...)
local handle
if global == true then
handle = skynet.call("SERVICE", "lua", "QUERY", ...)
return assert(skynet.call(".service", "lua", "GQUERY", ...))
else
handle = skynet.call(".service", "lua", "QUERY", global, ...)
end
assert(handle , "Unique service query failed")
return handle
end
local function group_command(cmd, handle, address)
if address then
return string.format("%s %d :%x",cmd, handle, address)
else
return string.format("%s %d",cmd,handle)
return assert(skynet.call(".service", "lua", "QUERY", global, ...))
end
end
@@ -479,66 +444,10 @@ function skynet.error(...)
return c.error(table.concat(t, " "))
end
----- debug
local internal_info_func
function skynet.info_func(func)
internal_info_func = func
end
local dbgcmd = {}
function dbgcmd.MEM()
local kb, bytes = collectgarbage "count"
skynet.ret(skynet.pack(kb,bytes))
end
function dbgcmd.GC()
coroutine_pool = {}
collectgarbage "collect"
end
function dbgcmd.STAT()
local stat = {}
stat.mqlen = skynet.mqlen()
skynet.ret(skynet.pack(stat))
end
function dbgcmd.INFO()
if internal_info_func then
skynet.ret(skynet.pack(internal_info_func()))
else
skynet.ret(skynet.pack(nil))
end
end
local function _debug_dispatch(session, address, cmd, ...)
local f = dbgcmd[cmd]
assert(f, cmd)
f(...)
end
----- register protocol
do
local REG = skynet.register_protocol
REG {
name = "text",
id = skynet.PTYPE_TEXT,
pack = function (...)
local n = select ("#" , ...)
if n == 0 then
return ""
elseif n == 1 then
return tostring(...)
else
return table.concat({...}," ")
end
end,
unpack = c.tostring
}
REG {
name = "lua",
id = skynet.PTYPE_LUA,
@@ -551,19 +460,10 @@ do
id = skynet.PTYPE_RESPONSE,
}
REG {
name = "debug",
id = skynet.PTYPE_DEBUG,
pack = skynet.pack,
unpack = skynet.unpack,
dispatch = _debug_dispatch,
}
REG {
name = "error",
id = skynet.PTYPE_ERROR,
pack = skynet.pack,
unpack = skynet.unpack,
unpack = function(...) return ... end,
dispatch = _error_dispatch,
}
end
@@ -603,10 +503,10 @@ local function init_service(start)
local ok, err = xpcall(init_template, debug.traceback, start)
if not ok then
print("init service failed:", err)
skynet.send(".launcher","text", "ERROR")
skynet.send(".launcher","lua", "ERROR")
skynet.exit()
else
skynet.send(".launcher","text", "")
skynet.send(".launcher","lua", "LAUNCHOK")
end
end
@@ -649,4 +549,8 @@ function skynet.mqlen()
return tonumber(c.command "MQLEN")
end
-- Inject internal debug framework
local debug = require "skynet.debug"
debug(skynet)
return skynet

49
lualib/skynet/debug.lua Normal file
View File

@@ -0,0 +1,49 @@
return function (skynet)
local internal_info_func
function skynet.info_func(func)
internal_info_func = func
end
local dbgcmd = {}
function dbgcmd.MEM()
local kb, bytes = collectgarbage "count"
skynet.ret(skynet.pack(kb,bytes))
end
function dbgcmd.GC()
coroutine_pool = {}
collectgarbage "collect"
end
function dbgcmd.STAT()
local stat = {}
stat.mqlen = skynet.mqlen()
skynet.ret(skynet.pack(stat))
end
function dbgcmd.INFO()
if internal_info_func then
skynet.ret(skynet.pack(internal_info_func()))
else
skynet.ret(skynet.pack(nil))
end
end
local function _debug_dispatch(session, address, cmd, ...)
local f = dbgcmd[cmd]
assert(f, cmd)
f(...)
end
skynet.register_protocol {
name = "debug",
id = assert(skynet.PTYPE_DEBUG),
pack = assert(skynet.pack),
unpack = assert(skynet.unpack),
dispatch = _debug_dispatch,
}
end

View File

@@ -1,11 +1,19 @@
local skynet = require "skynet"
local snax_interface = require "snax_interface"
local snax_interface = require "snax.interface"
local snax = {}
local typeclass = {}
local G = { require = function() end }
skynet.register_protocol {
name = "snax",
id = skynet.PTYPE_SNAX,
pack = skynet.pack,
unpack = skynet.unpack,
}
function snax.interface(name)
if typeclass[name] then
return typeclass[name]
@@ -38,7 +46,7 @@ local function gen_post(type, handle)
__index = function( t, k )
local id = assert(type.accept[k] , string.format("post %s no exist", k))
return function(...)
skynet_send(handle, "lua", id, ...)
skynet_send(handle, "snax", id, ...)
end
end })
end
@@ -48,7 +56,7 @@ local function gen_req(type, handle)
__index = function( t, k )
local id = assert(type.response[k] , string.format("request %s no exist", k))
return function(...)
return skynet_call(handle, "lua", id, ...)
return skynet_call(handle, "snax", id, ...)
end
end })
end
@@ -64,16 +72,14 @@ end
local handle_cache = setmetatable( {} , { __mode = "kv" } )
function snax.newservice(name, ...)
function snax.rawnewservice(name, ...)
local t = snax.interface(name)
local handle = skynet.newservice("snaxd", name)
assert(handle_cache[handle] == nil)
if t.system.init then
skynet.call(handle, "lua", t.system.init, ...)
skynet.call(handle, "snax", t.system.init, ...)
end
local ret = wrapper(handle, name, t)
handle_cache[handle] = ret
return ret
return handle
end
function snax.bind(handle, type)
@@ -88,9 +94,42 @@ function snax.bind(handle, type)
return ret
end
function snax.newservice(name, ...)
local handle = snax.rawnewservice(name, ...)
return snax.bind(handle, name)
end
local function service_name(global, name, ...)
if global == true then
return name
else
return global
end
end
function snax.uniqueservice(name, ...)
local handle = assert(skynet.call(".service", "lua", "LAUNCH", "snaxd", name, ...))
return snax.bind(handle, name)
end
function snax.globalservice(name, ...)
local handle = assert(skynet.call(".service", "lua", "GLAUNCH", "snaxd", name, ...))
return snax.bind(handle, name)
end
function snax.queryservice(name)
local handle = assert(skynet.call(".service", "lua", "QUERY", "snaxd", name))
return snax.bind(handle, name)
end
function snax.queryglobal(name)
local handle = assert(skynet.call(".service", "lua", "GQUERY", "snaxd", name))
return snax.bind(handle, name)
end
function snax.kill(obj, ...)
local t = snax.interface(obj.type)
skynet_call(obj.handle, "lua", t.system.exit, ...)
skynet_call(obj.handle, "snax", t.system.exit, ...)
end
local function test_result(ok, ...)
@@ -103,7 +142,7 @@ end
function snax.hotfix(obj, source, ...)
local t = snax.interface(obj.type)
return test_result(skynet_call(obj.handle, "lua", t.system.hotfix, source, ...))
return test_result(skynet_call(obj.handle, "snax", t.system.hotfix, source, ...))
end
return snax

View File

@@ -1,4 +1,4 @@
local si = require "snax_interface"
local si = require "snax.interface"
local io = io
local hotfix = {}

View File

@@ -30,6 +30,11 @@ local function suspend(s)
assert(not s.co)
s.co = coroutine.running()
skynet.wait()
-- wakeup closing corouting every time suspend,
-- because socket.close() will wait last socket buffer operation before clear the buffer.
if s.closing then
skynet.wakeup(s.closing)
end
end
-- read skynet_socket.h for these macro
@@ -37,7 +42,7 @@ end
socket_message[1] = function(id, size, data)
local s = socket_pool[id]
if s == nil then
print("socket: drop package from " .. id)
skynet.error("socket: drop package from " .. id)
driver.drop(data, size)
return
end
@@ -95,13 +100,14 @@ end
socket_message[5] = function(id)
local s = socket_pool[id]
if s == nil then
print("socket: error on unknown", id)
skynet.error("socket: error on unknown", id)
return
end
if s.connected then
print("socket: error on", id)
skynet.error("socket: error on", id)
end
s.connected = false
wakeup(s)
end
@@ -145,7 +151,7 @@ function socket.bind(os_fd)
end
function socket.stdin()
return socket.bind(1)
return socket.bind(0)
end
function socket.start(id, func)
@@ -175,7 +181,8 @@ function socket.close(id)
-- notice: call socket.close in __gc should be carefully,
-- because skynet.wait never return in __gc, so driver.clear may not be called
if s.co then
-- reading this socket on another coroutine
-- reading this socket on another coroutine, so don't shutdown (clear the buffer) immediatel
-- wait reading coroutine read the buffer.
assert(not s.closing)
s.closing = coroutine.running()
skynet.wait()
@@ -189,13 +196,6 @@ function socket.close(id)
socket_pool[id] = nil
end
local function close_socket(s)
if s.closing then
skynet.wakeup(s.closing)
end
return driver.readall(s.buffer, buffer_pool)
end
function socket.read(id, sz)
local s = socket_pool[id]
assert(s)
@@ -204,7 +204,7 @@ function socket.read(id, sz)
return ret
end
if not s.connected then
return false, close_socket(s)
return false, driver.readall(s.buffer, buffer_pool)
end
assert(not s.read_required)
@@ -214,7 +214,7 @@ function socket.read(id, sz)
if ret then
return ret
else
return false, close_socket(s)
return false, driver.readall(s.buffer, buffer_pool)
end
end
@@ -222,14 +222,14 @@ function socket.readall(id)
local s = socket_pool[id]
assert(s)
if not s.connected then
local r = close_socket(s)
local r = driver.readall(s.buffer, buffer_pool)
return r ~= "" and r
end
assert(not s.read_required)
s.read_required = true
suspend(s)
assert(s.connected == false)
return close_socket(s)
return driver.readall(s.buffer, buffer_pool)
end
function socket.readline(id, sep)
@@ -241,7 +241,7 @@ function socket.readline(id, sep)
return ret
end
if not s.connected then
return false, close_socket(s)
return false, driver.readall(s.buffer, buffer_pool)
end
assert(not s.read_required)
s.read_required = sep
@@ -249,7 +249,7 @@ function socket.readline(id, sep)
if s.connected then
return driver.readline(s.buffer, buffer_pool, sep)
else
return false, close_socket(s)
return false, driver.readall(s.buffer, buffer_pool)
end
end
@@ -261,9 +261,6 @@ function socket.block(id)
assert(not s.read_required)
s.read_required = 0
suspend(s)
if not s.connected and s.closing then
skynet.wakeup(s.closing)
end
return s.connected
end

View File

@@ -27,8 +27,8 @@ function socket_channel.channel(desc)
__host = assert(desc.host),
__port = assert(desc.port),
__auth = desc.auth,
__response = desc.response,
__request = {}, -- request seq { response func or session }
__response = desc.response, -- It's for session mode
__request = {}, -- request seq { response func or session } -- It's for order mode
__thread = {}, -- coroutine seq or session->coroutine map
__result = {}, -- response result { coroutine -> result }
__result_data = {},
@@ -51,72 +51,101 @@ local function close_channel_socket(self)
end
local function wakeup_all(self, errmsg)
for i = 1, #self.__thread do
local co = self.__thread[i]
self.__thread[i] = nil
self.__result[co] = socket_error
self.__result_data[co] = errmsg
skynet.wakeup(co)
if self.__response then
for k,co in pairs(self.__thread) do
self.__thread[k] = nil
self.__result[co] = socket_error
self.__result_data[co] = errmsg
skynet.wakeup(co)
end
else
for i = 1, #self.__request do
self.__request[i] = nil
end
for i = 1, #self.__thread do
local co = self.__thread[i]
self.__thread[i] = nil
self.__result[co] = socket_error
self.__result_data[co] = errmsg
skynet.wakeup(co)
end
end
end
local function dispatch_response(self)
local function dispatch_by_session(self)
local response = self.__response
if response then
-- response() return session
while self.__sock do
local ok , session, result_ok, result_data = pcall(response, self.__sock)
if ok and session then
local co = self.__thread[session]
self.__thread[session] = nil
if co then
self.__result[co] = result_ok
self.__result_data[co] = result_data
skynet.wakeup(co)
else
print("socket: unknown session :", session)
end
-- response() return session
while self.__sock do
local ok , session, result_ok, result_data = pcall(response, self.__sock)
if ok and session then
local co = self.__thread[session]
self.__thread[session] = nil
if co then
self.__result[co] = result_ok
self.__result_data[co] = result_data
skynet.wakeup(co)
else
skynet.error("socket: unknown session :", session)
end
else
close_channel_socket(self)
local errormsg
if session ~= socket_error then
errormsg = session
end
wakeup_all(self, errormsg)
end
end
end
local function pop_response(self)
return table.remove(self.__request, 1), table.remove(self.__thread, 1)
end
local function push_response(self, response, co)
if self.__response then
-- response is session
self.__thread[response] = co
else
-- response is a function, push it to __request
table.insert(self.__request, response)
table.insert(self.__thread, co)
end
end
local function dispatch_by_order(self)
while self.__sock do
local func, co = pop_response(self)
if func == nil then
if not socket.block(self.__sock[1]) then
close_channel_socket(self)
wakeup_all(self)
end
else
local ok, result_ok, result_data = pcall(func, self.__sock)
if ok then
self.__result[co] = result_ok
self.__result_data[co] = result_data
skynet.wakeup(co)
else
close_channel_socket(self)
local errormsg
if session ~= socket_error then
errormsg = session
end
for k,co in pairs(self.__thread) do
-- throw error (errormsg)
self.__thread[k] = nil
self.__result[co] = socket_error
self.__result_data[co] = errormsg
skynet.wakeup(co)
local errmsg
if result ~= socket_error then
errmsg = result_ok
end
wakeup_all(self, errmsg)
end
end
end
end
local function dispatch_function(self)
if self.__response then
return dispatch_by_session
else
-- pop response function from __request
while self.__sock do
local func = table.remove(self.__request, 1)
if func == nil then
if not socket.block(self.__sock[1]) then
close_channel_socket(self)
wakeup_all(self)
end
else
local ok, result_ok, result_data = pcall(func, self.__sock)
if ok then
local co = table.remove(self.__thread, 1)
self.__result[co] = result_ok
self.__result_data[co] = result_data
skynet.wakeup(co)
else
close_channel_socket(self)
local errmsg
if result ~= socket_error then
errmsg = result_ok
end
wakeup_all(self, errmsg)
end
end
end
return dispatch_by_order
end
end
@@ -128,14 +157,14 @@ local function connect_once(self)
end
self.__authcoroutine = coroutine.running()
self.__sock = setmetatable( {fd} , channel_socket_meta )
skynet.fork(dispatch_response, self)
skynet.fork(dispatch_function(self), self)
if self.__auth then
local ok , message = pcall(self.__auth, self)
if not ok then
close_channel_socket(self)
if message ~= socket_error then
print("socket: auth failed", message)
skynet.error("socket: auth failed", message)
end
end
self.__authcoroutine = false
@@ -150,12 +179,15 @@ local function try_connect(self , once)
local t = 100
while not self.__closed do
if connect_once(self) then
if not once then
skynet.error("socket: connect to", self.__host, self.__port)
end
return
elseif once then
error(string.format("Connect to %s:%d failed", self.__host, self.__port))
end
if t > 1000 then
print("socket: try to reconnect", self.__host, self.__port)
skynet.error("socket: try to reconnect", self.__host, self.__port)
skynet.sleep(t)
t = 0
else
@@ -179,6 +211,7 @@ local function block_connect(self, once)
if self.__closed then
return false
end
if #self.__connecting > 0 then
-- connecting in other coroutine
local co = coroutine.running()
@@ -187,7 +220,6 @@ local function block_connect(self, once)
-- check connection again
return block_connect(self, once)
end
self.__connecting[1] = true
try_connect(self, once)
self.__connecting[1] = nil
@@ -209,10 +241,30 @@ function channel:connect(once)
return block_connect(self, once)
end
local function wait_for_response(self, response)
local co = coroutine.running()
push_response(self, response, co)
skynet.wait()
local result = self.__result[co]
self.__result[co] = nil
local result_data = self.__result_data[co]
self.__result_data[co] = nil
if result == socket_error then
error(socket_error)
else
assert(result, result_data)
return result_data
end
end
function channel:request(request, response)
assert(block_connect(self))
if not socket.write(self.__sock[1], request) then
close_channel_socket(self)
wakeup_all(self)
error(socket_error)
end
@@ -221,59 +273,13 @@ function channel:request(request, response)
return
end
local co = coroutine.running()
if self.__response then
-- response is session
self.__thread[response] = co
else
-- response is a function, push it to __request
table.insert(self.__request, response)
table.insert(self.__thread, co)
end
skynet.wait()
local result = self.__result[co]
self.__result[co] = nil
local result_data = self.__result_data[co]
self.__result_data[co] = nil
if result == socket_error then
if result_data then
print("socket: dispatch", request, result_data)
end
error(socket_error)
else
assert(result, result_data)
return result_data
end
return wait_for_response(self, response)
end
function channel:response(response)
assert(block_connect(self))
assert(type(response) == "function")
local co = coroutine.running()
table.insert(self.__request, response)
table.insert(self.__thread, co)
skynet.wait()
local result = self.__result[co]
self.__result[co] = nil
local result_data = self.__result_data[co]
self.__result_data[co] = nil
if result == socket_error then
if result_data then
print("socket: dispatch", request, result_data)
end
error(socket_error)
else
assert(result, result_data)
return result_data
end
return wait_for_response(self, response)
end
function channel:close()

View File

@@ -5,30 +5,36 @@ CC ?= gcc
.PHONY : none $(PLATS) clean all cleanall
ifneq ($(PLAT), none)
#ifneq ($(PLAT), none)
.PHONY : default
default :
$(MAKE) $(PLAT)
endif
#endif
none :
@echo "Please do 'make PLATFORM' where PLATFORM is one of these:"
@echo " $(PLATS)"
LIBS := -lpthread -lm
SKYNET_LIBS := -lpthread -lm
SHARED := -fPIC --shared
EXPORT := -Wl,-E
$(PLATS) : all
linux : PLAT = linux
macosx : PLAT = macosx
freebsd : PLAT = freebsd
macosx linux : LIBS += -ldl
macosx : SHARED := -fPIC -dynamiclib -Wl,-undefined,dynamic_lookup
macosx : EXPORT :=
linux freebsd : LIBS += -lrt
macosx linux : SKYNET_LIBS += -ldl
linux freebsd : SKYNET_LIBS += -lrt
# Turn off jemalloc and malloc hook on macosx
macosx : MALLOC_STATICLIB :=
macosx : SKYNET_DEFINES :=-DNOUSE_JEMALLOC
linux macosx freebsd :
$(MAKE) all PLAT=$@ SKYNET_LIBS="$(SKYNET_LIBS)" SHARED="$(SHARED)" EXPORT="$(EXPORT)" MALLOC_STATICLIB="$(MALLOC_STATICLIB)" SKYNET_DEFINES="$(SKYNET_DEFINES)"

292
service-src/service_dummy.c Normal file
View File

@@ -0,0 +1,292 @@
#include "skynet.h"
#include "skynet_harbor.h"
#include <string.h>
#include <assert.h>
#define HASH_SIZE 4096
#define DEFAULT_QUEUE_SIZE 1024
struct msg {
uint8_t * buffer;
size_t size;
};
struct msg_queue {
int size;
int head;
int tail;
struct msg * data;
};
struct keyvalue {
struct keyvalue * next;
char key[GLOBALNAME_LENGTH];
uint32_t hash;
uint32_t value;
struct msg_queue * queue;
};
struct hashmap {
struct keyvalue *node[HASH_SIZE];
};
/*
message type (8bits) is in destination high 8bits
harbor id (8bits) is also in that place , but remote message doesn't need harbor id.
*/
struct remote_message_header {
uint32_t source;
uint32_t destination;
uint32_t session;
};
// 12 is sizeof(struct remote_message_header)
#define HEADER_COOKIE_LENGTH 12
struct dummy {
struct skynet_context *ctx;
struct hashmap * map;
};
// hash table
static void
_push_queue(struct msg_queue * queue, const void * buffer, size_t sz, struct remote_message_header * header) {
// If there is only 1 free slot which is reserved to distinguish full/empty
// of circular buffer, expand it.
if (((queue->tail + 1) % queue->size) == queue->head) {
struct msg * new_buffer = skynet_malloc(queue->size * 2 * sizeof(struct msg));
int i;
for (i=0;i<queue->size-1;i++) {
new_buffer[i] = queue->data[(i+queue->head) % queue->size];
}
skynet_free(queue->data);
queue->data = new_buffer;
queue->head = 0;
queue->tail = queue->size - 1;
queue->size *= 2;
}
struct msg * slot = &queue->data[queue->tail];
queue->tail = (queue->tail + 1) % queue->size;
slot->buffer = skynet_malloc(sz + sizeof(*header));
memcpy(slot->buffer, buffer, sz);
memcpy(slot->buffer + sz, header, sizeof(*header));
slot->size = sz + sizeof(*header);
}
static struct msg *
_pop_queue(struct msg_queue * queue) {
if (queue->head == queue->tail) {
return NULL;
}
struct msg * slot = &queue->data[queue->head];
queue->head = (queue->head + 1) % queue->size;
return slot;
}
static struct msg_queue *
_new_queue() {
struct msg_queue * queue = skynet_malloc(sizeof(*queue));
queue->size = DEFAULT_QUEUE_SIZE;
queue->head = 0;
queue->tail = 0;
queue->data = skynet_malloc(DEFAULT_QUEUE_SIZE * sizeof(struct msg));
return queue;
}
static void
_release_queue(struct msg_queue *queue) {
if (queue == NULL)
return;
struct msg * m = _pop_queue(queue);
while (m) {
skynet_free(m->buffer);
m = _pop_queue(queue);
}
skynet_free(queue->data);
skynet_free(queue);
}
static struct keyvalue *
_hash_search(struct hashmap * hash, const char name[GLOBALNAME_LENGTH]) {
uint32_t *ptr = (uint32_t*) name;
uint32_t h = ptr[0] ^ ptr[1] ^ ptr[2] ^ ptr[3];
struct keyvalue * node = hash->node[h % HASH_SIZE];
while (node) {
if (node->hash == h && strncmp(node->key, name, GLOBALNAME_LENGTH) == 0) {
return node;
}
node = node->next;
}
return NULL;
}
static struct keyvalue *
_hash_insert(struct hashmap * hash, const char name[GLOBALNAME_LENGTH]) {
uint32_t *ptr = (uint32_t *)name;
uint32_t h = ptr[0] ^ ptr[1] ^ ptr[2] ^ ptr[3];
struct keyvalue ** pkv = &hash->node[h % HASH_SIZE];
struct keyvalue * node = skynet_malloc(sizeof(*node));
memcpy(node->key, name, GLOBALNAME_LENGTH);
node->next = *pkv;
node->queue = NULL;
node->hash = h;
node->value = 0;
*pkv = node;
return node;
}
static struct hashmap *
_hash_new() {
struct hashmap * h = skynet_malloc(sizeof(struct hashmap));
memset(h,0,sizeof(*h));
return h;
}
static void
_hash_delete(struct hashmap *hash) {
int i;
for (i=0;i<HASH_SIZE;i++) {
struct keyvalue * node = hash->node[i];
while (node) {
struct keyvalue * next = node->next;
_release_queue(node->queue);
skynet_free(node);
node = next;
}
}
skynet_free(hash);
}
///////////////
struct dummy *
dummy_create(void) {
struct dummy * d = skynet_malloc(sizeof(*d));
d->map = _hash_new();
return d;
}
void
dummy_release(struct dummy *d) {
_hash_delete(d->map);
skynet_free(d);
}
static inline void
to_bigendian(uint8_t *buffer, uint32_t n) {
buffer[0] = (n >> 24) & 0xff;
buffer[1] = (n >> 16) & 0xff;
buffer[2] = (n >> 8) & 0xff;
buffer[3] = n & 0xff;
}
static inline void
_header_to_message(const struct remote_message_header * header, uint8_t * message) {
to_bigendian(message , header->source);
to_bigendian(message+4 , header->destination);
to_bigendian(message+8 , header->session);
}
static inline uint32_t
from_bigendian(uint32_t n) {
union {
uint32_t big;
uint8_t bytes[4];
} u;
u.big = n;
return u.bytes[0] << 24 | u.bytes[1] << 16 | u.bytes[2] << 8 | u.bytes[3];
}
static inline void
_message_to_header(const uint32_t *message, struct remote_message_header *header) {
header->source = from_bigendian(message[0]);
header->destination = from_bigendian(message[1]);
header->session = from_bigendian(message[2]);
}
static void
_dispatch_queue(struct dummy *h, struct msg_queue * queue, uint32_t handle, const char name[GLOBALNAME_LENGTH] ) {
struct msg * m = _pop_queue(queue);
while (m) {
struct remote_message_header cookie;
uint8_t *ptr = m->buffer + m->size - sizeof(cookie);
memcpy(&cookie, ptr, sizeof(cookie));
int type = cookie.destination >> HANDLE_REMOTE_SHIFT;
skynet_send(h->ctx, cookie.source, handle , type | PTYPE_TAG_DONTCOPY, cookie.session, m->buffer, m->size - sizeof(cookie));
m = _pop_queue(queue);
}
}
static void
_update_name(struct dummy *h, const char name[GLOBALNAME_LENGTH], uint32_t handle) {
struct keyvalue * node = _hash_search(h->map, name);
if (node == NULL) {
node = _hash_insert(h->map, name);
}
node->value = handle;
if (node->queue) {
_dispatch_queue(h, node->queue, handle, name);
_release_queue(node->queue);
node->queue = NULL;
}
}
static void
_send_name(struct dummy *h, uint32_t source, const char name[GLOBALNAME_LENGTH], int type, int session, const char * msg, size_t sz) {
struct keyvalue * node = _hash_search(h->map, name);
if (node == NULL) {
node = _hash_insert(h->map, name);
}
if (node->value == 0) {
if (node->queue == NULL) {
node->queue = _new_queue();
}
struct remote_message_header header;
header.source = source;
header.destination = type << HANDLE_REMOTE_SHIFT;
header.session = (uint32_t)session;
_push_queue(node->queue, msg, sz, &header);
} else {
// local message
skynet_send(h->ctx, source, node->value , type | PTYPE_TAG_DONTCOPY, session, (void *)msg, sz);
}
}
static int
_mainloop(struct skynet_context * context, void * ud, int type, int session, uint32_t source, const void * msg, size_t sz) {
struct dummy * h = ud;
switch (type) {
case PTYPE_SYSTEM: {
// register name message
const struct remote_message *rmsg = msg;
assert (sz == sizeof(rmsg->destination));
_update_name(h, rmsg->destination.name, rmsg->destination.handle);
return 0;
}
default: {
// remote message out
const struct remote_message *rmsg = msg;
if (rmsg->destination.handle == 0) {
_send_name(h, source , rmsg->destination.name, type, session, rmsg->message, rmsg->sz);
} else {
// local message
skynet_send(context, source, rmsg->destination.handle , type | PTYPE_TAG_DONTCOPY, session, (void *)rmsg->message, rmsg->sz);
}
return 0;
}
}
}
int
dummy_init(struct dummy *d, struct skynet_context *ctx, const char * args) {
d->ctx = ctx;
skynet_harbor_start(ctx);
skynet_callback(ctx, d, _mainloop);
return 0;
}

View File

@@ -47,6 +47,9 @@ struct remote_message_header {
uint32_t session;
};
// 12 is sizeof(struct remote_message_header)
#define HEADER_COOKIE_LENGTH 12
struct harbor {
struct skynet_context *ctx;
char * local_addr;
@@ -402,9 +405,8 @@ _remote_send_handle(struct harbor *h, uint32_t source, uint32_t destination, int
_send_remote(context, fd, msg,sz,&cookie);
} else {
// throw an error return to source
if (session != 0) {
skynet_send(context, destination, source, PTYPE_RESERVED_ERROR, session, NULL, 0);
}
// report the destination is dead
skynet_send(context, destination, source, PTYPE_ERROR, 0 , NULL, 0);
skynet_error(context, "Drop message to harbor %d from %x to %x (session = %d, msgsz = %d)",harbor_id, source, destination,session,(int)sz);
}
return 0;
@@ -503,23 +505,23 @@ _mainloop(struct skynet_context * context, void * ud, int type, int session, uin
case PTYPE_HARBOR: {
// remote message in
const char * cookie = msg;
cookie += sz - 12;
cookie += sz - HEADER_COOKIE_LENGTH;
struct remote_message_header header;
_message_to_header((const uint32_t *)cookie, &header);
if (header.source == 0) {
if (header.destination < REMOTE_MAX) {
// 1 byte harbor id (0~255)
// update remote harbor address
char ip [sz - 11];
memcpy(ip, msg, sz-12);
ip[sz-11] = '\0';
char ip [sz - HEADER_COOKIE_LENGTH + 1];
memcpy(ip, msg, sz-HEADER_COOKIE_LENGTH);
ip[sz-HEADER_COOKIE_LENGTH] = '\0';
_update_remote_address(h, header.destination, ip);
} else {
// update global name
if (sz - 12 > GLOBALNAME_LENGTH) {
char name[sz-11];
memcpy(name, msg, sz-12);
name[sz-11] = '\0';
if (sz - HEADER_COOKIE_LENGTH > GLOBALNAME_LENGTH) {
char name[sz-HEADER_COOKIE_LENGTH+1];
memcpy(name, msg, sz-HEADER_COOKIE_LENGTH);
name[sz-HEADER_COOKIE_LENGTH] = '\0';
skynet_error(context, "Global name is too long %s", name);
}
_update_remote_name(h, msg, header.destination);
@@ -528,7 +530,7 @@ _mainloop(struct skynet_context * context, void * ud, int type, int session, uin
uint32_t destination = header.destination;
int type = (destination >> HANDLE_REMOTE_SHIFT) | PTYPE_TAG_DONTCOPY;
destination = (destination & HANDLE_MASK) | ((uint32_t)h->id << HANDLE_REMOTE_SHIFT);
skynet_send(context, header.source, destination, type, (int)header.session, (void *)msg, sz-12);
skynet_send(context, header.source, destination, type, (int)header.session, (void *)msg, sz-HEADER_COOKIE_LENGTH);
return 1;
}
return 0;
@@ -593,6 +595,8 @@ harbor_init(struct harbor *h, struct skynet_context *ctx, const char * args) {
fprintf(stderr, "Harbor: Connect to master failed\n");
exit(1);
}
skynet_harbor_start(ctx);
h->local_addr = skynet_strdup(local_addr);
_launch_gate(ctx, local_addr);

View File

@@ -28,7 +28,7 @@ logger_release(struct logger * inst) {
static int
_logger(struct skynet_context * context, void *ud, int type, int session, uint32_t source, const void * msg, size_t sz) {
struct logger * inst = ud;
fprintf(inst->handle, "[:%x] ",source);
fprintf(inst->handle, "[:%08x] ",source);
fwrite(msg, sz , 1, inst->handle);
fprintf(inst->handle, "\n");
fflush(inst->handle);

View File

@@ -1,10 +0,0 @@
#ifndef SKYNET_SERVICE_LUA_H
#define SKYNET_SERVICE_LUA_H
struct snlua {
lua_State * L;
struct skynet_context * ctx;
int (*init)(struct snlua *l, struct skynet_context *ctx, const char * args);
};
#endif

View File

@@ -4,13 +4,17 @@
#include <lualib.h>
#include <lauxlib.h>
#include <assert.h>
#include "service_lua.h"
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
struct snlua {
lua_State * L;
struct skynet_context * ctx;
};
// LUA_CACHELIB may defined in patched lua for shared proto
#ifdef LUA_CACHELIB
@@ -30,121 +34,13 @@ codecache(lua_State *L) {
{ NULL, NULL },
};
luaL_newlib(L,l);
lua_getglobal(L, "loadfile");
lua_setfield(L, -2, "loadfile");
lua_getglobal(L, "loadfile");
lua_setfield(L, -2, "loadfile");
return 1;
}
#endif
// time
#include <time.h>
#define NANOSEC 1000000000
#if defined(__APPLE__)
#include <mach/task.h>
#include <mach/mach.h>
#endif
static void
current_time(struct timespec *ti) {
#if !defined(__APPLE__)
clock_gettime(CLOCK_THREAD_CPUTIME_ID, ti);
#else
struct task_thread_times_info aTaskInfo;
mach_msg_type_number_t aTaskInfoCount = TASK_THREAD_TIMES_INFO_COUNT;
assert(KERN_SUCCESS == task_info(mach_task_self(), TASK_THREAD_TIMES_INFO, (task_info_t )&aTaskInfo, &aTaskInfoCount));
ti->tv_sec = aTaskInfo.user_time.seconds;
ti->tv_nsec = aTaskInfo.user_time.microseconds * 1000;
#endif
}
static double
diff_time(struct timespec *ti) {
struct timespec end;
current_time(&end);
int diffsec = end.tv_sec - ti->tv_sec;
int diffnsec = end.tv_nsec - ti->tv_nsec;
if (diffnsec < 0) {
--diffsec;
diffnsec += NANOSEC;
}
return (double)diffsec + (double)diffnsec / NANOSEC;
}
static int
_try_load(lua_State *L, const char * path, int pathlen, const char * name) {
int namelen = strlen(name);
char tmp[pathlen + namelen];
int i;
for (i=0;i<pathlen;i++) {
if (path[i] == '?')
break;
tmp[i] = path[i];
}
if (path[i] == '?') {
memcpy(tmp+i,name,namelen);
memcpy(tmp+i+namelen,path+i+1,pathlen - i -1);
} else {
fprintf(stderr,"snlua : Invalid lua service path\n");
exit(1);
}
tmp[namelen+pathlen-1] = '\0';
int r = luaL_loadfile(L,tmp);
if (r == LUA_OK) {
int i;
for (i=namelen+pathlen-2;i>=0;i--) {
if (tmp[i] == '/') {
lua_pushlstring(L,tmp,i+1);
lua_setglobal(L,"SERVICE_PATH");
break;
}
}
if (i<0) {
return 0;
}
lua_getglobal(L,"package");
lua_getfield(L,-1,"path");
luaL_Buffer b;
luaL_buffinit(L, &b);
luaL_addlstring(&b, tmp, i+1);
luaL_addstring(&b, "?.lua;");
luaL_addvalue(&b);
luaL_pushresult(&b);
lua_setfield(L,-2,"path");
lua_pop(L,1);
return 0;
} else if (r == LUA_ERRFILE) {
lua_pop(L,1);
return -1;
}
return 1;
}
static int
_load(lua_State *L, char ** filename) {
const char * name = strsep(filename, " \r\n");
const char * path = skynet_command(NULL, "GETENV", "luaservice");
while (path[0]) {
int pathlen;
char * pathend = strchr(path,';');
if (pathend) {
pathlen = pathend - path;
} else {
pathlen = strlen(path);
}
int r = _try_load(L, path, pathlen, name);
if (r >=0) {
return r;
}
path+=pathlen;
if (path[0]==';')
++path;
}
return -1;
}
static int
traceback (lua_State *L) {
const char *msg = lua_tostring(L, 1);
@@ -156,80 +52,70 @@ traceback (lua_State *L) {
return 1;
}
static void
_report_error(lua_State *L, struct skynet_context *ctx, const char *filename, int err) {
switch (err) {
case LUA_ERRRUN:
skynet_error(ctx, "lua do [%s] error : %s", filename, lua_tostring(L,-1));
break;
case LUA_ERRMEM:
skynet_error(ctx, "lua memory error : %s",filename);
break;
case LUA_ERRERR:
skynet_error(ctx, "lua message error : %s",filename);
break;
case LUA_ERRGCMM:
skynet_error(ctx, "lua gc error : %s",filename);
break;
};
lua_pop(L,1);
}
static void
_report_launcher_error(struct skynet_context *ctx) {
// sizeof "ERROR" == 5
skynet_sendname(ctx, ".launcher", PTYPE_TEXT, 0, "ERROR", 5);
}
static const char *
optstring(struct skynet_context *ctx, const char *key, const char * str) {
const char * ret = skynet_command(ctx, "GETENV", key);
if (ret == NULL) {
return str;
}
return ret;
}
static int
_init(struct snlua *l, struct skynet_context *ctx, const char * args) {
_init(struct snlua *l, struct skynet_context *ctx, const char * args, size_t sz) {
lua_State *L = l->L;
l->ctx = ctx;
lua_gc(L, LUA_GCSTOP, 0);
lua_pushboolean(L, 1); /* signal for libraries to ignore env. vars. */
lua_setfield(L, LUA_REGISTRYINDEX, "LUA_NOENV");
luaL_openlibs(L);
lua_pushlightuserdata(L, l);
lua_setfield(L, LUA_REGISTRYINDEX, "skynet_lua");
lua_pushlightuserdata(L, ctx);
lua_setfield(L, LUA_REGISTRYINDEX, "skynet_context");
luaL_requiref(L, "skynet.codecache", codecache , 0);
lua_pop(L,1);
lua_gc(L, LUA_GCRESTART, 0);
char tmp[strlen(args)+1];
char *parm = tmp;
strcpy(parm,args);
const char *path = optstring(ctx, "lua_path","./lualib/?.lua;./lualib/?/init.lua");
lua_pushstring(L, path);
lua_setglobal(L, "LUA_PATH");
const char *cpath = optstring(ctx, "lua_cpath","./luaclib/?.so");
lua_pushstring(L, cpath);
lua_setglobal(L, "LUA_CPATH");
const char *service = optstring(ctx, "luaservice", "./service/?.lua");
lua_pushstring(L, service);
lua_setglobal(L, "LUA_SERVICE");
const char *preload = skynet_command(ctx, "GETENV", "preload");
lua_pushstring(L, preload);
lua_setglobal(L, "LUA_PRELOAD");
lua_pushcfunction(L, traceback);
int traceback_index = lua_gettop(L);
assert(traceback_index == 1);
assert(lua_gettop(L) == 1);
const char * filename = parm;
int r = _load(L, &parm);
if (r != 0) {
if (r<0) {
skynet_error(ctx, "lua parser [%s] load error", filename);
} else {
skynet_error(ctx, "lua parser [%s] error : %s", filename, lua_tostring(L,-1));
}
const char * loader = optstring(ctx, "lualoader", "./lualib/loader.lua");
int r = luaL_loadfile(L,loader);
if (r != LUA_OK) {
skynet_error(ctx, "Can't load %s : %s", loader, lua_tostring(L, -1));
_report_launcher_error(ctx);
return 1;
}
int n=0;
while(parm) {
const char * arg = strsep(&parm, " \r\n");
if (arg && arg[0]!='\0') {
lua_pushstring(L, arg);
++n;
}
lua_pushlstring(L, args, sz);
r = lua_pcall(L,1,0,1);
if (r != LUA_OK) {
skynet_error(ctx, "lua loader error : %s", lua_tostring(L, -1));
_report_launcher_error(ctx);
return 1;
}
r = lua_pcall(L,n,0,traceback_index);
if (r == LUA_OK) {
r = lua_gc(L, LUA_GCCOLLECT, 0);
if (r == LUA_OK) {
return 0;
}
}
_report_error(L, ctx, filename, r);
_report_launcher_error(ctx);
return 1;
lua_settop(L,0);
lua_gc(L, LUA_GCRESTART, 0);
return 0;
}
static int
@@ -237,12 +123,7 @@ _launch(struct skynet_context * context, void *ud, int type, int session, uint32
assert(type == 0 && session == 0);
struct snlua *l = ud;
skynet_callback(context, NULL, NULL);
struct timespec ti;
current_time(&ti);
int err = _init(l, context, msg);
double t = diff_time(&ti);
lua_pushnumber(l->L, t);
lua_setfield(l->L, LUA_REGISTRYINDEX, "skynet_boottime");
int err = _init(l, context, msg, sz);
if (err) {
skynet_command(context, "EXIT", NULL);
}
@@ -253,13 +134,13 @@ _launch(struct skynet_context * context, void *ud, int type, int session, uint32
int
snlua_init(struct snlua *l, struct skynet_context *ctx, const char * args) {
int sz = strlen(args);
char * tmp = skynet_malloc(sz+1);
memcpy(tmp, args, sz+1);
char * tmp = skynet_malloc(sz);
memcpy(tmp, args, sz);
skynet_callback(ctx, l , _launch);
const char * self = skynet_command(ctx, "REG", NULL);
uint32_t handle_id = strtoul(self+1, NULL, 16);
// it must be first message
skynet_send(ctx, 0, handle_id, PTYPE_TAG_DONTCOPY,0, tmp, sz+1);
skynet_send(ctx, 0, handle_id, PTYPE_TAG_DONTCOPY,0, tmp, sz);
return 0;
}
@@ -268,7 +149,6 @@ snlua_create(void) {
struct snlua * l = skynet_malloc(sizeof(*l));
memset(l,0,sizeof(*l));
l->L = lua_newstate(skynet_lalloc, NULL);
l->init = _init;
return l;
}

35
service/bootstrap.lua Normal file
View File

@@ -0,0 +1,35 @@
local skynet = require "skynet"
skynet.start(function()
assert(skynet.launch("logger", skynet.getenv "logger"))
local standalone = skynet.getenv "standalone"
local harbor_id = tonumber(skynet.getenv "harbor")
if harbor_id == 0 then
assert(standalone == nil)
standalone = true
skynet.setenv("standalone", "true")
assert(skynet.launch("dummy"))
else
local master_addr = skynet.getenv "master"
if standalone then
assert(skynet.launch("master", master_addr))
end
local local_addr = skynet.getenv "address"
assert(skynet.launch("harbor",master_addr, local_addr, harbor_id))
end
local launcher = assert(skynet.launch("snlua","launcher"))
skynet.name(".launcher", launcher)
if standalone then
local datacenter = assert(skynet.newservice "datacenterd")
skynet.name("DATACENTER", datacenter)
end
assert(skynet.newservice "service_mgr")
assert(skynet.newservice(skynet.getenv "start" or "main"))
skynet.exit()
end)

69
service/clusterd.lua Normal file
View File

@@ -0,0 +1,69 @@
local skynet = require "skynet"
local sc = require "socketchannel"
local socket = require "socket"
local cluster = require "cluster.c"
local config_name = skynet.getenv "cluster"
local node_address = {}
assert(loadfile(config_name, "t", node_address))()
local node_session = {}
local command = {}
local function read_response(sock)
local sz = sock:read(2)
local msg = sock:read(sz)
return cluster.unpackresponse(msg) -- session, ok, data
end
local function open_channel(t, key)
local host, port = string.match(node_address[key], "([^:]+):(.*)$")
local c = sc.channel {
host = host,
port = tonumber(port),
response = read_response,
}
assert(c:connect(true))
t[key] = c
node_session[key] = 1
return c
end
local node_channel = setmetatable({}, { __index = open_channel })
function command.listen(source, addr, port)
local gate = skynet.newservice("gate")
skynet.call(gate, "lua", "open", { address = addr, port = port })
skynet.ret(skynet.pack(nil))
end
function command.req(source, node, addr, msg, sz)
local request
local c = node_channel[node]
local session = node_session[node]
-- msg is a local pointer, cluster.packrequest will free it
request, node_session[node] = cluster.packrequest(addr, session , msg, sz)
skynet.ret(c:request(request, session))
end
local request_fd = {}
function command.socket(source, subcmd, fd, msg)
if subcmd == "data" then
local addr, session, msg = cluster.unpackrequest(msg)
local msg, sz = skynet.rawcall(addr, "lua", msg)
local response = cluster.packresponse(session, msg, sz)
socket.write(fd, response)
elseif subcmd == "open" then
skynet.error(string.format("socket accept from %s", msg))
skynet.call(source, "lua", "accept", fd)
else
skynet.error(string.format("socket %s %d : %s", subcmd, fd, msg))
end
end
skynet.start(function()
skynet.dispatch("lua", function(_, source, cmd, ...)
local f = assert(command[cmd])
f(source, ...)
end)
end)

43
service/datacenterd.lua Normal file
View File

@@ -0,0 +1,43 @@
local skynet = require "skynet"
local command = {}
local database = {}
local function query(db, key, ...)
if key == nil then
return db
else
return query(db[key], ...)
end
end
function command.QUERY(key, ...)
local d = database[key]
if d then
return query(d, ...)
end
end
local function update(db, key, value, ...)
if select("#",...) == 0 then
local ret = db[key]
db[key] = value
return ret
else
if db[key] == nil then
db[key] = {}
end
return update(db[key], value, ...)
end
end
function command.UPDATE(...)
return update(database, ...)
end
skynet.start(function()
skynet.dispatch("lua", function (_, source, cmd, ...)
local f = assert(command[cmd])
skynet.ret(skynet.pack(f(...)))
end)
end)

View File

@@ -1,6 +1,7 @@
local skynet = require "skynet"
local codecache = require "skynet.codecache"
local socket = require "socket"
local snax = require "snax"
local port = tonumber(...)
local COMMAND = {}
@@ -109,7 +110,9 @@ function COMMAND.help()
mem = "mem : show memory status",
gc = "gc : force every lua service do garbage collect",
start = "lanuch a new lua service",
snax = "lanuch a new snax service",
clearcache = "clear lua code cache",
service = "List unique service",
}
end
@@ -126,4 +129,16 @@ function COMMAND.start(...)
end
end
function COMMAND.snax(...)
local s = snax.newservice(...)
if s then
local addr = s.handle
return { [skynet.address(addr)] = ... }
else
return "Failed"
end
end
function COMMAND.service()
return skynet.call("SERVICE", "lua", "LIST")
end

View File

@@ -85,7 +85,7 @@ function MSG.data(fd, msg, sz)
if agent then
skynet.redirect(agent, c.client, "client", 0, msg, sz)
else
skynet.send(watchdog, "lua", "socket", "data", netpack.tostring(msg, sz))
skynet.send(watchdog, "lua", "socket", "data", fd, netpack.tostring(msg, sz))
end
end

View File

@@ -2,8 +2,8 @@ local skynet = require "skynet"
local string = string
local services = {}
local command = {}
local instance = {} -- for confirm (function command.LAUNCH / command.ERROR / command.LAUNCHOK)
local function handle_to_address(handle)
return tonumber("0x" .. string.sub(handle , 2))
@@ -19,18 +19,6 @@ function command.LIST()
return list
end
function command.RELOAD(handle)
handle = handle_to_address(handle)
local cmd = string.match(services[handle], "snlua (.+)")
print(services[handle],cmd)
if cmd then
skynet.send(handle,"debug","RELOAD",cmd)
return {cmd}
else
return {"Support only snlua"}
end
end
function command.STAT()
local list = {}
for k,v in pairs(services) do
@@ -40,7 +28,7 @@ function command.STAT()
return list
end
function command.INFO(handle)
function command.INFO(_, _, handle)
handle = handle_to_address(handle)
if services[handle] == nil then
return
@@ -50,23 +38,7 @@ function command.INFO(handle)
end
end
function command.TIMING(handle)
handle = handle_to_address(handle)
if services[handle] == nil then
return
else
local r = skynet.call(handle,"debug","TIMING")
local result = {}
for k,v in pairs(r) do
v.name = services[k]
v.avg = v.ti/v.n
result[skynet.address(k)] = v
end
return result
end
end
function command.KILL(handle)
function command.KILL(_, _, handle)
handle = handle_to_address(handle)
skynet.kill(handle)
local ret = { [skynet.address(handle)] = tostring(services[handle]) }
@@ -90,48 +62,71 @@ function command.GC()
return command.MEM()
end
function command.REMOVE(handle)
function command.REMOVE(_,_, handle)
services[handle] = nil
-- don't return (skynet.ret) because the handle may exit
return NORET
end
local instance = {}
skynet.dispatch("text" , function(session, address , cmd)
if cmd == "" then
-- init notice
local reply = instance[address]
if reply then
skynet.redirect(reply.address , 0, "response", reply.session, skynet.address(address))
instance[address] = nil
end
elseif cmd == "ERROR" then
-- see serivce-src/service_lua.c
-- init failed
local reply = instance[address]
if reply then
skynet.redirect(reply.address , 0, "response", reply.session, "")
instance[address] = nil
end
function command.LAUNCH(address, session, service, ...)
local param = table.concat({...}, " ")
local inst = skynet.launch(service, param)
if inst then
services[inst] = service .. " " .. param
instance[inst] = { session = session, address = address }
else
-- launch request
local service, param = string.match(cmd,"([^ ]+) (.*)")
local inst = skynet.launch(service, param)
if inst then
services[inst] = cmd
instance[inst] = { session = session, address = address }
else
skynet.ret("")
end
skynet.ret("") -- launch failed
end
end)
return NORET
end
function command.ERROR(address)
-- see serivce-src/service_lua.c
-- init failed
local reply = instance[address]
if reply then
skynet.redirect(reply.address , 0, "response", reply.session, "")
instance[address] = nil
end
services[address] = nil
return NORET
end
function command.LAUNCHOK(address)
-- init notice
local reply = instance[address]
if reply then
skynet.redirect(reply.address , 0, "response", reply.session, skynet.address(address))
instance[address] = nil
end
return NORET
end
-- for historical reasons, launcher support text command (for C service)
skynet.register_protocol {
name = "text",
id = skynet.PTYPE_TEXT,
unpack = skynet.tostring,
dispatch = function(session, address , cmd)
if cmd == "" then
command.LAUNCHOK(address)
elseif cmd == "ERROR" then
command.ERROR(address)
else
-- launch request
local service, param = string.match(cmd,"([^ ]+) (.*)")
command.LAUNCH(address, session, service, param)
end
end,
}
skynet.dispatch("lua", function(session, address, cmd , ...)
cmd = string.upper(cmd)
local f = command[cmd]
if f then
local ret = f(...)
local ret = f(address, session, ...)
if ret ~= NORET then
skynet.ret(skynet.pack(ret))
end

194
service/multicastd.lua Normal file
View File

@@ -0,0 +1,194 @@
local skynet = require "skynet"
local mc = require "multicast.c"
local datacenter = require "datacenter"
local harbor_id = skynet.harbor(skynet.self())
local command = {}
local channel = {}
local channel_n = {}
local channel_remote = {}
local channel_id = harbor_id
local NORET = {}
local function get_address(t, id)
local v = assert(datacenter.get("multicast", id))
t[id] = v
return v
end
local node_address = setmetatable({}, { __index = get_address })
-- new LOCAL channel , The low 8bit is the same with harbor_id
function command.NEW()
while channel[channel_id] do
channel_id = mc.nextid(channel_id)
end
channel[channel_id] = {}
channel_n[channel_id] = 0
local ret = channel_id
channel_id = mc.nextid(channel_id)
return ret
end
-- MUST call by the owner node of channel, delete a remote channel
function command.DELR(source, c)
channel[c] = nil
channel_n[c] = nil
return NORET
end
-- delete a channel, if the channel is remote, forward the command to the owner node
-- otherwise, delete the channel, and call all the remote node, DELR
function command.DEL(source, c)
local node = c % 256
if node ~= harbor_id then
skynet.send(node_address[node], "lua", "DEL", c)
return NORET
end
local remote = channel_remote[c]
channel[c] = nil
channel_n[c] = nil
channel_remote[c] = nil
for node in pairs(remote) do
skynet.send(node_address[node], "lua", "DELR", c)
end
return NORET
end
-- forward multicast message to a node (channel id use the session field)
local function remote_publish(node, channel, source, ...)
skynet.redirect(node_address[node], source, "multicast", channel, ...)
end
-- publish a message, for local node, use the message pointer (call mc.bind to add the reference)
-- for remote node, call remote_publish. (call mc.unpack and skynet.tostring to convert message pointer to string)
local function publish(c , source, pack, size)
local group = channel[c]
if group == nil then
-- dead channel, delete the pack
mc.bind(pack, 1)
mc.close(pack)
return
end
mc.bind(pack, channel_n[c])
local msg = skynet.tostring(pack, size)
for k in pairs(group) do
skynet.redirect(k, source, "multicast", c , msg)
end
local remote = channel_remote[c]
if remote then
local _, msg, sz = mc.unpack(pack, size)
local msg = skynet.tostring(msg,sz)
for node in pairs(remote) do
remote_publish(node, c, source, msg)
end
end
end
skynet.register_protocol {
name = "multicast",
id = skynet.PTYPE_MULTICAST,
unpack = function(msg, sz)
return mc.packremote(msg, sz)
end,
dispatch = publish,
}
-- publish a message, if the caller is remote, forward the message to the owner node (by remote_publish)
-- If the caller is local, call publish
function command.PUB(source, c, pack, size)
assert(skynet.harbor(source) == harbor_id)
local node = c % 256
if node ~= harbor_id then
-- remote publish
remote_publish(node, c, source, mc.remote(pack))
else
publish(c, source, pack,size)
end
end
-- the node (source) subscribe a channel
-- MUST call by channel owner node (assert source is not local and channel is create by self)
-- If channel is not exist, return true
-- Else set channel_remote[channel] true
function command.SUBR(source, c)
local node = skynet.harbor(source)
if not channel[c] then
-- channel none exist
return true
end
assert(node ~= harbor_id and c % 256 == harbor_id)
local group = channel_remote[c]
if group == nil then
group = {}
channel_remote[c] = group
end
group[node] = true
end
-- the service (source) subscribe a channel
-- If the channel is remote, node subscribe it by send a SUBR to the owner .
function command.SUB(source, c)
local node = c % 256
if node ~= harbor_id then
-- remote group
if channel[c] == nil then
if skynet.call(node_address[node], "lua", "SUBR", c) then
return
end
if channel[c] == nil then
-- double check, because skynet.call whould yield, other SUB may occur.
channel[c] = {}
channel_n[c] = 0
end
end
end
local group = channel[c]
if group and not group[source] then
channel_n[c] = channel_n[c] + 1
group[source] = true
end
end
-- MUST call by a node, unsubscribe a channel
function command.USUBR(source, c)
local node = skynet.harbor(source)
assert(node ~= harbor_id)
local group = assert(channel_remote[c])
group[node] = nil
return NORET
end
-- Unsubscribe a channel, if the subscriber is empty and the channel is remote, send USUBR to the channel owner
function command.USUB(source, c)
local group = assert(channel[c])
if group[source] then
group[source] = nil
channel_n[c] = channel_n[c] - 1
if channel_n[c] == 0 then
local node = c % 256
if node ~= harbor_id then
-- remote group
channel[c] = nil
channel_n[c] = nil
skynet.send(node_address[node], "lua", "USUBR", c)
end
end
end
return NORET
end
skynet.start(function()
skynet.dispatch("lua", function(_,source, cmd, ...)
local f = assert(command[cmd])
local result = f(source, ...)
if result ~= NORET then
skynet.ret(skynet.pack(result))
end
end)
local self = skynet.self()
local id = skynet.harbor(self)
assert(datacenter.set("multicast", id, self) == nil)
end)

View File

@@ -1,72 +1,191 @@
local skynet = require "skynet"
local snax = require "snax"
local cmd = {}
local service = {}
function cmd.LAUNCH(service_name, ...)
local s = service[service_name]
if type(s) == "number" then
return s
local function request(name, func, ...)
local ok, handle = pcall(func, ...)
local s = service[name]
assert(type(s) == "table")
if ok then
service[name] = handle
else
service[name] = tostring(handle)
end
if s == nil then
s = { launch = true }
service[service_name] = s
elseif s.launch then
assert(type(s) == "table")
local co = coroutine.running()
table.insert(s, co)
skynet.wait()
s = service[service_name]
assert(type(s) == "number")
return s
end
local handle = skynet.newservice(service_name, ...)
for _,v in ipairs(s) do
skynet.wakeup(v)
end
service[service_name] = handle
return handle
if ok then
return handle
else
error(tostring(handle))
end
end
function cmd.QUERY(service_name)
local s = service[service_name]
local function waitfor(name , func, ...)
local s = service[name]
if type(s) == "number" then
return s
end
local co = coroutine.running()
if s == nil then
s = {}
service[service_name] = s
service[name] = s
elseif type(s) == "string" then
error(s)
end
assert(type(s) == "table")
local co = coroutine.running()
if not s.launch and func then
s.launch = true
return request(name, func, ...)
end
table.insert(s, co)
skynet.wait()
s = service[service_name]
s = service[name]
if type(s) == "string" then
error(s)
end
assert(type(s) == "number")
return s
end
local function read_name(service_name)
if string.byte(service_name) == 64 then -- '@'
return string.sub(service_name , 2)
else
return service_name
end
end
function cmd.LAUNCH(service_name, subname, ...)
local realname = read_name(service_name)
if realname == "snaxd" then
return waitfor(service_name.."."..subname, snax.rawnewservice, subname, ...)
else
return waitfor(service_name, skynet.newservice, realname, subname, ...)
end
end
function cmd.QUERY(service_name, subname)
local realname = read_name(service_name)
if realname == "snaxd" then
return waitfor(service_name.."."..subname)
else
return waitfor(service_name)
end
end
local function list_service()
local result = {}
for k,v in pairs(service) do
if type(v) == "string" then
v = "Error: " .. v
elseif type(v) == "table" then
v = "Querying"
else
v = skynet.address(v)
end
result[k] = v
end
return result
end
local function register_global()
function cmd.GLAUNCH(name, ...)
local global_name = "@" .. name
return cmd.LAUNCH(global_name, ...)
end
function cmd.GQUERY(name, ...)
local global_name = "@" .. name
return cmd.QUERY(global_name, ...)
end
local mgr = {}
function cmd.REPORT(m)
mgr[m] = true
skynet.watch(m)
end
local function add_list(all, m)
local harbor = "@" .. skynet.harbor(m)
local result = skynet.call(m, "lua", "LIST")
for k,v in pairs(result) do
all[k .. harbor] = v
end
end
function cmd.LIST()
local result = {}
for k in pairs(mgr) do
pcall(add_list, result, k)
end
local l = list_service()
for k, v in pairs(l) do
result[k] = v
end
return result
end
end
local function register_local()
function cmd.GLAUNCH(name, ...)
local global_name = "@" .. name
return waitfor(global_name, skynet.call, "SERVICE", "lua", "LAUNCH", global_name, ...)
end
function cmd.GQUERY(name, ...)
local global_name = "@" .. name
return waitfor(global_name, skynet.call, "SERVICE", "lua", "QUERY", global_name, ...)
end
function cmd.LIST()
return list_service()
end
skynet.call("SERVICE", "lua", "REPORT", skynet.self())
end
skynet.start(function()
skynet.dispatch("lua", function(session, address, command, service_name , ...)
skynet.dispatch("lua", function(session, address, command, ...)
local f = cmd[command]
if f == nil then
skynet.ret(skynet.pack(nil))
skynet.ret(skynet.pack(nil, "Invalid command " .. command))
return
end
local ok, r = pcall(f, service_name, ...)
local ok, r = pcall(f, ...)
if ok then
skynet.ret(skynet.pack(r))
else
skynet.ret(skynet.pack(nil))
skynet.ret(skynet.pack(nil, r))
end
end)
skynet.register(".service")
local handle = skynet.localname ".service"
if handle ~= 0 then
skynet.error(".service is already register by ", skynet.address(handle))
skynet.exit()
else
skynet.register(".service")
end
if skynet.getenv "standalone" then
skynet.register("SERVICE")
register_global()
else
register_local()
end
end)

View File

@@ -1,7 +1,8 @@
local skynet = require "skynet"
local c = require "skynet.c"
local snax_interface = require "snax_interface"
local snax_interface = require "snax.interface"
local profile = require "profile"
local snax = require "snax"
local func = snax_interface(tostring(...), _ENV)
local mode
@@ -20,8 +21,10 @@ local function update_stat(name, ti)
t.time = t.time + ti
end
local traceback = debug.traceback
local function do_func(f, msg)
return pcall(f, table.unpack(msg))
return xpcall(f, traceback, table.unpack(msg))
end
local function dispatch(f, ...)
@@ -41,7 +44,7 @@ local function message_dispatch()
if method[2] == "accept" then
-- no return
profile.start()
local ok, data = pcall(f, table.unpack(msg))
local ok, data = xpcall(f, traceback, table.unpack(msg))
local ti = profile.stop()
update_stat(method[3], ti)
if not ok then
@@ -49,7 +52,7 @@ local function message_dispatch()
end
else
profile.start()
local ok, data, size = pcall(dispatch, f, table.unpack(msg))
local ok, data, size = xpcall(dispatch, traceback, f, table.unpack(msg))
local ti = profile.stop()
update_stat(method[3], ti)
if ok then
@@ -88,9 +91,9 @@ local function timing( method, ... )
profile.start()
if method[2] == "accept" then
-- no return
err,msg = pcall(method[4], ...)
err,msg = xpcall(method[4], traceback, ...)
else
err,msg = pcall(return_f, method[4], ...)
err,msg = xpcall(return_f, traceback, method[4], ...)
end
local ti = profile.stop()
update_stat(method[3], ti)
@@ -98,13 +101,13 @@ local function timing( method, ... )
end
skynet.start(function()
skynet.dispatch("lua", function ( session , source , id, ...)
skynet.dispatch("snax", function ( session , source , id, ...)
local method = func[id]
if method[2] == "system" then
local command = method[3]
if command == "hotfix" then
local hotfix = require "snax_hotfix"
local hotfix = require "snax.hotfix"
skynet.ret(skynet.pack(hotfix(func, ...)))
elseif command == "init" then
assert(not init, "Already init")

View File

@@ -1,18 +1,16 @@
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include "malloc_hook.h"
#include "jemalloc.h"
#include "skynet.h"
static size_t _used_memory = 0;
static size_t _memory_block = 0;
typedef struct _mem_data {
uint32_t handle;
ssize_t allocated;
uint32_t handle;
ssize_t allocated;
} mem_data;
#define SLOT_SIZE 0x10000
@@ -20,13 +18,18 @@ typedef struct _mem_data {
static mem_data mem_stats[SLOT_SIZE];
#ifndef NOUSE_JEMALLOC
#include "jemalloc.h"
static ssize_t*
get_allocated_field(uint32_t handle) {
int h = (int)(handle & (SLOT_SIZE - 1));
mem_data *data = &mem_stats[h];
int h = (int)(handle & (SLOT_SIZE - 1));
mem_data *data = &mem_stats[h];
uint32_t old_handle = data->handle;
ssize_t old_alloc = data->allocated;
if(old_handle == 0 || old_alloc <= 0) {
if(old_handle == 0 || old_alloc <= 0) {
// data->allocated may less than zero, because it may not count at start.
if(!__sync_bool_compare_and_swap(&data->handle, old_handle, handle)) {
return 0;
@@ -34,150 +37,174 @@ get_allocated_field(uint32_t handle) {
if (old_alloc < 0) {
__sync_bool_compare_and_swap(&data->allocated, old_alloc, 0);
}
}
if(data->handle != handle) {
return 0;
}
return &data->allocated;
}
if(data->handle != handle) {
return 0;
}
return &data->allocated;
}
inline static void
update_xmalloc_stat_alloc(uint32_t handle, size_t __n) {
__sync_add_and_fetch(&_used_memory, __n);
__sync_add_and_fetch(&_memory_block, 1);
ssize_t* allocated = get_allocated_field(handle);
if(allocated) {
__sync_add_and_fetch(allocated, __n);
}
__sync_add_and_fetch(&_used_memory, __n);
__sync_add_and_fetch(&_memory_block, 1);
ssize_t* allocated = get_allocated_field(handle);
if(allocated) {
__sync_add_and_fetch(allocated, __n);
}
}
inline static void
update_xmalloc_stat_free(uint32_t handle, size_t __n) {
__sync_sub_and_fetch(&_used_memory, __n);
__sync_sub_and_fetch(&_memory_block, 1);
ssize_t* allocated = get_allocated_field(handle);
if(allocated) {
__sync_sub_and_fetch(allocated, __n);
}
__sync_sub_and_fetch(&_used_memory, __n);
__sync_sub_and_fetch(&_memory_block, 1);
ssize_t* allocated = get_allocated_field(handle);
if(allocated) {
__sync_sub_and_fetch(allocated, __n);
}
}
inline static void*
fill_prefix(char* ptr) {
uint32_t handle = skynet_current_handle();
size_t size = je_malloc_usable_size(ptr);
uint32_t *p = (uint32_t *)(ptr + size - sizeof(uint32_t));
memcpy(p, &handle, sizeof(handle));
uint32_t handle = skynet_current_handle();
size_t size = je_malloc_usable_size(ptr);
uint32_t *p = (uint32_t *)(ptr + size - sizeof(uint32_t));
memcpy(p, &handle, sizeof(handle));
update_xmalloc_stat_alloc(handle, size);
return ptr;
return ptr;
}
inline static void*
clean_prefix(char* ptr) {
size_t size = je_malloc_usable_size(ptr);
uint32_t *p = (uint32_t *)(ptr + size - sizeof(uint32_t));
uint32_t handle;
memcpy(&handle, p, sizeof(handle));
update_xmalloc_stat_free(handle, size);
return ptr;
size_t size = je_malloc_usable_size(ptr);
uint32_t *p = (uint32_t *)(ptr + size - sizeof(uint32_t));
uint32_t handle;
memcpy(&handle, p, sizeof(handle));
update_xmalloc_stat_free(handle, size);
return ptr;
}
static void malloc_oom(size_t size) {
fprintf(stderr, "xmalloc: Out of memory trying to allocate %zu bytes\n",
size);
fflush(stderr);
abort();
fprintf(stderr, "xmalloc: Out of memory trying to allocate %zu bytes\n",
size);
fflush(stderr);
abort();
}
size_t
malloc_used_memory(void) {
return _used_memory;
void
memory_info_dump(void) {
je_malloc_stats_print(0,0,0);
}
size_t
malloc_memory_block(void) {
return _memory_block;
size_t
mallctl_int64(const char* name, size_t* newval) {
size_t v = 0;
size_t len = sizeof(v);
if(newval) {
je_mallctl(name, &v, &len, newval, sizeof(size_t));
} else {
je_mallctl(name, &v, &len, NULL, 0);
}
// skynet_error(NULL, "name: %s, value: %zd\n", name, v);
return v;
}
void memory_info_dump(void) {
je_malloc_stats_print(0,0,0);
int
mallctl_opt(const char* name, int* newval) {
int v = 0;
size_t len = sizeof(v);
if(newval) {
int ret = je_mallctl(name, &v, &len, newval, sizeof(int));
if(ret == 0) {
skynet_error(NULL, "set new value(%d) for (%s) succeed\n", *newval, name);
} else {
skynet_error(NULL, "set new value(%d) for (%s) failed: error -> %d\n", *newval, name, ret);
}
} else {
je_mallctl(name, &v, &len, NULL, 0);
}
return v;
}
size_t mallctl_int64(const char* name, size_t* newval) {
size_t v = 0;
size_t len = sizeof(v);
if(newval) {
je_mallctl(name, &v, &len, newval, sizeof(size_t));
} else {
je_mallctl(name, &v, &len, NULL, 0);
}
// printf("name: %s, value: %zd\n", name, v);
return v;
}
int mallctl_opt(const char* name, int* newval) {
int v = 0;
size_t len = sizeof(v);
if(newval) {
int ret = je_mallctl(name, &v, &len, newval, sizeof(int));
if(ret == 0) {
printf("set new value(%d) for (%s) succeed\n", *newval, name);
} else {
printf("set new value(%d) for (%s) failed: error -> %d\n", *newval, name, ret);
}
} else {
je_mallctl(name, &v, &len, NULL, 0);
}
return v;
}
void
dump_c_mem() {
int i;
size_t total = 0;
printf("dump all service mem:\n");
for(i=0; i<SLOT_SIZE; i++) {
mem_data* data = &mem_stats[i];
if(data->handle != 0 && data->allocated != 0) {
total += data->allocated;
printf("0x%x -> %zdkb\n", data->handle, data->allocated >> 10);
}
}
printf("+total: %zdkb\n",total >> 10);
}
// hook : malloc, realloc, memalign, free, calloc
// hook : malloc, realloc, free, calloc
void *
skynet_malloc(size_t size) {
void* ptr = je_malloc(size + PREFIX_SIZE);
if(!ptr) malloc_oom(size);
return fill_prefix(ptr);
void* ptr = je_malloc(size + PREFIX_SIZE);
if(!ptr) malloc_oom(size);
return fill_prefix(ptr);
}
void *
skynet_realloc(void *ptr, size_t size) {
if (ptr == NULL) return skynet_malloc(size);
if (ptr == NULL) return skynet_malloc(size);
void* rawptr = clean_prefix(ptr);
void *newptr = je_realloc(rawptr, size+PREFIX_SIZE);
if(!newptr) malloc_oom(size);
return fill_prefix(newptr);
void* rawptr = clean_prefix(ptr);
void *newptr = je_realloc(rawptr, size+PREFIX_SIZE);
if(!newptr) malloc_oom(size);
return fill_prefix(newptr);
}
void
skynet_free(void *ptr) {
if (ptr == NULL) return;
void* rawptr = clean_prefix(ptr);
je_free(rawptr);
if (ptr == NULL) return;
void* rawptr = clean_prefix(ptr);
je_free(rawptr);
}
void *
skynet_calloc(size_t nmemb,size_t size) {
void* ptr = je_calloc(nmemb + ((PREFIX_SIZE+size-1)/size), size );
if(!ptr) malloc_oom(size);
return fill_prefix(ptr);
void* ptr = je_calloc(nmemb + ((PREFIX_SIZE+size-1)/size), size );
if(!ptr) malloc_oom(size);
return fill_prefix(ptr);
}
#else
void
memory_info_dump(void) {
skynet_error(NULL, "No jemalloc");
}
size_t
mallctl_int64(const char* name, size_t* newval) {
skynet_error(NULL, "No jemalloc : mallctl_int64 %s.", name);
return 0;
}
int
mallctl_opt(const char* name, int* newval) {
skynet_error(NULL, "No jemalloc : mallctl_opt %s.", name);
return 0;
}
#endif
size_t
malloc_used_memory(void) {
return _used_memory;
}
size_t
malloc_memory_block(void) {
return _memory_block;
}
void
dump_c_mem() {
int i;
size_t total = 0;
skynet_error(NULL, "dump all service mem:");
for(i=0; i<SLOT_SIZE; i++) {
mem_data* data = &mem_stats[i];
if(data->handle != 0 && data->allocated != 0) {
total += data->allocated;
skynet_error(NULL, "0x%x -> %zdkb", data->handle, data->allocated >> 10);
}
}
skynet_error(NULL, "+total: %zdkb",total >> 10);
}
char *
@@ -197,4 +224,3 @@ skynet_lalloc(void *ud, void *ptr, size_t osize, size_t nsize) {
return skynet_realloc(ptr, nsize);
}
}

View File

@@ -8,17 +8,18 @@
#define PTYPE_TEXT 0
#define PTYPE_RESPONSE 1
#define PTYPE_MULTICAST_DEPRECATED 2
#define PTYPE_MULTICAST 2
#define PTYPE_CLIENT 3
#define PTYPE_SYSTEM 4
#define PTYPE_HARBOR 5
#define PTYPE_SOCKET 6
// read lualib/skynet.lua lualib/simplemonitor.lua
#define PTYPE_RESERVED_ERROR 7
// read lualib/skynet.lua lualib/mqueue.lua
// read lualib/skynet.lua examples/simplemonitor.lua
#define PTYPE_ERROR 7
// read lualib/skynet.lua lualib/mqueue.lua lualib/snax.lua
#define PTYPE_RESERVED_QUEUE 8
#define PTYPE_RESERVED_DEBUG 9
#define PTYPE_RESERVED_LUA 10
#define PTYPE_RESERVED_SNAX 11
#define PTYPE_TAG_DONTCOPY 0x10000
#define PTYPE_TAG_ALLOCSESSION 0x20000

View File

@@ -0,0 +1,96 @@
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/file.h>
#include <signal.h>
#include <errno.h>
#include <stdlib.h>
#include "skynet_daemon.h"
static int
check_pid(const char *pidfile) {
int pid = 0;
FILE *f = fopen(pidfile,"r");
if (f == NULL)
return 0;
int n = fscanf(f,"%d", &pid);
fclose(f);
if (n !=1 || pid == 0 || pid == getpid()) {
return 0;
}
if (kill(pid, 0) && errno == ESRCH)
return 0;
return pid;
}
static int
write_pid(const char *pidfile) {
FILE *f;
int pid = 0;
int fd = open(pidfile, O_RDWR|O_CREAT, 0644);
if (fd == -1) {
fprintf(stderr, "Can't create %s.\n", pidfile);
return 0;
}
f = fdopen(fd, "r+");
if (f == NULL) {
fprintf(stderr, "Can't open %s.\n", pidfile);
return 0;
}
if (flock(fd, LOCK_EX|LOCK_NB) == -1) {
int n = fscanf(f, "%d", &pid);
fclose(f);
if (n != 1) {
fprintf(stderr, "Can't lock and read pidfile.\n");
} else {
fprintf(stderr, "Can't lock pidfile, lock is held by pid %d.\n", pid);
}
return 0;
}
pid = getpid();
if (!fprintf(f,"%d\n", pid)) {
fprintf(stderr, "Can't write pid.\n");
close(fd);
return 0;
}
fflush(f);
return pid;
}
int
daemon_init(const char *pidfile) {
int pid = check_pid(pidfile);
if (pid) {
fprintf(stderr, "Skynet is already running, pid = %d.\n", pid);
return 1;
}
#ifdef __APPLE__
fprintf(stderr, "'daemon' is deprecated: first deprecated in OS X 10.5 , use launchd instead.\n");
#else
if (daemon(1,0)) {
fprintf(stderr, "Can't daemonize.\n");
return 1;
}
#endif
pid = write_pid(pidfile);
if (pid == 0) {
return 1;
}
return 0;
}
int
daemon_exit(const char *pidfile) {
return unlink(pidfile);
}

View File

@@ -0,0 +1,7 @@
#ifndef skynet_daemon_h
#define skynet_daemon_h
int daemon_init(const char *pidfile);
int daemon_exit(const char *pidfile);
#endif

View File

@@ -9,6 +9,7 @@
#include <string.h>
#define DEFAULT_SLOT_SIZE 4
#define MAX_SLOT_SIZE 0x40000000
struct handle_name {
char * name;
@@ -168,6 +169,7 @@ static void
_insert_name_before(struct handle_storage *s, char *name, uint32_t handle, int before) {
if (s->name_count >= s->name_cap) {
s->name_cap *= 2;
assert(s->name_cap <= MAX_SLOT_SIZE);
struct handle_name * n = skynet_malloc(s->name_cap * sizeof(struct handle_name));
int i;
for (i=0;i<before;i++) {

View File

@@ -7,18 +7,20 @@
#include <assert.h>
static struct skynet_context * REMOTE = 0;
static unsigned int HARBOR = 0;
static unsigned int HARBOR = ~0;
void
skynet_harbor_send(struct remote_message *rmsg, uint32_t source, int session) {
int type = rmsg->sz >> HANDLE_REMOTE_SHIFT;
rmsg->sz &= HANDLE_MASK;
assert(type != PTYPE_SYSTEM && type != PTYPE_HARBOR);
assert(type != PTYPE_SYSTEM && type != PTYPE_HARBOR && REMOTE);
skynet_context_send(REMOTE, rmsg, sizeof(*rmsg) , source, type , session);
}
void
skynet_harbor_register(struct remote_name *rname) {
if (REMOTE == NULL)
return;
int i;
int number = 1;
for (i=0;i<GLOBALNAME_LENGTH;i++) {
@@ -34,6 +36,7 @@ skynet_harbor_register(struct remote_name *rname) {
int
skynet_harbor_message_isremote(uint32_t handle) {
assert(HARBOR != ~0);
int h = (handle & ~HANDLE_MASK);
return h != HARBOR && h !=0;
}
@@ -43,16 +46,7 @@ skynet_harbor_init(int harbor) {
HARBOR = (unsigned int)harbor << HANDLE_REMOTE_SHIFT;
}
int
skynet_harbor_start(const char * master, const char *local) {
size_t sz = strlen(master) + strlen(local) + 32;
char args[sz];
sprintf(args, "%s %s %d",master,local,HARBOR >> HANDLE_REMOTE_SHIFT);
struct skynet_context * inst = skynet_context_new("harbor",args);
if (inst == NULL) {
return 1;
}
REMOTE = inst;
return 0;
void
skynet_harbor_start(void *ctx) {
REMOTE = ctx;
}

View File

@@ -26,6 +26,6 @@ void skynet_harbor_send(struct remote_message *rmsg, uint32_t source, int sessio
void skynet_harbor_register(struct remote_name *rname);
int skynet_harbor_message_isremote(uint32_t handle);
void skynet_harbor_init(int harbor);
int skynet_harbor_start(const char * master, const char *local);
void skynet_harbor_start(void * ctx);
#endif

View File

@@ -4,14 +4,17 @@
struct skynet_config {
int thread;
int harbor;
const char * logger;
const char * daemon;
const char * module_path;
const char * master;
const char * local;
const char * start;
const char * standalone;
const char * bootstrap;
};
#define THREAD_WORKER 0
#define THREAD_MAIN 1
#define THREAD_SOCKET 2
#define THREAD_TIMER 3
#define THREAD_MONITOR 4
void skynet_start(struct skynet_config * config);
#endif

View File

@@ -2,6 +2,7 @@
#include "skynet_imp.h"
#include "skynet_env.h"
#include "skynet_server.h"
#include <stdio.h>
#include <stdlib.h>
@@ -34,6 +35,7 @@ optboolean(const char *key, int opt) {
return strcmp(str,"true")==0;
}
*/
static const char *
optstring(const char *key,const char * opt) {
const char * str = skynet_getenv(key);
@@ -87,6 +89,7 @@ main(int argc, char *argv[]) {
if (argc > 1) {
config_file = argv[1];
}
skynet_globalinit();
skynet_env_init();
sigign();
@@ -107,30 +110,16 @@ main(int argc, char *argv[]) {
}
_init_env(L);
#ifdef LUA_CACHELIB
printf("Skynet lua code cache enable\n");
#endif
const char *path = optstring("lua_path","./lualib/?.lua;./lualib/?/init.lua");
setenv("LUA_PATH",path,1);
const char *cpath = optstring("lua_cpath","./luaclib/?.so");
setenv("LUA_CPATH",cpath,1);
optstring("luaservice","./service/?.lua");
config.thread = optint("thread",8);
config.module_path = optstring("cpath","./cservice/?.so");
config.logger = optstring("logger",NULL);
config.harbor = optint("harbor", 1);
config.master = optstring("master","127.0.0.1:2012");
config.start = optstring("start","main.lua");
config.local = optstring("address","127.0.0.1:2525");
config.standalone = optstring("standalone",NULL);
config.bootstrap = optstring("bootstrap","snlua bootstrap");
config.daemon = optstring("daemon", NULL);
lua_close(L);
skynet_start(&config);
printf("skynet exit\n");
skynet_globalexit();
return 0;
}

View File

@@ -3,14 +3,10 @@
#include <stddef.h>
#ifdef SKYNET_MALLOC_RENAME
#define malloc skynet_malloc
#define calloc skynet_calloc
#define realloc skynet_realloc
#define free skynet_free
#endif
#define skynet_malloc malloc
#define skynet_calloc calloc
#define skynet_realloc realloc
#define skynet_free free
void * skynet_malloc(size_t sz);
void * skynet_calloc(size_t nmemb,size_t size);

View File

@@ -13,12 +13,8 @@
// 0 means mq is not in global mq.
// 1 means mq is in global mq , or the message is dispatching.
// 2 means message is dispatching with locked session set.
// 3 means mq is not in global mq, and locked session has been set.
#define MQ_IN_GLOBAL 1
#define MQ_DISPATCHING 2
#define MQ_LOCKED 3
struct message_queue {
uint32_t handle;
@@ -27,17 +23,18 @@ struct message_queue {
int tail;
int lock;
int release;
int lock_session;
int in_global;
struct skynet_message *queue;
struct message_queue *next;
};
struct global_queue {
uint32_t head;
uint32_t tail;
struct message_queue ** queue;
bool * flag;
// We use a separated flag array to ensure the mq is pushed.
// See the comments below.
struct message_queue *list;
};
static struct global_queue *Q = NULL;
@@ -47,36 +44,61 @@ static struct global_queue *Q = NULL;
#define GP(p) ((p) % MAX_GLOBAL_MQ)
static void
void
skynet_globalmq_push(struct message_queue * queue) {
struct global_queue *q= Q;
uint32_t tail = GP(__sync_fetch_and_add(&q->tail,1));
q->queue[tail] = queue;
__sync_synchronize();
q->flag[tail] = true;
// only one thread can set the slot (change q->queue[tail] from NULL to queue)
if (!__sync_bool_compare_and_swap(&q->queue[tail], NULL, queue)) {
// The queue may full seldom, save queue in list
assert(queue->next == NULL);
struct message_queue * last;
do {
last = q->list;
queue->next = last;
} while(!__sync_bool_compare_and_swap(&q->list, last, queue));
return;
}
}
struct message_queue *
skynet_globalmq_pop() {
struct global_queue *q = Q;
uint32_t head = q->head;
if (head == q->tail) {
// The queue is empty.
return NULL;
}
uint32_t head_ptr = GP(head);
if (head_ptr == GP(q->tail)) {
return NULL;
}
if(!q->flag[head_ptr]) {
return NULL;
struct message_queue * list = q->list;
if (list) {
// If q->list is not empty, try to load it back to the queue
struct message_queue *newhead = list->next;
if (__sync_bool_compare_and_swap(&q->list, list, newhead)) {
// try load list only once, if success , push it back to the queue.
list->next = NULL;
skynet_globalmq_push(list);
}
}
__sync_synchronize();
struct message_queue * mq = q->queue[head_ptr];
if (mq == NULL) {
// globalmq push not complete
return NULL;
}
if (!__sync_bool_compare_and_swap(&q->head, head, head+1)) {
return NULL;
}
q->flag[head_ptr] = false;
// only one thread can get the slot (change q->queue[head_ptr] to NULL)
if (!__sync_bool_compare_and_swap(&q->queue[head_ptr], mq, NULL)) {
return NULL;
}
return mq;
}
@@ -89,16 +111,20 @@ skynet_mq_create(uint32_t handle) {
q->head = 0;
q->tail = 0;
q->lock = 0;
// When the queue is create (always between service create and service init) ,
// set in_global flag to avoid push it to global queue .
// If the service init success, skynet_context_new will call skynet_mq_force_push to push it to global queue.
q->in_global = MQ_IN_GLOBAL;
q->release = 0;
q->lock_session = 0;
q->queue = skynet_malloc(sizeof(struct skynet_message) * q->cap);
q->next = NULL;
return q;
}
static void
_release(struct message_queue *q) {
assert(q->next == NULL);
skynet_free(q->queue);
skynet_free(q);
}
@@ -161,113 +187,37 @@ expand_queue(struct message_queue *q) {
q->queue = new_queue;
}
static void
_unlock(struct message_queue *q) {
// this api use in push a unlock message, so the in_global flags must not be 0 ,
// but the q is not exist in global queue.
if (q->in_global == MQ_LOCKED) {
skynet_globalmq_push(q);
q->in_global = MQ_IN_GLOBAL;
} else {
assert(q->in_global == MQ_DISPATCHING);
}
q->lock_session = 0;
}
static void
_pushhead(struct message_queue *q, struct skynet_message *message) {
int head = q->head - 1;
if (head < 0) {
head = q->cap - 1;
}
if (head == q->tail) {
expand_queue(q);
--q->tail;
head = q->cap - 1;
}
q->queue[head] = *message;
q->head = head;
_unlock(q);
}
void
skynet_mq_push(struct message_queue *q, struct skynet_message *message) {
assert(message);
LOCK(q)
if (q->lock_session !=0 && message->session == q->lock_session) {
_pushhead(q,message);
} else {
q->queue[q->tail] = *message;
if (++ q->tail >= q->cap) {
q->tail = 0;
}
if (q->head == q->tail) {
expand_queue(q);
}
q->queue[q->tail] = *message;
if (++ q->tail >= q->cap) {
q->tail = 0;
}
if (q->lock_session == 0) {
if (q->in_global == 0) {
q->in_global = MQ_IN_GLOBAL;
skynet_globalmq_push(q);
}
}
if (q->head == q->tail) {
expand_queue(q);
}
if (q->in_global == 0) {
q->in_global = MQ_IN_GLOBAL;
skynet_globalmq_push(q);
}
UNLOCK(q)
}
void
skynet_mq_lock(struct message_queue *q, int session) {
LOCK(q)
assert(q->lock_session == 0);
assert(q->in_global == MQ_IN_GLOBAL);
q->in_global = MQ_DISPATCHING;
q->lock_session = session;
UNLOCK(q)
}
void
skynet_mq_unlock(struct message_queue *q) {
LOCK(q)
_unlock(q);
UNLOCK(q)
}
void
skynet_mq_init() {
struct global_queue *q = skynet_malloc(sizeof(*q));
memset(q,0,sizeof(*q));
q->queue = skynet_malloc(MAX_GLOBAL_MQ * sizeof(struct message_queue *));
q->flag = skynet_malloc(MAX_GLOBAL_MQ * sizeof(bool));
memset(q->flag, 0, sizeof(bool) * MAX_GLOBAL_MQ);
memset(q->queue, 0, sizeof(struct message_queue *) * MAX_GLOBAL_MQ);
Q=q;
}
void
skynet_mq_force_push(struct message_queue * queue) {
assert(queue->in_global);
skynet_globalmq_push(queue);
}
void
skynet_mq_pushglobal(struct message_queue *queue) {
LOCK(queue)
assert(queue->in_global);
if (queue->in_global == MQ_DISPATCHING) {
// lock message queue just now.
queue->in_global = MQ_LOCKED;
}
if (queue->lock_session == 0) {
skynet_globalmq_push(queue);
queue->in_global = MQ_IN_GLOBAL;
}
UNLOCK(queue)
}
void
skynet_mq_mark_release(struct message_queue *q) {
LOCK(q)
@@ -279,31 +229,24 @@ skynet_mq_mark_release(struct message_queue *q) {
UNLOCK(q)
}
static int
_drop_queue(struct message_queue *q) {
// todo: send message back to message source
static void
_drop_queue(struct message_queue *q, message_drop drop_func, void *ud) {
struct skynet_message msg;
int s = 0;
while(!skynet_mq_pop(q, &msg)) {
++s;
skynet_free(msg.data);
drop_func(&msg, ud);
}
_release(q);
return s;
}
int
skynet_mq_release(struct message_queue *q) {
int ret = 0;
void
skynet_mq_release(struct message_queue *q, message_drop drop_func, void *ud) {
LOCK(q)
if (q->release) {
UNLOCK(q)
ret = _drop_queue(q);
_drop_queue(q, drop_func, ud);
} else {
skynet_mq_force_push(q);
skynet_globalmq_push(q);
UNLOCK(q)
}
return ret;
}

View File

@@ -13,25 +13,24 @@ struct skynet_message {
struct message_queue;
void skynet_globalmq_push(struct message_queue * queue);
struct message_queue * skynet_globalmq_pop(void);
struct message_queue * skynet_mq_create(uint32_t handle);
void skynet_mq_mark_release(struct message_queue *q);
int skynet_mq_release(struct message_queue *q);
typedef void (*message_drop)(struct skynet_message *, void *);
void skynet_mq_release(struct message_queue *q, message_drop drop_func, void *ud);
uint32_t skynet_mq_handle(struct message_queue *);
// 0 for success
int skynet_mq_pop(struct message_queue *q, struct skynet_message *message);
void skynet_mq_push(struct message_queue *q, struct skynet_message *message);
void skynet_mq_lock(struct message_queue *q, int session);
void skynet_mq_unlock(struct message_queue *q);
// return the length of message queue, for debug
int skynet_mq_length(struct message_queue *q);
void skynet_mq_force_push(struct message_queue *q);
void skynet_mq_pushglobal(struct message_queue *q);
void skynet_mq_init();
#endif

View File

@@ -8,6 +8,9 @@
#include "skynet_harbor.h"
#include "skynet_env.h"
#include "skynet_monitor.h"
#include "skynet_imp.h"
#include <pthread.h>
#include <string.h>
#include <assert.h>
@@ -50,10 +53,10 @@ struct skynet_context {
struct skynet_node {
int total;
uint32_t monitor_exit;
pthread_key_t handle_key;
};
static struct skynet_node G_NODE = { 0,0 };
static __thread uint32_t handle_tls = 0xffffffff;
static struct skynet_node G_NODE;
int
skynet_context_total() {
@@ -61,22 +64,23 @@ skynet_context_total() {
}
static void
_context_inc() {
context_inc() {
__sync_fetch_and_add(&G_NODE.total,1);
}
static void
_context_dec() {
context_dec() {
__sync_fetch_and_sub(&G_NODE.total,1);
}
uint32_t
skynet_current_handle(void) {
return handle_tls;
void * handle = pthread_getspecific(G_NODE.handle_key);
return (uint32_t)(uintptr_t)handle;
}
static void
_id_to_hex(char * str, uint32_t id) {
id_to_hex(char * str, uint32_t id) {
int i;
static char hex[16] = { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' };
str[0] = ':';
@@ -86,6 +90,20 @@ _id_to_hex(char * str, uint32_t id) {
str[9] = '\0';
}
struct drop_t {
uint32_t handle;
};
static void
drop_message(struct skynet_message *msg, void *ud) {
struct drop_t *d = ud;
skynet_free(msg->data);
uint32_t source = d->handle;
assert(source);
// report error to the message source
skynet_send(NULL, source, msg->source, PTYPE_ERROR, 0, NULL, 0);
}
struct skynet_context *
skynet_context_new(const char * name, const char *param) {
struct skynet_module * mod = skynet_module_query(name);
@@ -111,7 +129,7 @@ skynet_context_new(const char * name, const char *param) {
ctx->handle = skynet_handle_register(ctx);
struct message_queue * queue = ctx->queue = skynet_mq_create(ctx->handle);
// init function maybe use ctx->handle, so it must init at last
_context_inc();
context_inc();
CHECKCALLING_BEGIN(ctx)
int r = skynet_module_instance_init(mod, inst, ctx, param);
@@ -121,16 +139,18 @@ skynet_context_new(const char * name, const char *param) {
if (ret) {
ctx->init = true;
}
skynet_mq_force_push(queue);
skynet_globalmq_push(queue);
if (ret) {
skynet_error(ret, "LAUNCH %s %s", name, param ? param : "");
}
return ret;
} else {
skynet_error(ctx, "FAILED launch %s", name);
uint32_t handle = ctx->handle;
skynet_context_release(ctx);
skynet_handle_retire(ctx->handle);
skynet_mq_release(queue);
skynet_handle_retire(handle);
struct drop_t d = { handle };
skynet_mq_release(queue, drop_message, &d);
return NULL;
}
}
@@ -148,17 +168,17 @@ skynet_context_grab(struct skynet_context *ctx) {
}
static void
_delete_context(struct skynet_context *ctx) {
delete_context(struct skynet_context *ctx) {
skynet_module_instance_release(ctx->mod, ctx->instance);
skynet_mq_mark_release(ctx->queue);
skynet_free(ctx);
_context_dec();
context_dec();
}
struct skynet_context *
skynet_context_release(struct skynet_context *ctx) {
if (__sync_sub_and_fetch(&ctx->ref,1) == 0) {
_delete_context(ctx);
delete_context(ctx);
return NULL;
}
return ctx;
@@ -199,59 +219,63 @@ static void
_dispatch_message(struct skynet_context *ctx, struct skynet_message *msg) {
assert(ctx->init);
CHECKCALLING_BEGIN(ctx)
handle_tls = ctx->handle;
pthread_setspecific(G_NODE.handle_key, (void *)(uintptr_t)(ctx->handle));
int type = msg->sz >> HANDLE_REMOTE_SHIFT;
size_t sz = msg->sz & HANDLE_MASK;
if (!ctx->cb(ctx, ctx->cb_ud, type, msg->session, msg->source, msg->data, sz)) {
skynet_free(msg->data);
}
handle_tls = 0xffffffff;
CHECKCALLING_END(ctx)
}
int
skynet_context_message_dispatch(struct skynet_monitor *sm) {
struct message_queue * q = skynet_globalmq_pop();
if (q==NULL)
return 1;
struct message_queue *
skynet_context_message_dispatch(struct skynet_monitor *sm, struct message_queue *q) {
if (q == NULL) {
q = skynet_globalmq_pop();
if (q==NULL)
return NULL;
}
uint32_t handle = skynet_mq_handle(q);
struct skynet_context * ctx = skynet_handle_grab(handle);
if (ctx == NULL) {
int s = skynet_mq_release(q);
if (s>0) {
skynet_error(NULL, "Drop message queue %x (%d messages)", handle,s);
}
return 0;
struct drop_t d = { handle };
skynet_mq_release(q, drop_message, &d);
return skynet_globalmq_pop();
}
struct skynet_message msg;
if (skynet_mq_pop(q,&msg)) {
skynet_context_release(ctx);
return 0;
return skynet_globalmq_pop();
}
skynet_monitor_trigger(sm, msg.source , handle);
if (ctx->cb == NULL) {
skynet_free(msg.data);
skynet_error(NULL, "Drop message from %x to %x without callback , size = %d",msg.source, handle, (int)msg.sz);
} else {
_dispatch_message(ctx, &msg);
}
assert(q == ctx->queue);
skynet_mq_pushglobal(q);
struct message_queue *nq = skynet_globalmq_pop();
if (nq) {
// If global mq is not empty , push q back, and return next queue (nq)
// Else (global mq is empty or block, don't push q back, and return q again (for next dispatch)
skynet_globalmq_push(q);
q = nq;
}
skynet_context_release(ctx);
skynet_monitor_trigger(sm, 0,0);
return 0;
return q;
}
static void
_copy_name(char name[GLOBALNAME_LENGTH], const char * addr) {
copy_name(char name[GLOBALNAME_LENGTH], const char * addr) {
int i;
for (i=0;i<GLOBALNAME_LENGTH && addr[i];i++) {
name[i] = addr[i];
@@ -287,192 +311,225 @@ handle_exit(struct skynet_context * context, uint32_t handle) {
skynet_handle_retire(handle);
}
const char *
skynet_command(struct skynet_context * context, const char * cmd , const char * param) {
if (strcmp(cmd,"TIMEOUT") == 0) {
char * session_ptr = NULL;
int ti = strtol(param, &session_ptr, 10);
int session = skynet_context_newsession(context);
skynet_timeout(context->handle, ti, session);
sprintf(context->result, "%d", session);
// skynet command
struct command_func {
const char *name;
const char * (*func)(struct skynet_context * context, const char * param);
};
static const char *
cmd_timeout(struct skynet_context * context, const char * param) {
char * session_ptr = NULL;
int ti = strtol(param, &session_ptr, 10);
int session = skynet_context_newsession(context);
skynet_timeout(context->handle, ti, session);
sprintf(context->result, "%d", session);
return context->result;
}
static const char *
cmd_reg(struct skynet_context * context, const char * param) {
if (param == NULL || param[0] == '\0') {
sprintf(context->result, ":%x", context->handle);
return context->result;
} else if (param[0] == '.') {
return skynet_handle_namehandle(context->handle, param + 1);
} else {
assert(context->handle!=0);
struct remote_name *rname = skynet_malloc(sizeof(*rname));
copy_name(rname->name, param);
rname->handle = context->handle;
skynet_harbor_register(rname);
return NULL;
}
}
static const char *
cmd_query(struct skynet_context * context, const char * param) {
if (param[0] == '.') {
uint32_t handle = skynet_handle_findname(param+1);
sprintf(context->result, ":%x", handle);
return context->result;
}
return NULL;
}
if (strcmp(cmd,"LOCK") == 0) {
if (context->init == false) {
return NULL;
}
skynet_mq_lock(context->queue, context->session_id+1);
static const char *
cmd_name(struct skynet_context * context, const char * param) {
int size = strlen(param);
char name[size+1];
char handle[size+1];
sscanf(param,"%s %s",name,handle);
if (handle[0] != ':') {
return NULL;
}
if (strcmp(cmd,"UNLOCK") == 0) {
if (context->init == false) {
return NULL;
}
skynet_mq_unlock(context->queue);
uint32_t handle_id = strtoul(handle+1, NULL, 16);
if (handle_id == 0) {
return NULL;
}
if (strcmp(cmd,"REG") == 0) {
if (param == NULL || param[0] == '\0') {
sprintf(context->result, ":%x", context->handle);
return context->result;
} else if (param[0] == '.') {
return skynet_handle_namehandle(context->handle, param + 1);
} else {
assert(context->handle!=0);
struct remote_name *rname = skynet_malloc(sizeof(*rname));
_copy_name(rname->name, param);
rname->handle = context->handle;
skynet_harbor_register(rname);
return NULL;
}
if (name[0] == '.') {
return skynet_handle_namehandle(handle_id, name + 1);
} else {
struct remote_name *rname = skynet_malloc(sizeof(*rname));
copy_name(rname->name, name);
rname->handle = handle_id;
skynet_harbor_register(rname);
}
return NULL;
}
if (strcmp(cmd,"QUERY") == 0) {
if (param[0] == '.') {
uint32_t handle = skynet_handle_findname(param+1);
sprintf(context->result, ":%x", handle);
static const char *
cmd_now(struct skynet_context * context, const char * param) {
uint32_t ti = skynet_gettime();
sprintf(context->result,"%u",ti);
return context->result;
}
static const char *
cmd_exit(struct skynet_context * context, const char * param) {
handle_exit(context, 0);
return NULL;
}
static const char *
cmd_kill(struct skynet_context * context, const char * param) {
uint32_t handle = 0;
if (param[0] == ':') {
handle = strtoul(param+1, NULL, 16);
} else if (param[0] == '.') {
handle = skynet_handle_findname(param+1);
} else {
skynet_error(context, "Can't kill %s",param);
// todo : kill global service
}
if (handle) {
handle_exit(context, handle);
}
return NULL;
}
static const char *
cmd_launch(struct skynet_context * context, const char * param) {
size_t sz = strlen(param);
char tmp[sz+1];
strcpy(tmp,param);
char * args = tmp;
char * mod = strsep(&args, " \t\r\n");
args = strsep(&args, "\r\n");
struct skynet_context * inst = skynet_context_new(mod,args);
if (inst == NULL) {
return NULL;
} else {
id_to_hex(context->result, inst->handle);
return context->result;
}
}
static const char *
cmd_getenv(struct skynet_context * context, const char * param) {
return skynet_getenv(param);
}
static const char *
cmd_setenv(struct skynet_context * context, const char * param) {
size_t sz = strlen(param);
char key[sz+1];
int i;
for (i=0;param[i] != ' ' && param[i];i++) {
key[i] = param[i];
}
if (param[i] == '\0')
return NULL;
key[i] = '\0';
param += i+1;
skynet_setenv(key,param);
return NULL;
}
static const char *
cmd_starttime(struct skynet_context * context, const char * param) {
uint32_t sec = skynet_gettime_fixsec();
sprintf(context->result,"%u",sec);
return context->result;
}
static const char *
cmd_endless(struct skynet_context * context, const char * param) {
if (context->endless) {
strcpy(context->result, "1");
context->endless = false;
return context->result;
}
return NULL;
}
static const char *
cmd_abort(struct skynet_context * context, const char * param) {
skynet_handle_retireall();
return NULL;
}
static const char *
cmd_monitor(struct skynet_context * context, const char * param) {
uint32_t handle=0;
if (param == NULL || param[0] == '\0') {
if (G_NODE.monitor_exit) {
// return current monitor serivce
sprintf(context->result, ":%x", G_NODE.monitor_exit);
return context->result;
}
return NULL;
}
if (strcmp(cmd,"NAME") == 0) {
int size = strlen(param);
char name[size+1];
char handle[size+1];
sscanf(param,"%s %s",name,handle);
if (handle[0] != ':') {
return NULL;
}
uint32_t handle_id = strtoul(handle+1, NULL, 16);
if (handle_id == 0) {
return NULL;
}
if (name[0] == '.') {
return skynet_handle_namehandle(handle_id, name + 1);
} else {
struct remote_name *rname = skynet_malloc(sizeof(*rname));
_copy_name(rname->name, name);
rname->handle = handle_id;
skynet_harbor_register(rname);
}
return NULL;
}
if (strcmp(cmd,"NOW") == 0) {
uint32_t ti = skynet_gettime();
sprintf(context->result,"%u",ti);
return context->result;
}
if (strcmp(cmd,"EXIT") == 0) {
handle_exit(context, 0);
return NULL;
}
if (strcmp(cmd,"KILL") == 0) {
uint32_t handle = 0;
} else {
if (param[0] == ':') {
handle = strtoul(param+1, NULL, 16);
} else if (param[0] == '.') {
handle = skynet_handle_findname(param+1);
} else {
skynet_error(context, "Can't kill %s",param);
// todo : kill global service
}
if (handle) {
handle_exit(context, handle);
}
return NULL;
}
if (strcmp(cmd,"LAUNCH") == 0) {
size_t sz = strlen(param);
char tmp[sz+1];
strcpy(tmp,param);
char * args = tmp;
char * mod = strsep(&args, " \t\r\n");
args = strsep(&args, "\r\n");
struct skynet_context * inst = skynet_context_new(mod,args);
if (inst == NULL) {
return NULL;
} else {
_id_to_hex(context->result, inst->handle);
return context->result;
skynet_error(context, "Can't monitor %s",param);
// todo : monitor global service
}
}
G_NODE.monitor_exit = handle;
return NULL;
}
if (strcmp(cmd,"GETENV") == 0) {
return skynet_getenv(param);
}
static const char *
cmd_mqlen(struct skynet_context * context, const char * param) {
int len = skynet_mq_length(context->queue);
sprintf(context->result, "%d", len);
return context->result;
}
if (strcmp(cmd,"SETENV") == 0) {
size_t sz = strlen(param);
char key[sz+1];
int i;
for (i=0;param[i] != ' ' && param[i];i++) {
key[i] = param[i];
static struct command_func cmd_funcs[] = {
{ "TIMEOUT", cmd_timeout },
{ "REG", cmd_reg },
{ "QUERY", cmd_query },
{ "NAME", cmd_name },
{ "NOW", cmd_now },
{ "EXIT", cmd_exit },
{ "KILL", cmd_kill },
{ "LAUNCH", cmd_launch },
{ "GETENV", cmd_getenv },
{ "SETENV", cmd_setenv },
{ "STARTTIME", cmd_starttime },
{ "ENDLESS", cmd_endless },
{ "ABORT", cmd_abort },
{ "MONITOR", cmd_monitor },
{ "MQLEN", cmd_mqlen },
{ NULL, NULL },
};
const char *
skynet_command(struct skynet_context * context, const char * cmd , const char * param) {
struct command_func * method = &cmd_funcs[0];
while(method->name) {
if (strcmp(cmd, method->name) == 0) {
return method->func(context, param);
}
if (param[i] == '\0')
return NULL;
key[i] = '\0';
param += i+1;
skynet_setenv(key,param);
return NULL;
}
if (strcmp(cmd,"STARTTIME") == 0) {
uint32_t sec = skynet_gettime_fixsec();
sprintf(context->result,"%u",sec);
return context->result;
}
if (strcmp(cmd,"ENDLESS") == 0) {
if (context->endless) {
strcpy(context->result, "1");
context->endless = false;
return context->result;
}
return NULL;
}
if (strcmp(cmd,"ABORT") == 0) {
skynet_handle_retireall();
return NULL;
}
if (strcmp(cmd,"MONITOR") == 0) {
uint32_t handle=0;
if (param == NULL || param[0] == '\0') {
if (G_NODE.monitor_exit) {
// return current monitor serivce
sprintf(context->result, ":%x", G_NODE.monitor_exit);
return context->result;
}
return NULL;
} else {
if (param[0] == ':') {
handle = strtoul(param+1, NULL, 16);
} else if (param[0] == '.') {
handle = skynet_handle_findname(param+1);
} else {
skynet_error(context, "Can't monitor %s",param);
// todo : monitor global service
}
}
G_NODE.monitor_exit = handle;
return NULL;
}
if (strcmp(cmd, "MQLEN") == 0) {
int len = skynet_mq_length(context->queue);
sprintf(context->result, "%d", len);
return context->result;
++method;
}
return NULL;
@@ -526,7 +583,6 @@ skynet_send(struct skynet_context * context, uint32_t source, uint32_t destinati
if (skynet_context_push(destination, &smsg)) {
skynet_free(data);
skynet_error(NULL, "Drop message from %x to %x (type=%d)(size=%d)", source, destination, type&0xff, (int)(sz & HANDLE_MASK));
return -1;
}
}
@@ -543,16 +599,15 @@ skynet_sendname(struct skynet_context * context, const char * addr , int type, i
des = skynet_handle_findname(addr + 1);
if (des == 0) {
if (type & PTYPE_TAG_DONTCOPY) {
skynet_free(data);
}
skynet_error(context, "Drop message to %s", addr);
skynet_free(data);
}
return session;
}
} else {
_filter_args(context, type, &session, (void **)&data, &sz);
struct remote_message * rmsg = skynet_malloc(sizeof(*rmsg));
_copy_name(rmsg->destination.name, addr);
copy_name(rmsg->destination.name, addr);
rmsg->destination.handle = 0;
rmsg->message = data;
rmsg->sz = sz;
@@ -590,3 +645,27 @@ skynet_context_send(struct skynet_context * ctx, void * msg, size_t sz, uint32_t
skynet_mq_push(ctx->queue, &smsg);
}
void
skynet_globalinit(void) {
G_NODE.total = 0;
G_NODE.monitor_exit = 0;
if (pthread_key_create(&G_NODE.handle_key, NULL)) {
fprintf(stderr, "pthread_key_create failed");
exit(1);
}
// set mainthread's key
skynet_initthread(THREAD_MAIN);
}
void
skynet_globalexit(void) {
pthread_key_delete(G_NODE.handle_key);
}
void
skynet_initthread(int m) {
uintptr_t v = (uint32_t)(-m);
pthread_setspecific(G_NODE.handle_key, (void *)v);
}

View File

@@ -16,9 +16,13 @@ void skynet_context_init(struct skynet_context *, uint32_t handle);
int skynet_context_push(uint32_t handle, struct skynet_message *message);
void skynet_context_send(struct skynet_context * context, void * msg, size_t sz, uint32_t source, int type, int session);
int skynet_context_newsession(struct skynet_context *);
int skynet_context_message_dispatch(struct skynet_monitor *); // return 1 when block
struct message_queue * skynet_context_message_dispatch(struct skynet_monitor *, struct message_queue *); // return next queue
int skynet_context_total();
void skynet_context_endless(uint32_t handle); // for monitor
void skynet_globalinit(void);
void skynet_globalexit(void);
void skynet_initthread(int m);
#endif

View File

@@ -5,9 +5,9 @@
#include "skynet_handle.h"
#include "skynet_module.h"
#include "skynet_timer.h"
#include "skynet_harbor.h"
#include "skynet_monitor.h"
#include "skynet_socket.h"
#include "skynet_daemon.h"
#include <pthread.h>
#include <unistd.h>
@@ -50,6 +50,7 @@ wakeup(struct monitor *m, int busy) {
static void *
_socket(void *p) {
struct monitor * m = p;
skynet_initthread(THREAD_SOCKET);
for (;;) {
int r = skynet_socket_poll();
if (r==0)
@@ -81,6 +82,7 @@ _monitor(void *p) {
struct monitor * m = p;
int i;
int n = m->count;
skynet_initthread(THREAD_MONITOR);
for (;;) {
CHECK_ABORT
for (i=0;i<n;i++) {
@@ -98,6 +100,7 @@ _monitor(void *p) {
static void *
_timer(void *p) {
struct monitor * m = p;
skynet_initthread(THREAD_TIMER);
for (;;) {
skynet_updatetime();
CHECK_ABORT
@@ -117,8 +120,11 @@ _worker(void *p) {
int id = wp->id;
struct monitor *m = wp->m;
struct skynet_monitor *sm = m->m[id];
skynet_initthread(THREAD_WORKER);
struct message_queue * q = NULL;
for (;;) {
if (skynet_context_message_dispatch(sm)) {
q = skynet_context_message_dispatch(sm, q);
if (q == NULL) {
CHECK_ABORT
if (pthread_mutex_lock(&m->mutex) == 0) {
++ m->sleep;
@@ -177,16 +183,26 @@ _start(int thread) {
free_monitor(m);
}
static int
_start_master(const char * master) {
struct skynet_context *ctx = skynet_context_new("master", master);
if (ctx == NULL)
return 1;
return 0;
static void
bootstrap(const char * cmdline) {
int sz = strlen(cmdline);
char name[sz+1];
char args[sz+1];
sscanf(cmdline, "%s %s", name, args);
struct skynet_context *ctx = skynet_context_new(name, args);
if (ctx == NULL) {
skynet_error(NULL, "Bootstrap error : %s\n", cmdline);
exit(1);
}
}
void
skynet_start(struct skynet_config * config) {
if (config->daemon) {
if (daemon_init(config->daemon)) {
exit(1);
}
}
skynet_harbor_init(config->harbor);
skynet_handle_init(config->harbor);
skynet_mq_init();
@@ -194,32 +210,11 @@ skynet_start(struct skynet_config * config) {
skynet_timer_init();
skynet_socket_init();
struct skynet_context *ctx;
ctx = skynet_context_new("logger", config->logger);
if (ctx == NULL) {
fprintf(stderr,"launch logger error");
exit(1);
}
if (config->standalone) {
if (_start_master(config->standalone)) {
fprintf(stderr, "Init fail : mater");
return;
}
}
// harbor must be init first
if (skynet_harbor_start(config->master , config->local)) {
fprintf(stderr, "Init fail : no master");
return;
}
ctx = skynet_context_new("snlua", "launcher");
if (ctx) {
skynet_command(ctx, "REG", ".launcher");
ctx = skynet_context_new("snlua", config->start);
}
bootstrap(config->bootstrap);
_start(config->thread);
skynet_socket_free();
if (config->daemon) {
daemon_exit(config->daemon);
}
}

View File

@@ -16,6 +16,9 @@
typedef void (*timer_execute_func)(void *ud,void *arg);
#define LOCK(q) while (__sync_lock_test_and_set(&(q)->lock,1)) {}
#define UNLOCK(q) __sync_lock_release(&(q)->lock);
#define TIME_NEAR_SHIFT 8
#define TIME_NEAR (1 << TIME_NEAR_SHIFT)
#define TIME_LEVEL_SHIFT 6
@@ -50,8 +53,7 @@ struct timer {
static struct timer * TI = NULL;
static inline struct timer_node *
link_clear(struct link_list *list)
{
link_clear(struct link_list *list) {
struct timer_node * ret = list->head.next;
list->head.next = 0;
list->tail = &(list->head);
@@ -60,23 +62,20 @@ link_clear(struct link_list *list)
}
static inline void
link(struct link_list *list,struct timer_node *node)
{
link(struct link_list *list,struct timer_node *node) {
list->tail->next = node;
list->tail = node;
node->next=0;
}
static void
add_node(struct timer *T,struct timer_node *node)
{
add_node(struct timer *T,struct timer_node *node) {
int time=node->expire;
int current_time=T->time;
if ((time|TIME_NEAR_MASK)==(current_time|TIME_NEAR_MASK)) {
link(&T->near[time&TIME_NEAR_MASK],node);
}
else {
} else {
int i;
int mask=TIME_NEAR << TIME_LEVEL_SHIFT;
for (i=0;i<3;i++) {
@@ -90,21 +89,21 @@ add_node(struct timer *T,struct timer_node *node)
}
static void
timer_add(struct timer *T,void *arg,size_t sz,int time)
{
timer_add(struct timer *T,void *arg,size_t sz,int time) {
struct timer_node *node = (struct timer_node *)skynet_malloc(sizeof(*node)+sz);
memcpy(node+1,arg,sz);
while (__sync_lock_test_and_set(&T->lock,1)) {};
LOCK(T);
node->expire=time+T->time;
add_node(T,node);
__sync_lock_release(&T->lock);
UNLOCK(T);
}
static void
timer_shift(struct timer *T) {
LOCK(T);
int mask = TIME_NEAR;
int time = (++T->time) >> TIME_NEAR_SHIFT;
int i=0;
@@ -125,50 +124,57 @@ timer_shift(struct timer *T) {
time >>= TIME_LEVEL_SHIFT;
++i;
}
UNLOCK(T);
}
static inline void
dispatch_list(struct timer_node *current) {
do {
struct timer_event * event = (struct timer_event *)(current+1);
struct skynet_message message;
message.source = 0;
message.session = event->session;
message.data = NULL;
message.sz = PTYPE_RESPONSE << HANDLE_REMOTE_SHIFT;
skynet_context_push(event->handle, &message);
struct timer_node * temp = current;
current=current->next;
skynet_free(temp);
} while (current);
}
static inline void
timer_execute(struct timer *T) {
LOCK(T);
int idx = T->time & TIME_NEAR_MASK;
while (T->near[idx].head.next) {
struct timer_node *current = link_clear(&T->near[idx]);
do {
struct timer_event * event = (struct timer_event *)(current+1);
struct skynet_message message;
message.source = 0;
message.session = event->session;
message.data = NULL;
message.sz = PTYPE_RESPONSE << HANDLE_REMOTE_SHIFT;
skynet_context_push(event->handle, &message);
struct timer_node * temp = current;
current=current->next;
skynet_free(temp);
} while (current);
UNLOCK(T);
// dispatch_list don't need lock T
dispatch_list(current);
LOCK(T);
}
UNLOCK(T);
}
static void
timer_update(struct timer *T)
{
while (__sync_lock_test_and_set(&T->lock,1)) {};
timer_update(struct timer *T) {
// try to dispatch timeout 0 (rare condition)
timer_execute(T);
// shift time first, and then dispatch timer message
timer_shift(T);
timer_execute(T);
__sync_lock_release(&T->lock);
}
static struct timer *
timer_create_timer()
{
timer_create_timer() {
struct timer *r=(struct timer *)skynet_malloc(sizeof(struct timer));
memset(r,0,sizeof(*r));

View File

@@ -815,6 +815,22 @@ report_accept(struct socket_server *ss, struct socket *s, struct socket_message
return 1;
}
static inline void
clear_closed_event(struct socket_server *ss, struct socket_message * result, int type) {
if (type == SOCKET_CLOSE || type == SOCKET_ERROR) {
int id = result->id;
int i;
for (i=ss->event_index; i<ss->event_n; i++) {
struct event *e = &ss->ev[i];
struct socket *s = e->s;
if (s) {
if (s->type == SOCKET_TYPE_INVALID && s->id == id) {
e->s = NULL;
}
}
}
}
}
// return type
int
@@ -823,9 +839,10 @@ socket_server_poll(struct socket_server *ss, struct socket_message * result, int
if (ss->checkctrl) {
if (has_cmd(ss)) {
int type = ctrl_cmd(ss, result);
if (type != -1)
if (type != -1) {
clear_closed_event(ss, result, type);
return type;
else
} else
continue;
} else {
ss->checkctrl = 0;
@@ -865,12 +882,14 @@ socket_server_poll(struct socket_server *ss, struct socket_message * result, int
int type = send_buffer(ss, s, result);
if (type == -1)
break;
clear_closed_event(ss, result, type);
return type;
}
if (e->read) {
int type = forward_message(ss, s, result);
if (type == -1)
break;
clear_closed_event(ss, result, type);
return type;
}
break;

View File

@@ -1,12 +0,0 @@
local skynet = require "skynet"
local mqueue = require "mqueue"
skynet.start(function()
local id = 0
local pingserver = skynet.newservice "pingserver"
mqueue.register(function(str)
id = id + 1
str = string.format("id = %d , %s",id, str)
return skynet.call(pingserver, "lua", "PING", str)
end)
end)

27
test/testdeadcall.lua Normal file
View File

@@ -0,0 +1,27 @@
local skynet = require "skynet"
local mode = ...
if mode == "test" then
skynet.start(function()
skynet.dispatch("lua", function (...)
print("====>", ...)
skynet.exit()
end)
end)
else
skynet.start(function()
local test = skynet.newservice("testdeadcall", "test") -- launch self in test mode
print(pcall(function()
skynet.send(test,"lua", "hello world")
skynet.send(test,"lua", "never get there")
skynet.call(test,"lua", "fake call")
end))
skynet.exit()
end)
end

View File

@@ -1,9 +0,0 @@
local skynet = require "skynet"
local log = require "log"
skynet.start(function()
log.Info("hello world")
skynet.exit()
end)

40
test/testmulticast.lua Normal file
View File

@@ -0,0 +1,40 @@
local skynet = require "skynet"
local mc = require "multicast"
local dc = require "datacenter"
local mode = ...
if mode == "sub" then
skynet.start(function()
skynet.dispatch("lua", function (_,_, cmd, channel)
assert(cmd == "init")
local c = mc.new {
channel = channel ,
dispatch = function (channel, source, ...)
print(string.format("%s <=== %s %s",skynet.address(skynet.self()),skynet.address(source), channel), ...)
end
}
print(skynet.address(skynet.self()), "sub", c)
c:subscribe()
skynet.ret(skynet.pack())
end)
end)
else
skynet.start(function()
local channel = mc.new()
print("New channel", channel)
for i=1,10 do
local sub = skynet.newservice("testmulticast", "sub")
skynet.call(sub, "lua", "init", channel.channel)
end
dc.set("MCCHANNEL", channel.channel) -- for multi node test
print(skynet.address(skynet.self()), "===>", channel)
channel:publish("Hello World")
end)
end

24
test/testmulticast2.lua Normal file
View File

@@ -0,0 +1,24 @@
local skynet = require "skynet"
local dc = require "datacenter"
local mc = require "multicast"
skynet.start(function()
print("remote start")
skynet.monitor("simplemonitor", true)
local console = skynet.newservice("console")
local channel = dc.get "MCCHANNEL"
for i=1,10 do
local sub = skynet.newservice("testmulticast", "sub")
skynet.call(sub, "lua", "init", channel)
end
local c = mc.new {
channel = channel ,
dispatch = function(...) print("======>", ...) end,
}
c:subscribe()
c:publish("Remote message")
c:unsubscribe()
c:publish("Remote message2")
c:delete()
skynet.exit()
end)

View File

@@ -2,7 +2,7 @@ local skynet = require "skynet"
local snax = require "snax"
skynet.start(function()
local ps = snax.newservice ("pingserver", "hello world")
local ps = snax.uniqueservice ("pingserver", "hello world")
print(ps.req.ping("foobar"))
print(ps.post.hello())
print(pcall(ps.req.error))

View File

@@ -37,7 +37,8 @@ else
end
skynet.start(function()
local id = socket.listen("127.0.0.1", 8000)
local id = socket.listen("127.0.0.1", 8001)
print("Listen socket :", "127.0.0.1", 8001)
socket.start(id , function(id, addr)
print("connect from " .. addr .. " " .. id)