Compare commits

..

70 Commits

Author SHA1 Message Date
云风
4e4dacbb4d Merge pull request #130 from cloudwu/dev
Dev
2014-07-07 20:53:38 +08:00
Cloud Wu
8f8b844bde ready for release 2014-07-07 20:52:34 +08:00
Cloud Wu
54f4d94ba2 bugfix: create queue first 2014-07-07 19:50:31 +08:00
Cloud Wu
4967dc2fce skynet.task return session:traceback 2014-07-07 19:00:22 +08:00
Cloud Wu
ece89a1b49 add new api skynet.task() 2014-07-07 18:29:27 +08:00
Cloud Wu
f874fdc618 throw error when skynet.exit 2014-07-03 17:40:07 +08:00
Cloud Wu
711c04e6a9 bugfix: redirect should pass session (0) 2014-07-03 17:13:47 +08:00
Cloud Wu
1e0189962b bugfix: dead lock when service_harbor exit 2014-06-30 11:31:24 +08:00
Cloud Wu
f4d865a882 release v0.4.0 2014-06-30 10:07:55 +08:00
云风
60b0976253 Merge pull request #129 from cloudwu/dev
pre release v0.4.0
2014-06-30 10:05:30 +08:00
Cloud Wu
2b9efcec3f revert 2014-06-26 15:12:16 +08:00
Cloud Wu
d343f3b374 use atomic operation 2014-06-26 15:06:47 +08:00
Cloud Wu
ceeb9912bf typo 2014-06-26 14:04:57 +08:00
Cloud Wu
be1db23ea3 get now before starttime would be better 2014-06-25 11:59:54 +08:00
Cloud Wu
36cab8e060 add skynet.time() 2014-06-25 11:15:28 +08:00
云风
b1d6f7fd88 Merge pull request #127 from xjdrew/real_bugfix_time
Real bugfix time
2014-06-25 11:00:01 +08:00
xjdrew
e0d8b00226 bugfix: ntp校验时把时间往后调整 2014-06-25 10:53:56 +08:00
xjdrew
1cbe639186 bugfix: time rewind 2014-06-25 00:50:42 +08:00
Cloud Wu
f849c522bb bugfix: silly bug 2014-06-24 21:06:40 +08:00
Cloud Wu
3cadd29796 don't throw error when destination address is invalid 2014-06-24 19:32:15 +08:00
Cloud Wu
db952dc658 report too long message error, instead of assert 2014-06-24 19:22:18 +08:00
Cloud Wu
813e6c764d add assert 2014-06-24 18:30:20 +08:00
Cloud Wu
79b6b80676 bugfix: harbor socket package split 2014-06-24 18:19:04 +08:00
Cloud Wu
d30a206a15 bugfix: typo 2014-06-24 11:51:52 +08:00
Cloud Wu
cc2cffacb7 bugfix: master, don't report offline slave 2014-06-24 11:33:29 +08:00
Cloud Wu
d8b0e977f3 merge history 2014-06-23 11:06:20 +08:00
Cloud Wu
2128a82571 update history, and release v0.3.2 2014-06-23 11:01:20 +08:00
Cloud Wu
4e2dc64f8f Merge branch 'master' into dev 2014-06-21 18:03:24 +08:00
Cloud Wu
d910686cb8 cluster bugfix : add socket.header to decode bigendian header 2014-06-21 18:03:08 +08:00
Cloud Wu
9937081854 Redesign harbor/master/dummy service 2014-06-21 17:01:59 +08:00
Cloud Wu
7137850eb2 restore config_log 2014-06-20 03:02:51 +08:00
Cloud Wu
c3eb5cd202 After connecting, socket can send before connected. remove block connect api 2014-06-20 02:49:48 +08:00
Cloud Wu
1a44bfb09a launch logger in C 2014-06-19 22:12:29 +08:00
Cloud Wu
e4197daa54 minor optimize 2014-06-19 16:29:54 +08:00
Cloud Wu
d0468a39f4 cluster.open support cluster name 2014-06-19 16:25:50 +08:00
Cloud Wu
ad9898a209 add module skynet.harbor 2014-06-19 15:43:41 +08:00
云风
33e51a4de9 Merge pull request #124 from xjdrew/bugfix_redis
optimize redis compose_message, see redistest for changes
2014-06-18 20:30:10 +08:00
xjdrew
4ccb677e84 optimize redis compose_message, see redistest for changes
test case: start redistest
2014-06-18 20:20:45 +08:00
Cloud Wu
942c2d3a67 freebsd can use malloc hook now 2014-06-18 15:10:21 +08:00
Cloud Wu
44bef99f64 use skynet.error instead of print 2014-06-18 15:10:21 +08:00
Cloud Wu
078ac2bcd2 bugfix: double free 2014-06-18 15:09:15 +08:00
云风
c7887cceb7 Merge pull request #119 from cloudwu/dev
release v0.3.1
2014-06-16 10:11:18 +08:00
Cloud Wu
4fbd433199 release v0.3.1 2014-06-16 10:10:18 +08:00
Cloud Wu
87d276caf6 typo fix 2014-06-12 17:28:54 +08:00
Cloud Wu
0f64e909fa big-endian encoding bson objectid 2014-06-11 15:11:18 +08:00
Cloud Wu
4bc2e800fd bugfix: lua mongo result 2014-06-09 18:08:23 +08:00
Cloud Wu
bf6501f2ee check mongo reply data stream 2014-06-09 17:43:40 +08:00
Cloud Wu
f92fc153e4 bugfix: in freebsd, libthr may call calloc before globalinit 2014-06-04 02:51:29 +00:00
Cloud Wu
c10e5412c0 invalid length may nagtive 2014-06-04 10:23:49 +08:00
Cloud Wu
9477cc8171 turn off memory hook in freeBSD 2014-06-03 12:35:00 +00:00
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
59 changed files with 1808 additions and 864 deletions

7
.gitignore vendored
View File

@@ -1,9 +1,10 @@
*.o
*.a
skynet
./skynet
./skynet.pid
3rd/lua/lua
3rd/lua/luac
cservice
luaclib
./cservice
./luaclib
*.so
*.dSYM

View File

@@ -1,3 +1,42 @@
v0.4.1 (2014-7-7)
-----------
* Add SERVICE_NAME in loader
* Throw error back when skynet.error
* Add skynet.task
* Bugfix for last version (harbor service bugs)
v0.4.0 (2014-6-30)
-----------
* Optimize redis driver `compose_message`.
* Add module skynet.harbor for monitor harbor connect/disconnect, see test/testharborlink.lua .
* cluster.open support cluster name.
* Add new api skynet.packstring , and skynet.unpack support lua string
* socket.listen support put port into address. (address:port)
* Redesign harbor/master/dummy, remove lots of C code and rewite in lua.
* Remove block connect api, queue sending message during connecting now.
* Add skynet.time()
v0.3.2 (2014-6-23)
----------
* Bugfix : cluster (double free).
* Add socket.header() to decode big-endian package header (and fix the bug in cluster).
v0.3.1 (2014-6-16)
-----------
* Bugfix: lua mongo driver . Hold reply string before decode bson data.
* More check in bson decoding.
* Use big-endian for encoding bson objectid.
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

View File

@@ -40,13 +40,15 @@ jemalloc : $(MALLOC_STATICLIB)
# skynet
CSERVICE = snlua logger gate master harbor
LUA_CLIB = skynet socketdriver int64 bson mongo md5 netpack cjson clientsocket memory profile multicast
CSERVICE = snlua logger gate harbor
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 \
@@ -64,7 +66,7 @@ $(CSERVICE_PATH) :
define CSERVICE_TEMP
$$(CSERVICE_PATH)/$(1).so : service-src/service_$(1).c | $$(CSERVICE_PATH)
$(CC) $$(CFLAGS) $$(SHARED) $$< -o $$@ -Iskynet-src
$$(CC) $$(CFLAGS) $$(SHARED) $$< -o $$@ -Iskynet-src
endef
$(foreach v, $(CSERVICE), $(eval $(call CSERVICE_TEMP,$(v))))
@@ -105,6 +107,9 @@ $(LUA_CLIB_PATH)/profile.so : lualib-src/lua-profile.c | $(LUA_CLIB_PATH)
$(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

@@ -1,6 +1,6 @@
## Build
Install autoconf first for jemalloc
For linux, install autoconf first for jemalloc
```
git clone git@github.com:cloudwu/skynet.git
@@ -15,6 +15,8 @@ export PLAT=linux
make
```
For freeBSD , use gmake instead of make .
## Test
Run these in different console

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 "db"
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

@@ -12,3 +12,4 @@ 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"

View File

@@ -1,8 +1,8 @@
local skynet = require "skynet"
skynet.start(function()
skynet.dispatch("text", function(session, address, text)
print("[GLOBALLOG]", skynet.address(address),text)
skynet.dispatch("lua", function(session, address, ...)
print("[GLOBALLOG]", skynet.address(address), ...)
end)
skynet.register "LOG"
end)

View File

@@ -2,7 +2,6 @@ local skynet = require "skynet"
skynet.start(function()
print("Log server start")
local service = skynet.newservice("service_mgr")
skynet.monitor "simplemonitor"
local log = skynet.newservice("globallog")
skynet.exit()

View File

@@ -12,7 +12,7 @@ skynet.register_protocol {
local w = service_map[address]
if w then
for watcher in pairs(w) do
skynet.redirect(watcher, address, "error", "")
skynet.redirect(watcher, address, "error", 0, "")
end
service_map[address] = false
end

View File

@@ -1,5 +1,3 @@
#include "skynet_malloc.h"
#include <lua.h>
#include <lauxlib.h>
@@ -70,10 +68,17 @@ struct bson_reader {
int size;
};
static inline int32_t
get_length(const uint8_t * data) {
const uint8_t * b = (const uint8_t *)data;
int32_t len = b[0] | b[1]<<8 | b[2]<<16 | b[3]<<24;
return len;
}
static inline void
bson_destroy(struct bson *b) {
if (b->ptr != b->buffer) {
skynet_free(b->ptr);
free(b->ptr);
}
}
@@ -93,10 +98,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 +263,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 +464,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 +539,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 = %d", sz);
}
lua_pushlstring(L, read_bytes(L, &t, sz), sz-1);
break;
}
@@ -643,7 +651,7 @@ static int
lmakeindex(lua_State *L) {
int32_t *bson = luaL_checkudata(L,1,"bson");
const uint8_t * start = (const uint8_t *)bson;
struct bson_reader br = { start+4, *bson - 5 };
struct bson_reader br = { start+4, get_length(start) - 5 };
lua_newtable(L);
for (;;) {
@@ -806,6 +814,9 @@ lreplace(lua_State *L) {
write_int64(&b, i);
break;
}
default:
luaL_error(L, "Can't replace type %d", type);
break;
}
return 0;
}
@@ -816,7 +827,9 @@ ldecode(lua_State *L) {
if (data == NULL) {
return 0;
}
struct bson_reader br = { (const uint8_t *)data , *data };
const uint8_t * b = (const uint8_t *)data;
int32_t len = get_length(b);
struct bson_reader br = { b , len };
unpack_dict(L, &br, false);
@@ -865,7 +878,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);
@@ -1136,14 +1149,14 @@ lobjectid(lua_State *L) {
}
} else {
time_t ti = time(NULL);
oid[2] = ti & 0xff;
oid[3] = (ti>>8) & 0xff;
oid[4] = (ti>>16) & 0xff;
oid[5] = (ti>>24) & 0xff;
oid[2] = (ti>>24) & 0xff;
oid[3] = (ti>>16) & 0xff;
oid[4] = (ti>>8) & 0xff;
oid[5] = ti & 0xff;
memcpy(oid+6 , oid_header, 5);
oid[11] = oid_counter & 0xff;
oid[11] = (oid_counter>>16) & 0xff;
oid[12] = (oid_counter>>8) & 0xff;
oid[13] = (oid_counter>>16) & 0xff;
oid[13] = oid_counter & 0xff;
++oid_counter;
}
lua_pushlstring( L, (const char *)oid, 14);
@@ -1190,3 +1203,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);
// clusterd.lua:command.socket call lpackresponse,
// and the msg/sz is return by skynet.rawcall , so don't free(msg)
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);
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

@@ -260,6 +260,13 @@ op_reply(lua_State *L) {
lua_pushnil(L);
lua_rawseti(L, 2, i);
}
} else {
if (sz >= 4) {
sz -= get_length((document)doc);
}
}
if (sz != 0) {
return luaL_error(L, "Invalid result bson document");
}
lua_pushboolean(L,1);
lua_pushinteger(L, id);

View File

@@ -581,8 +581,16 @@ _luaseri_unpack(lua_State *L) {
if (lua_isnoneornil(L,1)) {
return 0;
}
void * buffer = lua_touserdata(L,1);
int len = luaL_checkinteger(L,2);
void * buffer;
int len;
if (lua_type(L,1) == LUA_TSTRING) {
size_t sz;
buffer = (void *)lua_tolstring(L,1,&sz);
len = (int)sz;
} else {
buffer = lua_touserdata(L,1);
len = luaL_checkinteger(L,2);
}
if (len == 0) {
return 0;
}

View File

@@ -149,7 +149,7 @@ _sendname(lua_State *L, struct skynet_context * context, const char * dest) {
luaL_error(L, "skynet.send invalid param %s", lua_typename(L,lua_type(L,4)));
}
if (session < 0) {
luaL_error(L, "skynet.send session (%d) < 0", session);
return 0;
}
lua_pushinteger(L,session);
return 1;
@@ -221,7 +221,7 @@ _send(lua_State *L) {
}
if (session < 0) {
// send to invalid address
// todo: maybe throw error is better
// todo: maybe throw error whould be better
return 0;
}
lua_pushinteger(L,session);
@@ -289,6 +289,16 @@ _harbor(lua_State *L) {
return 2;
}
static int
lpackstring(lua_State *L) {
_luaseri_pack(L);
char * str = (char *)lua_touserdata(L, -2);
int sz = lua_tointeger(L, -1);
lua_pushlstring(L, str, sz);
skynet_free(str);
return 1;
}
int
luaopen_skynet_c(lua_State *L) {
luaL_checkversion(L);
@@ -303,6 +313,7 @@ luaopen_skynet_c(lua_State *L) {
{ "harbor", _harbor },
{ "pack", _luaseri_pack },
{ "unpack", _luaseri_unpack },
{ "packstring", lpackstring },
{ "callback", _callback },
{ NULL, NULL },
};

View File

@@ -188,10 +188,29 @@ pop_lstring(lua_State *L, struct socket_buffer *sb, int sz, int skip) {
luaL_pushresult(&b);
}
static int
lheader(lua_State *L) {
size_t len;
const uint8_t * s = (const uint8_t *)luaL_checklstring(L, 1, &len);
if (len > 4 || len < 1) {
return luaL_error(L, "Invalid read %s", s);
}
int i;
size_t sz = 0;
for (i=0;i<(int)len;i++) {
sz <<= 8;
sz |= s[i];
}
lua_pushunsigned(L, sz);
return 1;
}
/*
userdata send_buffer
table pool
integer sz
integer sz
*/
static int
lpopbuffer(lua_State *L) {
@@ -469,6 +488,7 @@ luaopen_socketdriver(lua_State *L) {
{ "clear", lclearbuffer },
{ "readline", lreadline },
{ "str2p", lstr2p },
{ "header", lheader },
{ "unpack", lunpack },
{ NULL, NULL },

23
lualib/cluster.lua Normal file
View File

@@ -0,0 +1,23 @@
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)
if type(port) == "string" then
skynet.call(clusterd, "lua", "listen", port)
else
skynet.call(clusterd, "lua", "listen", "0.0.0.0", port)
end
end
skynet.init(function()
clusterd = skynet.uniqueservice("clusterd")
end)
return cluster

View File

@@ -3,11 +3,13 @@ for word in string.gmatch(..., "%S+") do
table.insert(args, word)
end
SERVICE_NAME = args[1]
local main, pattern
local err = {}
for pat in string.gmatch(LUA_SERVICE, "([^;]+);*") do
local filename = string.gsub(pat, "?", args[1])
local filename = string.gsub(pat, "?", SERVICE_NAME)
local f, msg = loadfile(filename)
if not f then
table.insert(err, msg)

View File

@@ -98,7 +98,7 @@ function mongo.client( conf )
host = obj.host,
port = obj.port,
response = dispatch_reply,
auth = mongo_auth(conf),
auth = mongo_auth(obj),
}
setmetatable(obj, client_meta)
obj.__sock:connect(true) -- try connect only once
@@ -167,7 +167,9 @@ function mongo_db:runCommand(cmd,cmd_v,...)
bson_cmd = bson_encode_order(cmd,cmd_v,...)
end
local pack = driver.query(request_id, 0, self.__cmd, 0, 1, bson_cmd)
local doc = sock:request(pack, request_id).document
-- we must hold req (req.data), because req.document is a lightuserdata, it's a pointer to the string (req.data)
local req = sock:request(pack, request_id)
local doc = req.document
return bson_decode(doc)
end
@@ -224,7 +226,9 @@ function mongo_collection:findOne(query, selector)
local request_id = conn:genId()
local sock = conn.__sock
local pack = driver.query(request_id, 0, self.full_name, 0, 1, query and bson_encode(query) or empty_bson, selector and bson_encode(selector))
local doc = sock:request(pack, request_id).document
-- we must hold req (req.data), because req.document is a lightuserdata, it's a pointer to the string (req.data)
local req = sock:request(pack, request_id)
local doc = req.document
return bson_decode(doc)
end

View File

@@ -1,6 +1,7 @@
local skynet = require "skynet"
local socket = require "socket"
local socketchannel = require "socketchannel"
local int64 = require "int64"
local table = table
local string = string
@@ -94,31 +95,56 @@ function command:disconnect()
setmetatable(self, nil)
end
local function compose_message(msg)
if #msg == 1 then
return msg[1] .. "\r\n"
-- msg could be any type of value
local function pack_value(lines, v)
if v == nil then
return
end
local lines = { "*" .. #msg }
for _,v in ipairs(msg) do
local t = type(v)
if t == "number" then
v = tostring(v)
elseif t == "userdata" then
v = int64.tostring(int64.new(v),10)
end
table.insert(lines,"$"..#v)
table.insert(lines,v)
end
table.insert(lines,"")
local cmd = table.concat(lines,"\r\n")
return cmd
local t = type(v)
if t == "number" then
v = tostring(v)
elseif t == "userdata" then
v = int64.tostring(int64.new(v),10)
end
table.insert(lines,"$"..#v)
table.insert(lines,v)
end
local function compose_message(cmd, msg)
local len = 1
local t = type(msg)
if t == "table" then
len = len + #msg
elseif t ~= nil then
len = len + 1
end
local lines = {"*" .. len}
pack_value(lines, cmd)
if t == "table" then
for _,v in ipairs(msg) do
pack_value(lines, v)
end
else
pack_value(lines, msg)
end
table.insert(lines, "")
local chunk = table.concat(lines,"\r\n")
return chunk
end
setmetatable(command, { __index = function(t,k)
local cmd = string.upper(k)
local f = function (self, ...)
return self[1]:request(compose_message { cmd, ... }, read_response)
local f = function (self, v, ...)
if type(v) == "table" then
return self[1]:request(compose_message(cmd, v), read_response)
else
return self[1]:request(compose_message(cmd, {v, ...}), read_response)
end
end
t[k] = f
return f
@@ -131,12 +157,12 @@ end
function command:exists(key)
local fd = self[1]
return fd:request(compose_message { "EXISTS", key }, read_boolean)
return fd:request(compose_message ("EXISTS", key), read_boolean)
end
function command:sismember(key, value)
local fd = self[1]
return fd:request(compose_message { "SISMEMBER", key, value }, read_boolean)
return fd:request(compose_message ("SISMEMBER", {key, value}), read_boolean)
end
--- watch mode
@@ -156,10 +182,10 @@ local function watch_login(obj, auth)
so:request("AUTH "..auth.."\r\n", read_response)
end
for k in pairs(obj.__psubscribe) do
so:request(compose_message { "PSUBSCRIBE", k })
so:request(compose_message ("PSUBSCRIBE", k))
end
for k in pairs(obj.__subscribe) do
so:request(compose_message { "SUBSCRIBE", k })
so:request(compose_message("SUBSCRIBE", k))
end
end
end
@@ -192,7 +218,7 @@ local function watch_func( name )
local so = self.__sock
for i = 1, select("#", ...) do
local v = select(i, ...)
so:request(compose_message { NAME, v })
so:request(compose_message(NAME, v))
end
end
end

View File

@@ -190,12 +190,33 @@ function skynet.wait()
session_id_coroutine[session] = nil
end
local function globalname(name, handle)
local c = string.sub(name,1,1)
assert(c ~= ':')
if c == '.' then
return false
end
assert(#name <= 16) -- GLOBALNAME_LENGTH is 16, defined in skynet_harbor.h
assert(tonumber(name) == nil) -- global name can't be number
local harbor = require "skynet.harbor"
harbor.globalname(name, handle)
return true
end
function skynet.register(name)
c.command("REG", name)
if not globalname(name) then
c.command("REG", name)
end
end
function skynet.name(name, handle)
c.command("NAME", name .. " " .. skynet.address(handle))
if not globalname(name, handle) then
c.command("NAME", name .. " " .. skynet.address(handle))
end
end
local self_handle
@@ -226,8 +247,19 @@ function skynet.starttime()
return tonumber(c.command("STARTTIME"))
end
function skynet.time()
return skynet.now()/100 + skynet.starttime() -- get now first would be better
end
function skynet.exit()
skynet.send(".launcher","lua","REMOVE",skynet.self())
for co, session in pairs(session_coroutine_id) do
local address = session_coroutine_address[co]
local self = skynet.self()
if session~=0 and address then
skynet.redirect(self, address, "error", session, "")
end
end
c.command("EXIT")
end
@@ -267,6 +299,7 @@ skynet.redirect = function(dest,source,typename,...)
end
skynet.pack = assert(c.pack)
skynet.packstring = assert(c.packstring)
skynet.unpack = assert(c.unpack)
skynet.tostring = assert(c.tostring)
@@ -318,8 +351,8 @@ function skynet.dispatch(typename, func)
p.dispatch = func
end
local function unknown_request(session, address, msg, sz)
print("Unknown request :" , c.tostring(msg,sz))
local function unknown_request(session, address, msg, sz, prototype)
skynet.error(string.format("Unknown request (%s): %s", prototype, c.tostring(msg,sz)))
error(string.format("Unknown session : %d from %x", session, address))
end
@@ -373,7 +406,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, proto[prototype])
end
end
end
@@ -502,7 +535,7 @@ end
local function init_service(start)
local ok, err = xpcall(init_template, debug.traceback, start)
if not ok then
print("init service failed:", err)
skynet.error("init service failed: " .. tostring(err))
skynet.send(".launcher","lua", "ERROR")
skynet.exit()
else
@@ -543,12 +576,24 @@ function skynet.monitor(service, query)
end
assert(monitor, "Monitor launch failed")
c.command("MONITOR", string.format(":%08x", monitor))
return monitor
end
function skynet.mqlen()
return tonumber(c.command "MQLEN")
end
function skynet.task(ret)
local t = 0
for session,co in pairs(session_id_coroutine) do
if ret then
ret[session] = debug.traceback(co)
end
t = t + 1
end
return t
end
-- Inject internal debug framework
local debug = require "skynet.debug"
debug(skynet)

View File

@@ -21,9 +21,16 @@ end
function dbgcmd.STAT()
local stat = {}
stat.mqlen = skynet.mqlen()
stat.task = skynet.task()
skynet.ret(skynet.pack(stat))
end
function dbgcmd.TASK()
local task = {}
skynet.task(task)
skynet.ret(skynet.pack(task))
end
function dbgcmd.INFO()
if internal_info_func then
skynet.ret(skynet.pack(internal_info_func()))

18
lualib/skynet/harbor.lua Normal file
View File

@@ -0,0 +1,18 @@
local skynet = require "skynet"
local harbor = {}
function harbor.globalname(name, handle)
handle = handle or skynet.self()
skynet.send(".slave", "lua", "REGISTER", name, handle)
end
function harbor.link(id)
skynet.call(".slave", "lua", "LINK", id)
end
function harbor.connect(id)
skynet.call(".slave", "lua", "CONNECT", id)
end
return harbor

View File

@@ -266,12 +266,19 @@ end
socket.write = assert(driver.send)
socket.lwrite = assert(driver.lsend)
socket.header = assert(driver.header)
function socket.invalid(id)
return socket_pool[id] == nil
end
socket.listen = assert(driver.listen)
function socket.listen(host, port, backlog)
if port == nil then
host, port = string.match(host, "([^:]+):(.+)$")
port = tonumber(port)
end
return driver.listen(host, port, backlog)
end
function socket.lock(id)
local s = socket_pool[id]

View File

@@ -2,6 +2,16 @@
#include "skynet_harbor.h"
#include "skynet_socket.h"
/*
harbor listen the PTYPE_HARBOR (in text)
N name : update the global name
S fd id: connect to new harbor , we should send self_id to fd first , and then recv a id (check it), and at last send queue.
A fd id: accept new harbor , we should send self_id to fd , and then send queue.
If the fd is disconnected, send message to slave in PTYPE_TEXT. D id
If we don't known a globalname, send message to slave in PTYPE_TEXT. Q name
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
@@ -13,8 +23,22 @@
#define HASH_SIZE 4096
#define DEFAULT_QUEUE_SIZE 1024
// 12 is sizeof(struct remote_message_header)
#define HEADER_COOKIE_LENGTH 12
/*
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;
};
struct msg {
uint8_t * buffer;
struct remote_message_header header;
void * buffer;
size_t size;
};
@@ -37,35 +61,34 @@ 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;
};
#define STATUS_WAIT 0
#define STATUS_HANDSHAKE 1
#define STATUS_HEADER 2
#define STATUS_CONTENT 3
#define STATUS_DOWN 4
// 12 is sizeof(struct remote_message_header)
#define HEADER_COOKIE_LENGTH 12
struct slave {
int fd;
struct msg_queue *queue;
int status;
int length;
int read;
uint8_t size[4];
char * recv_buffer;
};
struct harbor {
struct skynet_context *ctx;
char * local_addr;
int id;
uint32_t slave;
struct hashmap * map;
int master_fd;
char * master_addr;
int remote_fd[REMOTE_MAX];
bool connected[REMOTE_MAX];
char * remote_addr[REMOTE_MAX];
struct slave s[REMOTE_MAX];
};
// hash table
static void
_push_queue(struct msg_queue * queue, const void * buffer, size_t sz, struct remote_message_header * header) {
push_queue_msg(struct msg_queue * queue, struct msg * m) {
// 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) {
@@ -81,16 +104,21 @@ _push_queue(struct msg_queue * queue, const void * buffer, size_t sz, struct rem
queue->size *= 2;
}
struct msg * slot = &queue->data[queue->tail];
*slot = *m;
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 void
push_queue(struct msg_queue * queue, void * buffer, size_t sz, struct remote_message_header * header) {
struct msg m;
m.header = *header;
m.buffer = buffer;
m.size = sz;
push_queue_msg(queue, &m);
}
static struct msg *
_pop_queue(struct msg_queue * queue) {
pop_queue(struct msg_queue * queue) {
if (queue->head == queue->tail) {
return NULL;
}
@@ -100,7 +128,7 @@ _pop_queue(struct msg_queue * queue) {
}
static struct msg_queue *
_new_queue() {
new_queue() {
struct msg_queue * queue = skynet_malloc(sizeof(*queue));
queue->size = DEFAULT_QUEUE_SIZE;
queue->head = 0;
@@ -111,20 +139,19 @@ _new_queue() {
}
static void
_release_queue(struct msg_queue *queue) {
release_queue(struct msg_queue *queue) {
if (queue == NULL)
return;
struct msg * m = _pop_queue(queue);
while (m) {
struct msg * m;
while ((m=pop_queue(queue)) != NULL) {
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]) {
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];
@@ -142,7 +169,7 @@ _hash_search(struct hashmap * hash, const char name[GLOBALNAME_LENGTH]) {
// Don't support erase name yet
static struct void
_hash_erase(struct hashmap * hash, char name[GLOBALNAME_LENGTH) {
hash_erase(struct hashmap * hash, char name[GLOBALNAME_LENGTH) {
uint32_t *ptr = name;
uint32_t h = ptr[0] ^ ptr[1] ^ ptr[2] ^ ptr[3];
struct keyvalue ** ptr = &hash->node[h % HASH_SIZE];
@@ -160,7 +187,7 @@ _hash_erase(struct hashmap * hash, char name[GLOBALNAME_LENGTH) {
*/
static struct keyvalue *
_hash_insert(struct hashmap * hash, const char name[GLOBALNAME_LENGTH]) {
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];
@@ -176,20 +203,20 @@ _hash_insert(struct hashmap * hash, const char name[GLOBALNAME_LENGTH]) {
}
static struct hashmap *
_hash_new() {
hash_new() {
struct hashmap * h = skynet_malloc(sizeof(struct hashmap));
memset(h,0,sizeof(*h));
return h;
}
static void
_hash_delete(struct hashmap *hash) {
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);
release_queue(node->queue);
skynet_free(node);
node = next;
}
@@ -199,64 +226,50 @@ _hash_delete(struct hashmap *hash) {
///////////////
static void
close_harbor(struct harbor *h, int id) {
struct slave *s = &h->s[id];
s->status = STATUS_DOWN;
if (s->fd) {
skynet_socket_close(h->ctx, s->fd);
}
if (s->queue) {
release_queue(s->queue);
s->queue = NULL;
}
}
static void
report_harbor_down(struct harbor *h, int id) {
char down[64];
int n = sprintf(down, "D %d",id);
skynet_send(h->ctx, 0, h->slave, PTYPE_TEXT, 0, down, n);
}
struct harbor *
harbor_create(void) {
struct harbor * h = skynet_malloc(sizeof(*h));
h->ctx = NULL;
h->id = 0;
h->master_fd = -1;
h->master_addr = NULL;
int i;
for (i=0;i<REMOTE_MAX;i++) {
h->remote_fd[i] = -1;
h->connected[i] = false;
h->remote_addr[i] = NULL;
}
h->map = _hash_new();
memset(h,0,sizeof(*h));
h->map = hash_new();
return h;
}
void
harbor_release(struct harbor *h) {
struct skynet_context *ctx = h->ctx;
if (h->master_fd >= 0) {
skynet_socket_close(ctx, h->master_fd);
}
skynet_free(h->master_addr);
skynet_free(h->local_addr);
int i;
for (i=0;i<REMOTE_MAX;i++) {
if (h->remote_fd[i] >= 0) {
skynet_socket_close(ctx, h->remote_fd[i]);
skynet_free(h->remote_addr[i]);
for (i=1;i<REMOTE_MAX;i++) {
struct slave *s = &h->s[i];
if (s->fd && s->status != STATUS_DOWN) {
close_harbor(h,i);
// don't call report_harbor_down.
// never call skynet_send during module exit, because of dead lock
}
}
_hash_delete(h->map);
hash_delete(h->map);
skynet_free(h);
}
static int
_connect_to(struct harbor *h, const char *ipaddress, bool blocking) {
char * port = strchr(ipaddress,':');
if (port==NULL) {
return -1;
}
int sz = port - ipaddress;
char tmp[sz + 1];
memcpy(tmp,ipaddress,sz);
tmp[sz] = '\0';
int portid = strtol(port+1, NULL,10);
skynet_error(h->ctx, "Harbor(%d) connect to %s:%d", h->id, tmp, portid);
if (blocking) {
return skynet_socket_block_connect(h->ctx, tmp, portid);
} else {
return skynet_socket_connect(h->ctx, tmp, portid);
}
}
static inline void
to_bigendian(uint8_t *buffer, uint32_t n) {
buffer[0] = (n >> 24) & 0xff;
@@ -266,7 +279,7 @@ to_bigendian(uint8_t *buffer, uint32_t n) {
}
static inline void
_header_to_message(const struct remote_message_header * header, uint8_t * message) {
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);
@@ -283,112 +296,205 @@ from_bigendian(uint32_t n) {
}
static inline void
_message_to_header(const uint32_t *message, struct remote_message_header *header) {
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
_send_package(struct skynet_context *ctx, int fd, const void * buffer, size_t sz) {
uint8_t * sendbuf = skynet_malloc(sz+4);
to_bigendian(sendbuf, sz);
memcpy(sendbuf+4, buffer, sz);
// socket package
if (skynet_socket_send(ctx, fd, sendbuf, sz+4)) {
skynet_error(ctx, "Send to %d error", fd);
}
static void
forward_local_messsage(struct harbor *h, void *msg, int sz) {
const char * cookie = msg;
cookie += sz - HEADER_COOKIE_LENGTH;
struct remote_message_header header;
message_to_header((const uint32_t *)cookie, &header);
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(h->ctx, header.source, destination, type, (int)header.session, (void *)msg, sz-HEADER_COOKIE_LENGTH);
}
static void
_send_remote(struct skynet_context * ctx, int fd, const char * buffer, size_t sz, struct remote_message_header * cookie) {
send_remote(struct skynet_context * ctx, int fd, const char * buffer, size_t sz, struct remote_message_header * cookie) {
uint32_t sz_header = sz+sizeof(*cookie);
uint8_t * sendbuf = skynet_malloc(sz_header+4);
to_bigendian(sendbuf, sz_header);
memcpy(sendbuf+4, buffer, sz);
_header_to_message(cookie, sendbuf+4+sz);
header_to_message(cookie, sendbuf+4+sz);
if (skynet_socket_send(ctx, fd, sendbuf, sz_header+4)) {
skynet_error(ctx, "Remote send to %d error", fd);
}
// ignore send error, because if the connection is broken, the mainloop will recv a message.
skynet_socket_send(ctx, fd, sendbuf, sz_header+4);
}
static void
_update_remote_address(struct harbor *h, int harbor_id, const char * ipaddr) {
if (harbor_id == h->id) {
return;
}
assert(harbor_id > 0 && harbor_id< REMOTE_MAX);
struct skynet_context * context = h->ctx;
if (h->remote_fd[harbor_id] >=0) {
skynet_socket_close(context, h->remote_fd[harbor_id]);
skynet_free(h->remote_addr[harbor_id]);
h->remote_addr[harbor_id] = NULL;
}
h->remote_fd[harbor_id] = _connect_to(h, ipaddr, false);
h->connected[harbor_id] = false;
}
static void
_dispatch_queue(struct harbor *h, struct msg_queue * queue, uint32_t handle, const char name[GLOBALNAME_LENGTH] ) {
dispatch_name_queue(struct harbor *h, struct keyvalue * node) {
struct msg_queue * queue = node->queue;
uint32_t handle = node->value;
int harbor_id = handle >> HANDLE_REMOTE_SHIFT;
assert(harbor_id != 0);
struct skynet_context * context = h->ctx;
int fd = h->remote_fd[harbor_id];
if (fd < 0) {
char tmp [GLOBALNAME_LENGTH+1];
memcpy(tmp, name , GLOBALNAME_LENGTH);
tmp[GLOBALNAME_LENGTH] = '\0';
skynet_error(context, "Drop message to %s (in harbor %d)",tmp,harbor_id);
struct slave *s = &h->s[harbor_id];
int fd = s->fd;
if (fd == 0) {
if (s->status == STATUS_DOWN) {
char tmp [GLOBALNAME_LENGTH+1];
memcpy(tmp, node->key, GLOBALNAME_LENGTH);
tmp[GLOBALNAME_LENGTH] = '\0';
skynet_error(context, "Drop message to %s (in harbor %d)",tmp,harbor_id);
} else {
if (s->queue == NULL) {
s->queue = node->queue;
node->queue = NULL;
} else {
struct msg * m;
while ((m = pop_queue(queue))!=NULL) {
push_queue_msg(s->queue, m);
}
}
}
return;
}
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));
cookie.destination |= (handle & HANDLE_MASK);
_header_to_message(&cookie, ptr);
_send_package(context, fd, m->buffer, m->size);
m = _pop_queue(queue);
struct msg * m;
while ((m = pop_queue(queue)) != NULL) {
m->header.destination |= (handle & HANDLE_MASK);
send_remote(context, fd, m->buffer, m->size, &m->header);
}
}
static void
_update_remote_name(struct harbor *h, const char name[GLOBALNAME_LENGTH], uint32_t handle) {
struct keyvalue * node = _hash_search(h->map, name);
dispatch_queue(struct harbor *h, int id) {
struct slave *s = &h->s[id];
int fd = s->fd;
assert(fd != 0);
struct msg_queue *queue = s->queue;
if (queue == NULL)
return;
struct msg * m;
while ((m = pop_queue(queue)) != NULL) {
send_remote(h->ctx, fd, m->buffer, m->size, &m->header);
}
release_queue(queue);
s->queue = NULL;
}
static void
push_socket_data(struct harbor *h, const struct skynet_socket_message * message) {
assert(message->type == SKYNET_SOCKET_TYPE_DATA);
int fd = message->id;
int i;
int id = 0;
struct slave * s = NULL;
for (i=1;i<REMOTE_MAX;i++) {
if (h->s[i].fd == fd) {
s = &h->s[i];
id = i;
break;
}
}
if (s == NULL) {
skynet_free(message->buffer);
skynet_error(h->ctx, "Invalid socket fd (%d) data", fd);
return;
}
uint8_t * buffer = (uint8_t *)message->buffer;
int size = message->ud;
for (;;) {
switch(s->status) {
case STATUS_HANDSHAKE: {
// check id
uint8_t remote_id = buffer[0];
if (remote_id != id) {
skynet_error(h->ctx, "Invalid shakehand id (%d) from fd = %d , harbor = %d", id, fd, remote_id);
close_harbor(h,id);
return;
}
++buffer;
--size;
s->status = STATUS_HEADER;
dispatch_queue(h, id);
if (size == 0) {
break;
}
// go though
}
case STATUS_HEADER: {
// big endian 4 bytes length, the first one must be 0.
int need = 4 - s->read;
if (size < need) {
memcpy(s->size + s->read, buffer, size);
s->read += size;
return;
} else {
memcpy(s->size + s->read, buffer, need);
buffer += need;
size -= need;
if (s->size[0] != 0) {
skynet_error(h->ctx, "Message is too long from harbor %d", id);
close_harbor(h,id);
return;
}
s->length = s->size[1] << 16 | s->size[2] << 8 | s->size[3];
s->read = 0;
s->recv_buffer = skynet_malloc(s->length);
s->status = STATUS_CONTENT;
if (size == 0) {
return;
}
}
}
// go though
case STATUS_CONTENT: {
int need = s->length - s->read;
if (size < need) {
memcpy(s->recv_buffer + s->read, buffer, size);
s->read += size;
return;
}
memcpy(s->recv_buffer + s->read, buffer, need);
forward_local_messsage(h, s->recv_buffer, s->length);
s->length = 0;
s->read = 0;
s->recv_buffer = NULL;
size -= need;
buffer += need;
s->status = STATUS_HEADER;
if (size == 0)
return;
break;
}
default:
return;
}
}
}
static void
update_name(struct harbor *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 = hash_insert(h->map, name);
}
node->value = handle;
if (node->queue) {
_dispatch_queue(h, node->queue, handle, name);
_release_queue(node->queue);
dispatch_name_queue(h, node);
release_queue(node->queue);
node->queue = NULL;
}
}
static void
_request_master(struct harbor *h, const char name[GLOBALNAME_LENGTH], size_t i, uint32_t handle) {
uint8_t buffer[4+i];
to_bigendian(buffer, handle);
memcpy(buffer+4,name,i);
_send_package(h->ctx, h->master_fd, buffer, 4+i);
}
/*
update global name to master
2 bytes (size)
4 bytes (handle) (handle == 0 for request)
n bytes string (name)
*/
static int
_remote_send_handle(struct harbor *h, uint32_t source, uint32_t destination, int type, int session, const char * msg, size_t sz) {
remote_send_handle(struct harbor *h, uint32_t source, uint32_t destination, int type, int session, const char * msg, size_t sz) {
int harbor_id = destination >> HANDLE_REMOTE_SHIFT;
assert(harbor_id != 0);
struct skynet_context * context = h->ctx;
if (harbor_id == h->id) {
// local message
@@ -396,55 +502,117 @@ _remote_send_handle(struct harbor *h, uint32_t source, uint32_t destination, int
return 1;
}
int fd = h->remote_fd[harbor_id];
if (fd >= 0 && h->connected[harbor_id]) {
struct slave * s = &h->s[harbor_id];
if (s->fd == 0 || s->status == STATUS_HANDSHAKE) {
if (s->status == STATUS_DOWN) {
// throw an error return to source
// 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);
} else {
if (s->queue == NULL) {
s->queue = new_queue();
}
struct remote_message_header header;
header.source = source;
header.destination = type << HANDLE_REMOTE_SHIFT;
header.session = (uint32_t)session;
push_queue(s->queue, (void *)msg, sz, &header);
return 1;
}
} else {
struct remote_message_header cookie;
cookie.source = source;
cookie.destination = (destination & HANDLE_MASK) | ((uint32_t)type << HANDLE_REMOTE_SHIFT);
cookie.session = (uint32_t)session;
_send_remote(context, fd, msg,sz,&cookie);
} else {
// throw an error return to source
// 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);
send_remote(context, s->fd, msg,sz,&cookie);
}
return 0;
}
static void
_remote_register_name(struct harbor *h, const char name[GLOBALNAME_LENGTH], uint32_t handle) {
int i;
for (i=0;i<GLOBALNAME_LENGTH;i++) {
if (name[i] == '\0')
break;
}
if (handle != 0) {
_update_remote_name(h, name, handle);
}
_request_master(h, name,i,handle);
}
static int
_remote_send_name(struct harbor *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);
remote_send_name(struct harbor *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);
node = hash_insert(h->map, name);
}
if (node->value == 0) {
if (node->queue == NULL) {
node->queue = _new_queue();
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);
// 0 for request
_remote_register_name(h, name, 0);
push_queue(node->queue, (void *)msg, sz, &header);
char query[2+GLOBALNAME_LENGTH+1] = "Q ";
query[2+GLOBALNAME_LENGTH] = 0;
memcpy(query+2, name, GLOBALNAME_LENGTH);
skynet_send(h->ctx, 0, h->slave, PTYPE_TEXT, 0, query, strlen(query));
return 1;
} else {
return _remote_send_handle(h, source, node->value, type, session, msg, sz);
return remote_send_handle(h, source, node->value, type, session, msg, sz);
}
}
static void
handshake(struct harbor *h, int id) {
struct slave *s = &h->s[id];
uint8_t * handshake = skynet_malloc(1);
handshake[0] = (uint8_t)h->id;
skynet_socket_send(h->ctx, s->fd, handshake, 1);
}
static void
harbor_command(struct harbor * h, const char * msg, size_t sz, int session, uint32_t source) {
const char * name = msg + 2;
int s = (int)sz;
s -= 2;
switch(msg[0]) {
case 'N' : {
if (s <=0 || s>= GLOBALNAME_LENGTH) {
skynet_error(h->ctx, "Invalid global name %s", name);
return;
}
struct remote_name rn;
memset(&rn, 0, sizeof(rn));
memcpy(rn.name, name, s);
rn.handle = source;
update_name(h, rn.name, rn.handle);
break;
}
case 'S' :
case 'A' : {
char buffer[s+1];
memcpy(buffer, name, s);
buffer[s] = 0;
int fd=0, id=0;
sscanf(buffer, "%d %d",&fd,&id);
if (fd == 0 || id <= 0 || id>=REMOTE_MAX) {
skynet_error(h->ctx, "Invalid command %c %s", msg[0], buffer);
return;
}
struct slave * slave = &h->s[id];
if (slave->fd != 0) {
skynet_error(h->ctx, "Harbor %d alreay exist", id);
return;
}
slave->fd = fd;
skynet_socket_start(h->ctx, fd);
handshake(h, id);
if (msg[0] == 'S') {
slave->status = STATUS_HANDSHAKE;
} else {
slave->status = STATUS_HEADER;
dispatch_queue(h,id);
}
break;
}
default:
skynet_error(h->ctx, "Unknown command %s", msg);
return;
}
}
@@ -452,105 +620,57 @@ static int
harbor_id(struct harbor *h, int fd) {
int i;
for (i=1;i<REMOTE_MAX;i++) {
if (h->remote_fd[i] == fd)
struct slave *s = &h->s[i];
if (s->fd == fd) {
return i;
}
}
return 0;
}
static void
close_harbor(struct harbor *h, int fd) {
int id = harbor_id(h,fd);
if (id == 0)
return;
skynet_error(h->ctx, "Harbor %d closed",id);
skynet_socket_close(h->ctx, fd);
h->remote_fd[id] = -1;
h->connected[id] = false;
}
static void
open_harbor(struct harbor *h, int fd) {
int id = harbor_id(h,fd);
if (id == 0)
return;
assert(h->connected[id] == false);
h->connected[id] = true;
}
static int
_mainloop(struct skynet_context * context, void * ud, int type, int session, uint32_t source, const void * msg, size_t sz) {
mainloop(struct skynet_context * context, void * ud, int type, int session, uint32_t source, const void * msg, size_t sz) {
struct harbor * h = ud;
switch (type) {
case PTYPE_SOCKET: {
const struct skynet_socket_message * message = msg;
switch(message->type) {
case SKYNET_SOCKET_TYPE_DATA:
push_socket_data(h, message);
skynet_free(message->buffer);
skynet_error(context, "recv invalid socket message (size=%d)", message->ud);
break;
case SKYNET_SOCKET_TYPE_ACCEPT:
skynet_error(context, "recv invalid socket accept message");
break;
case SKYNET_SOCKET_TYPE_ERROR:
case SKYNET_SOCKET_TYPE_CLOSE:
close_harbor(h, message->id);
case SKYNET_SOCKET_TYPE_CLOSE: {
int id = harbor_id(h, message->id);
if (id) {
report_harbor_down(h,id);
} else {
skynet_error(context, "Unkown fd (%d) closed", message->id);
}
break;
}
case SKYNET_SOCKET_TYPE_CONNECT:
open_harbor(h, message->id);
// fd forward to this service
break;
default:
skynet_error(context, "recv invalid socket message type %d", type);
break;
}
return 0;
}
case PTYPE_HARBOR: {
// remote message in
const char * cookie = msg;
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 - 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 - 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);
}
} else {
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-HEADER_COOKIE_LENGTH);
return 1;
}
return 0;
}
case PTYPE_SYSTEM: {
// register name message
const struct remote_message *rmsg = msg;
assert (sz == sizeof(rmsg->destination));
_remote_register_name(h, rmsg->destination.name, rmsg->destination.handle);
harbor_command(h, msg,sz,session,source);
return 0;
}
default: {
// remote message out
const struct remote_message *rmsg = msg;
if (rmsg->destination.handle == 0) {
if (_remote_send_name(h, source , rmsg->destination.name, type, session, rmsg->message, rmsg->sz)) {
if (remote_send_name(h, source , rmsg->destination.name, type, session, rmsg->message, rmsg->sz)) {
return 0;
}
} else {
if (_remote_send_handle(h, source , rmsg->destination.handle, type, session, rmsg->message, rmsg->sz)) {
if (remote_send_handle(h, source , rmsg->destination.handle, type, session, rmsg->message, rmsg->sz)) {
return 0;
}
}
@@ -560,48 +680,19 @@ _mainloop(struct skynet_context * context, void * ud, int type, int session, uin
}
}
static void
_launch_gate(struct skynet_context * ctx, const char * local_addr) {
char tmp[128];
sprintf(tmp,"gate L ! %s %d %d 0",local_addr, PTYPE_HARBOR, REMOTE_MAX);
const char * gate_addr = skynet_command(ctx, "LAUNCH", tmp);
if (gate_addr == NULL) {
fprintf(stderr, "Harbor : launch gate failed\n");
exit(1);
}
uint32_t gate = strtoul(gate_addr+1 , NULL, 16);
if (gate == 0) {
fprintf(stderr, "Harbor : launch gate invalid %s", gate_addr);
exit(1);
}
const char * self_addr = skynet_command(ctx, "REG", NULL);
int n = sprintf(tmp,"broker %s",self_addr);
skynet_send(ctx, 0, gate, PTYPE_TEXT, 0, tmp, n);
skynet_send(ctx, 0, gate, PTYPE_TEXT, 0, "start", 5);
}
int
harbor_init(struct harbor *h, struct skynet_context *ctx, const char * args) {
h->ctx = ctx;
int sz = strlen(args)+1;
char master_addr[sz];
char local_addr[sz];
int harbor_id = 0;
sscanf(args,"%s %s %d",master_addr, local_addr, &harbor_id);
h->master_addr = skynet_strdup(master_addr);
h->id = harbor_id;
h->master_fd = _connect_to(h, master_addr, true);
if (h->master_fd == -1) {
fprintf(stderr, "Harbor: Connect to master failed\n");
exit(1);
uint32_t slave = 0;
sscanf(args,"%d %u", &harbor_id, &slave);
if (slave == 0) {
return 1;
}
h->id = harbor_id;
h->slave = slave;
skynet_callback(ctx, h, mainloop);
skynet_harbor_start(ctx);
h->local_addr = skynet_strdup(local_addr);
_launch_gate(ctx, local_addr);
skynet_callback(ctx, h, _mainloop);
_request_master(h, local_addr, strlen(local_addr), harbor_id);
return 0;
}

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,313 +0,0 @@
#include "skynet.h"
#include "skynet_harbor.h"
#include "skynet_socket.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <assert.h>
#include <stdint.h>
#define HASH_SIZE 4096
struct name {
struct name * next;
char key[GLOBALNAME_LENGTH];
uint32_t hash;
uint32_t value;
};
struct namemap {
struct name *node[HASH_SIZE];
};
struct master {
struct skynet_context *ctx;
int remote_fd[REMOTE_MAX];
bool connected[REMOTE_MAX];
char * remote_addr[REMOTE_MAX];
struct namemap map;
};
struct master *
master_create() {
struct master *m = skynet_malloc(sizeof(*m));
int i;
for (i=0;i<REMOTE_MAX;i++) {
m->remote_fd[i] = -1;
m->remote_addr[i] = NULL;
m->connected[i] = false;
}
memset(&m->map, 0, sizeof(m->map));
return m;
}
void
master_release(struct master * m) {
int i;
struct skynet_context *ctx = m->ctx;
for (i=0;i<REMOTE_MAX;i++) {
int fd = m->remote_fd[i];
if (fd >= 0) {
assert(ctx);
skynet_socket_close(ctx, fd);
}
skynet_free(m->remote_addr[i]);
}
for (i=0;i<HASH_SIZE;i++) {
struct name * node = m->map.node[i];
while (node) {
struct name * next = node->next;
skynet_free(node);
node = next;
}
}
skynet_free(m);
}
static struct name *
_search_name(struct master *m, char name[GLOBALNAME_LENGTH]) {
uint32_t *ptr = (uint32_t *) name;
uint32_t h = ptr[0] ^ ptr[1] ^ ptr[2] ^ ptr[3];
struct name * node = m->map.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 name *
_insert_name(struct master *m, char name[GLOBALNAME_LENGTH]) {
uint32_t *ptr = (uint32_t *)name;
uint32_t h = ptr[0] ^ ptr[1] ^ ptr[2] ^ ptr[3];
struct name **pname = &m->map.node[h % HASH_SIZE];
struct name * node = skynet_malloc(sizeof(*node));
memcpy(node->key, name, GLOBALNAME_LENGTH);
node->next = *pname;
node->hash = h;
node->value = 0;
*pname = node;
return node;
}
static void
_copy_name(char *name, const char * buffer, size_t sz) {
if (sz < GLOBALNAME_LENGTH) {
memcpy(name, buffer, sz);
memset(name+sz, 0 , GLOBALNAME_LENGTH - sz);
} else {
memcpy(name, buffer, GLOBALNAME_LENGTH);
}
}
static void
_connect_to(struct master *m, int id) {
assert(m->connected[id] == false);
struct skynet_context * ctx = m->ctx;
const char *ipaddress = m->remote_addr[id];
char * portstr = strchr(ipaddress,':');
if (portstr==NULL) {
skynet_error(ctx, "Harbor %d : address invalid (%s)",id, ipaddress);
return;
}
int sz = portstr - ipaddress;
char tmp[sz + 1];
memcpy(tmp,ipaddress,sz);
tmp[sz] = '\0';
int port = strtol(portstr+1,NULL,10);
skynet_error(ctx, "Master connect to harbor(%d) %s:%d", id, tmp, port);
m->remote_fd[id] = skynet_socket_connect(ctx, tmp, port);
}
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 void
_send_to(struct master *m, int id, const void * buf, int sz, uint32_t handle) {
uint8_t * buffer= (uint8_t *)skynet_malloc(4 + sz + 12);
to_bigendian(buffer, sz+12);
memcpy(buffer+4, buf, sz);
to_bigendian(buffer+4+sz, 0);
to_bigendian(buffer+4+sz+4, handle);
to_bigendian(buffer+4+sz+8, 0);
sz += 4 + 12;
if (skynet_socket_send(m->ctx, m->remote_fd[id], buffer, sz)) {
skynet_error(m->ctx, "Harbor %d : send error", id);
}
}
static void
_broadcast(struct master *m, const char *name, size_t sz, uint32_t handle) {
int i;
for (i=1;i<REMOTE_MAX;i++) {
int fd = m->remote_fd[i];
if (fd < 0 || m->connected[i]==false)
continue;
_send_to(m, i , name, sz, handle);
}
}
static void
_request_name(struct master *m, const char * buffer, size_t sz) {
char name[GLOBALNAME_LENGTH];
_copy_name(name, buffer, sz);
struct name * n = _search_name(m, name);
if (n == NULL) {
return;
}
_broadcast(m, name, GLOBALNAME_LENGTH, n->value);
}
static void
_update_name(struct master *m, uint32_t handle, const char * buffer, size_t sz) {
char name[GLOBALNAME_LENGTH];
_copy_name(name, buffer, sz);
struct name * n = _search_name(m, name);
if (n==NULL) {
n = _insert_name(m,name);
}
n->value = handle;
_broadcast(m,name,GLOBALNAME_LENGTH, handle);
}
static void
close_harbor(struct master *m, int harbor_id) {
if (m->connected[harbor_id]) {
struct skynet_context * context = m->ctx;
skynet_socket_close(context, m->remote_fd[harbor_id]);
m->remote_fd[harbor_id] = -1;
m->connected[harbor_id] = false;
}
}
static void
_update_address(struct master *m, int harbor_id, const char * buffer, size_t sz) {
if (m->remote_fd[harbor_id] >= 0) {
close_harbor(m, harbor_id);
}
skynet_free(m->remote_addr[harbor_id]);
char * addr = skynet_malloc(sz+1);
memcpy(addr, buffer, sz);
addr[sz] = '\0';
m->remote_addr[harbor_id] = addr;
_connect_to(m, harbor_id);
}
static int
socket_id(struct master *m, int id) {
int i;
for (i=1;i<REMOTE_MAX;i++) {
if (m->remote_fd[i] == id)
return i;
}
return 0;
}
static void
on_connected(struct master *m, int id) {
_broadcast(m, m->remote_addr[id], strlen(m->remote_addr[id]), id);
m->connected[id] = true;
int i;
for (i=1;i<REMOTE_MAX;i++) {
if (i == id)
continue;
const char * addr = m->remote_addr[i];
if (addr == NULL || m->connected[i] == false) {
continue;
}
_send_to(m, id , addr, strlen(addr), i);
}
}
static void
dispatch_socket(struct master *m, const struct skynet_socket_message *msg, int sz) {
int id = socket_id(m, msg->id);
switch(msg->type) {
case SKYNET_SOCKET_TYPE_CONNECT:
assert(id);
on_connected(m, id);
break;
case SKYNET_SOCKET_TYPE_ERROR:
skynet_error(m->ctx, "socket error on harbor %d", id);
// go though, close socket
case SKYNET_SOCKET_TYPE_CLOSE:
close_harbor(m, id);
break;
default:
skynet_error(m->ctx, "Invalid socket message type %d", msg->type);
break;
}
}
/*
update global name to master
4 bytes (handle) (handle == 0 for request)
n bytes string (name)
*/
static int
_mainloop(struct skynet_context * context, void * ud, int type, int session, uint32_t source, const void * msg, size_t sz) {
if (type == PTYPE_SOCKET) {
dispatch_socket(ud, msg, (int)sz);
return 0;
}
if (type != PTYPE_HARBOR) {
skynet_error(context, "None harbor message recv from %x (type = %d)", source, type);
return 0;
}
assert(sz >= 4);
struct master *m = ud;
const uint8_t *handlen = msg;
uint32_t handle = handlen[0]<<24 | handlen[1]<<16 | handlen[2]<<8 | handlen[3];
sz -= 4;
const char * name = msg;
name += 4;
if (handle == 0) {
_request_name(m , name, sz);
} else if (handle < REMOTE_MAX) {
_update_address(m , handle, name, sz);
} else {
_update_name(m , handle, name, sz);
}
return 0;
}
int
master_init(struct master *m, struct skynet_context *ctx, const char * args) {
char tmp[strlen(args) + 32];
sprintf(tmp,"gate L ! %s %d %d 0",args,PTYPE_HARBOR,REMOTE_MAX);
const char * gate_addr = skynet_command(ctx, "LAUNCH", tmp);
if (gate_addr == NULL) {
skynet_error(ctx, "Master : launch gate failed");
return 1;
}
uint32_t gate = strtoul(gate_addr+1, NULL, 16);
if (gate == 0) {
skynet_error(ctx, "Master : launch gate invalid %s", gate_addr);
return 1;
}
const char * self_addr = skynet_command(ctx, "REG", NULL);
int n = sprintf(tmp,"broker %s",self_addr);
skynet_send(ctx, 0, gate, PTYPE_TEXT, 0, tmp, n);
skynet_send(ctx, 0, gate, PTYPE_TEXT, 0, "start", 5);
skynet_callback(ctx, m, _mainloop);
m->ctx = ctx;
return 0;
}

View File

@@ -1,23 +1,38 @@
local skynet = require "skynet"
local harbor = require "skynet.harbor"
skynet.start(function()
assert(skynet.launch("logger", skynet.getenv "logger"))
local standalone = skynet.getenv "standalone"
local master_addr = skynet.getenv "master"
if standalone then
assert(skynet.launch("master", master_addr))
end
local local_addr = skynet.getenv "address"
local harbor_id = skynet.getenv "harbor"
assert(skynet.launch("harbor",master_addr, local_addr, harbor_id))
local launcher = assert(skynet.launch("snlua","launcher"))
skynet.name(".launcher", launcher)
local harbor_id = tonumber(skynet.getenv "harbor")
if harbor_id == 0 then
assert(standalone == nil)
standalone = true
skynet.setenv("standalone", "true")
local slave = skynet.newservice "cdummy"
if slave == nil then
skynet.abort()
end
skynet.name(".slave", slave)
else
if standalone then
if not skynet.newservice "cmaster" then
skynet.abort()
end
end
local slave = skynet.newservice "cslave"
if slave == nil then
skynet.abort()
end
skynet.name(".slave", slave)
end
if standalone then
local datacenter = assert(skynet.newservice "datacenterd")
skynet.name("DATACENTER", datacenter)

47
service/cdummy.lua Normal file
View File

@@ -0,0 +1,47 @@
local skynet = require "skynet"
local globalname = {}
local harbor = {}
skynet.register_protocol {
name = "harbor",
id = skynet.PTYPE_HARBOR,
pack = function(...) return ... end,
unpack = skynet.tostring,
}
skynet.register_protocol {
name = "text",
id = skynet.PTYPE_TEXT,
pack = function(...) return ... end,
unpack = skynet.tostring,
}
function harbor.REGISTER(name, handle)
assert(globalname[name] == nil)
globalname[name] = handle
skynet.redirect(harbor_service, handle, "harbor", 0, "N " .. name)
end
function harbor.LINK(id)
skynet.ret()
end
function harbor.CONNECT(id)
skynet.error("Can't connect to other harbor in single node mode")
end
skynet.start(function()
local harbor_id = tonumber(skynet.getenv "harbor")
assert(harbor_id == 0)
skynet.dispatch("lua", function (session,source,command,...)
local f = assert(harbor[command])
f(...)
end)
skynet.dispatch("text", function(session,source,command)
-- ignore all the command
end)
harbor_service = assert(skynet.launch("harbor", harbor_id, skynet.self()))
end)

72
service/clusterd.lua Normal file
View File

@@ -0,0 +1,72 @@
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 = socket.header(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")
if port == nil then
addr, port = string.match(node_address[addr], "([^:]+):(.*)$")
end
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)

125
service/cmaster.lua Normal file
View File

@@ -0,0 +1,125 @@
local skynet = require "skynet"
local socket = require "socket"
--[[
master manage data :
1. all the slaves address : id -> ipaddr:port
2. all the global names : name -> address
master hold connections from slaves .
protocol slave->master :
package size 1 byte
type 1 byte :
'H' : HANDSHAKE, report slave id, and address.
'R' : REGISTER name address
'Q' : QUERY name
protocol master->slave:
package size 1 byte
type 1 byte :
'W' : WAIT n
'C' : CONNECT slave_id slave_address
'N' : NAME globalname address
'D' : DISCONNECT slave_id
]]
local slave_node = {}
local global_name = {}
local function read_package(fd)
local sz = socket.read(fd, 1)
assert(sz, "closed")
sz = string.byte(sz)
local content = socket.read(fd, sz)
return skynet.unpack(content)
end
local function pack_package(...)
local message = skynet.packstring(...)
local size = #message
assert(size <= 255 , "too long")
return string.char(size) .. message
end
local function report_slave(fd, slave_id, slave_addr)
local message = pack_package("C", slave_id, slave_addr)
local n = 0
for k,v in pairs(slave_node) do
if v.fd ~= 0 then
socket.write(v.fd, message)
n = n + 1
end
end
socket.write(fd, pack_package("W", n))
end
local function handshake(fd)
local t, slave_id, slave_addr = read_package(fd)
assert(t=='H', "Invalid handshake type " .. t)
assert(slave_id ~= 0 , "Invalid slave id 0")
if slave_node[slave_id] then
error(string.format("Slave %d already register on %s", slave_id, slave_node[slave_id].addr))
end
report_slave(fd, slave_id, slave_addr)
slave_node[slave_id] = {
fd = fd,
id = slave_id,
addr = slave_addr,
}
return slave_id , slave_addr
end
local function dispatch_slave(fd)
local t, name, address = read_package(fd)
if t == 'R' then
-- register name
assert(type(address)=="number", "Invalid request")
if not global_name[name] then
global_name[name] = address
end
local message = pack_package("N", name, address)
for k,v in pairs(slave_node) do
socket.write(v.fd, message)
end
elseif t == 'Q' then
-- query name
local address = global_name[name]
if address then
socket.write(fd, pack_package("N", name, address))
end
else
skynet.error("Invalid slave message type " .. t)
end
end
local function monitor_slave(slave_id, slave_address)
local fd = slave_node[slave_id].fd
skynet.error(string.format("Harbor %d (fd=%d) report %s", slave_id, fd, slave_address))
while pcall(dispatch_slave, fd) do end
skynet.error("slave " ..slave_id .. " is down")
local message = pack_package("D", slave_id)
slave_node[slave_id].fd = 0
for k,v in pairs(slave_node) do
socket.write(v.fd, message)
end
socket.close(fd)
end
skynet.start(function()
local master_addr = skynet.getenv "standalone"
skynet.error("master listen socket " .. tostring(master_addr))
local fd = socket.listen(master_addr)
socket.start(fd , function(id, addr)
skynet.error("connect from " .. addr .. " " .. id)
socket.start(id)
local ok, slave, slave_addr = pcall(handshake, id)
if ok then
skynet.fork(monitor_slave, slave, slave_addr)
else
skynet.error(string.format("disconnect fd = %d, error = %s", id, slave))
socket.close(id)
end
end)
end)

222
service/cslave.lua Normal file
View File

@@ -0,0 +1,222 @@
local skynet = require "skynet"
local socket = require "socket"
local slaves = {}
local connect_queue = {}
local globalname = {}
local harbor = {}
local harbor_service
local monitor = {}
local function read_package(fd)
local sz = socket.read(fd, 1)
assert(sz, "closed")
sz = string.byte(sz)
local content = socket.read(fd, sz)
return skynet.unpack(content)
end
local function pack_package(...)
local message = skynet.packstring(...)
local size = #message
assert(size <= 255 , "too long")
return string.char(size) .. message
end
local function monitor_clear(id)
local v = monitor[id]
if v then
monitor[id] = nil
for _, v in ipairs(v) do
skynet.redirect(v.address, 0, "response", v.session, "")
end
end
end
local function connect_slave(slave_id, address)
local ok, err = pcall(function()
if slaves[slave_id] == nil then
local fd = assert(socket.open(address), "Can't connect to "..address)
skynet.error(string.format("Connect to harbor %d (fd=%d), %s", slave_id, fd, address))
slaves[slave_id] = fd
monitor_clear(slave_id)
socket.abandon(fd)
skynet.send(harbor_service, "harbor", string.format("S %d %d",fd,slave_id))
end
end)
if not ok then
skynet.error(err)
end
end
local function ready()
local queue = connect_queue
connect_queue = nil
for k,v in pairs(queue) do
connect_slave(k,v)
end
for name,address in pairs(globalname) do
skynet.redirect(harbor_service, address, "harbor", "N " .. name)
end
end
local function monitor_master(master_fd)
while true do
local ok, t, id_name, address = pcall(read_package,master_fd)
if ok then
if t == 'C' then
if connect_queue then
connect_queue[id_name] = address
else
connect_slave(id_name, address)
end
elseif t == 'N' then
globalname[id_name] = address
if connect_queue == nil then
skynet.redirect(harbor_service, address, "harbor", 0, "N " .. id_name)
end
elseif t == 'D' then
local fd = slaves[id_name]
slaves[id_name] = false
if fd then
socket.close(fd)
end
end
else
skynet.error("Master disconnect")
socket.close(master_fd)
break
end
end
end
local function accept_slave(fd)
socket.start(fd)
local id = socket.read(fd, 1)
if not id then
skynet.error(string.format("Connection (fd =%d) closed", fd))
socket.close(fd)
return
end
id = string.byte(id)
if slaves[id] ~= nil then
skynet.error(string.format("Slave %d exist (fd =%d)", id, fd))
socket.close(fd)
return
end
slaves[id] = fd
monitor_clear(id)
socket.abandon(fd)
skynet.error(string.format("Harbor %d connected (fd = %d)", id, fd))
skynet.send(harbor_service, "harbor", string.format("A %d %d", fd, id))
end
skynet.register_protocol {
name = "harbor",
id = skynet.PTYPE_HARBOR,
pack = function(...) return ... end,
unpack = skynet.tostring,
}
skynet.register_protocol {
name = "text",
id = skynet.PTYPE_TEXT,
pack = function(...) return ... end,
unpack = skynet.tostring,
}
local function monitor_harbor(master_fd)
return function(session, source, command)
local t = string.sub(command, 1, 1)
local arg = string.sub(command, 3)
if t == "Q" then
-- query name
if globalname[arg] then
skynet.redirect(harbor_service, globalname[arg], "harbor", 0, "N " .. arg)
else
socket.write(master_fd, pack_package("Q", arg))
end
elseif t == "D" then
-- harbor down
local id = tonumber(arg)
if slaves[id] then
monitor_clear(id)
end
slaves[id] = false
else
skynet.error("Unknown command ", command)
end
end
end
function harbor.REGISTER(_,_, fd, name, handle)
assert(globalname[name] == nil)
globalname[name] = handle
socket.write(fd, pack_package("R", name, handle))
skynet.redirect(harbor_service, handle, "harbor", 0, "N " .. name)
end
function harbor.LINK(session, source, fd, id)
if slaves[id] then
if monitor[id] == nil then
monitor[id] = {}
end
table.insert(monitor[id], { address = source, session = session })
else
skynet.ret()
end
end
function harbor.CONNECT(session, source, fd, id)
if not slaves[id] then
if monitor[id] == nil then
monitor[id] = {}
end
table.insert(monitor[id], { address = source, session = session })
else
skynet.ret()
end
end
skynet.start(function()
local master_addr = skynet.getenv "master"
local harbor_id = tonumber(skynet.getenv "harbor")
local slave_address = assert(skynet.getenv "address")
local slave_fd = socket.listen(slave_address)
skynet.error("slave connect to master " .. tostring(master_addr))
local master_fd = socket.open(master_addr)
skynet.dispatch("lua", function (session,source,command,...)
local f = assert(harbor[command])
f(session, source, master_fd, ...)
end)
skynet.dispatch("text", monitor_harbor(master_fd))
harbor_service = assert(skynet.launch("harbor", harbor_id, skynet.self()))
local hs_message = pack_package("H", harbor_id, slave_address)
socket.write(master_fd, hs_message)
local t, n = read_package(master_fd)
assert(t == "W" and type(n) == "number", "slave shakehand failed")
skynet.error(string.format("Waiting for %d harbors", n))
skynet.fork(monitor_master, master_fd)
if n > 0 then
local co = coroutine.running()
socket.start(slave_fd, function(fd, addr)
skynet.error(string.format("New connection (fd = %d, %s)",fd, addr))
if pcall(accept_slave,fd) then
local s = 0
for k,v in pairs(slaves) do
s = s + 1
end
if s >= n then
skynet.wakeup(co)
end
end
end)
skynet.wait()
end
socket.close(slave_fd)
skynet.error("Shakehand ready")
skynet.fork(ready)
end)

View File

@@ -88,7 +88,7 @@ end
skynet.start(function()
local listen_socket = socket.listen ("127.0.0.1", port)
print("Start debug console at 127.0.0.1",port)
skynet.error("Start debug console at 127.0.0.1 " .. port)
socket.start(listen_socket , function(id, addr)
local function print(...)
local t = { ... }
@@ -113,6 +113,7 @@ function COMMAND.help()
snax = "lanuch a new snax service",
clearcache = "clear lua code cache",
service = "List unique service",
task = "task address : show service task detail",
}
end

View File

@@ -38,6 +38,16 @@ function command.INFO(_, _, handle)
end
end
function command.TASK(_, _, handle)
handle = handle_to_address(handle)
if services[handle] == nil then
return
else
local result = skynet.call(handle,"debug","TASK")
return result
end
end
function command.KILL(_, _, handle)
handle = handle_to_address(handle)
skynet.kill(handle)

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

@@ -7,34 +7,19 @@
#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) {
int i;
int number = 1;
for (i=0;i<GLOBALNAME_LENGTH;i++) {
char c = rname->name[i];
if (!(c >= '0' && c <='9')) {
number = 0;
break;
}
}
assert(number == 0);
skynet_context_send(REMOTE, rname, sizeof(*rname), 0, PTYPE_SYSTEM , 0);
}
int
skynet_harbor_message_isremote(uint32_t handle) {
assert(HARBOR != 0);
assert(HARBOR != ~0);
int h = (handle & ~HANDLE_MASK);
return h != HARBOR && h !=0;
}

View File

@@ -23,7 +23,6 @@ struct remote_message {
};
void skynet_harbor_send(struct remote_message *rmsg, uint32_t source, int session);
void skynet_harbor_register(struct remote_name *rname);
int skynet_harbor_message_isremote(uint32_t handle);
void skynet_harbor_init(int harbor);
void skynet_harbor_start(void * ctx);

View File

@@ -4,8 +4,10 @@
struct skynet_config {
int thread;
int harbor;
const char * daemon;
const char * module_path;
const char * bootstrap;
const char * logger;
};
#define THREAD_WORKER 0

View File

@@ -35,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);
@@ -109,21 +110,17 @@ main(int argc, char *argv[]) {
}
_init_env(L);
#ifdef LUA_CACHELIB
printf("Skynet lua code cache enable\n");
#endif
config.thread = optint("thread",8);
config.module_path = optstring("cpath","./cservice/?.so");
config.harbor = optint("harbor", 1);
config.bootstrap = optstring("bootstrap","snlua bootstrap");
config.daemon = optstring("daemon", NULL);
config.logger = optstring("logger", NULL);
lua_close(L);
skynet_start(&config);
skynet_globalexit();
printf("skynet exit\n");
return 0;
}

View File

@@ -34,7 +34,6 @@ struct global_queue {
struct message_queue ** queue;
// We use a separated flag array to ensure the mq is pushed.
// See the comments below.
bool * flag;
struct message_queue *list;
};
@@ -45,11 +44,14 @@ 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;
if (q->flag[GP(q->tail)]) {
uint32_t tail = GP(__sync_fetch_and_add(&q->tail,1));
// 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;
@@ -60,14 +62,6 @@ skynet_globalmq_push(struct message_queue * queue) {
return;
}
uint32_t tail = GP(__sync_fetch_and_add(&q->tail,1));
// The thread would suspend here, and the q->queue[tail] is last version ,
// but the queue tail is increased.
// So we set q->flag[tail] after changing q->queue[tail].
q->queue[tail] = queue;
__sync_synchronize();
q->flag[tail] = true;
}
struct message_queue *
@@ -93,18 +87,18 @@ skynet_globalmq_pop() {
}
}
// Check the flag first, if the flag is false, the pushing may not complete.
if(!q->flag[head_ptr]) {
struct message_queue * mq = q->queue[head_ptr];
if (mq == NULL) {
// globalmq push not complete
return NULL;
}
__sync_synchronize();
struct message_queue * mq = q->queue[head_ptr];
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;
}
@@ -220,26 +214,10 @@ 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);
skynet_globalmq_push(queue);
queue->in_global = MQ_IN_GLOBAL;
UNLOCK(queue)
}
void
skynet_mq_mark_release(struct message_queue *q) {
LOCK(q)
@@ -268,7 +246,7 @@ skynet_mq_release(struct message_queue *q, message_drop drop_func, void *ud) {
UNLOCK(q)
_drop_queue(q, drop_func, ud);
} else {
skynet_mq_force_push(q);
skynet_globalmq_push(q);
UNLOCK(q)
}
}

View File

@@ -13,6 +13,7 @@ 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);
@@ -30,9 +31,6 @@ void skynet_mq_push(struct message_queue *q, struct skynet_message *message);
// 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

@@ -52,6 +52,7 @@ struct skynet_context {
struct skynet_node {
int total;
int init;
uint32_t monitor_exit;
pthread_key_t handle_key;
};
@@ -75,8 +76,13 @@ context_dec() {
uint32_t
skynet_current_handle(void) {
void * handle = pthread_getspecific(G_NODE.handle_key);
return (uint32_t)(uintptr_t)handle;
if (G_NODE.init) {
void * handle = pthread_getspecific(G_NODE.handle_key);
return (uint32_t)(uintptr_t)handle;
} else {
uintptr_t v = (uint32_t)(-THREAD_MAIN);
return v;
}
}
static void
@@ -139,7 +145,7 @@ 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 : "");
}
@@ -228,11 +234,23 @@ _dispatch_message(struct skynet_context *ctx, struct skynet_message *msg) {
CHECKCALLING_END(ctx)
}
int
skynet_context_message_dispatch(struct skynet_monitor *sm) {
struct message_queue * q = skynet_globalmq_pop();
if (q==NULL)
return 1;
void
skynet_context_dispatchall(struct skynet_context * ctx) {
// for skynet_error
struct skynet_message msg;
struct message_queue *q = ctx->queue;
while (!skynet_mq_pop(q,&msg)) {
_dispatch_message(ctx, &msg);
}
}
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);
@@ -240,13 +258,13 @@ skynet_context_message_dispatch(struct skynet_monitor *sm) {
if (ctx == NULL) {
struct drop_t d = { handle };
skynet_mq_release(q, drop_message, &d);
return 0;
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);
@@ -258,12 +276,18 @@ skynet_context_message_dispatch(struct skynet_monitor *sm) {
}
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
@@ -328,11 +352,7 @@ cmd_reg(struct skynet_context * context, const char * param) {
} 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);
skynet_error(context, "Can't register global name %s in C", param);
return NULL;
}
}
@@ -363,10 +383,7 @@ cmd_name(struct skynet_context * context, const char * param) {
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);
skynet_error(context, "Can't set global name %s in C", name);
}
return NULL;
}
@@ -545,12 +562,16 @@ _filter_args(struct skynet_context * context, int type, int *session, void ** da
*data = msg;
}
assert((*sz & HANDLE_MASK) == *sz);
*sz |= type << HANDLE_REMOTE_SHIFT;
}
int
skynet_send(struct skynet_context * context, uint32_t source, uint32_t destination , int type, int session, void * data, size_t sz) {
if ((sz & HANDLE_MASK) != sz) {
skynet_error(context, "The message to %x is too large (sz = %lu)", destination, sz);
skynet_free(data);
return -1;
}
_filter_args(context, type, &session, (void **)&data, &sz);
if (source == 0) {
@@ -642,6 +663,7 @@ void
skynet_globalinit(void) {
G_NODE.total = 0;
G_NODE.monitor_exit = 0;
G_NODE.init = 1;
if (pthread_key_create(&G_NODE.handle_key, NULL)) {
fprintf(stderr, "pthread_key_create failed");
exit(1);

View File

@@ -16,8 +16,9 @@ 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_dispatchall(struct skynet_context * context); // for skynet_error output before exit
void skynet_context_endless(uint32_t handle); // for monitor

View File

@@ -133,12 +133,6 @@ skynet_socket_connect(struct skynet_context *ctx, const char *host, int port) {
return socket_server_connect(SOCKET_SERVER, source, host, port);
}
int
skynet_socket_block_connect(struct skynet_context *ctx, const char *host, int port) {
uint32_t source = skynet_context_handle(ctx);
return socket_server_block_connect(SOCKET_SERVER, source, host, port);
}
int
skynet_socket_bind(struct skynet_context *ctx, int fd) {
uint32_t source = skynet_context_handle(ctx);

View File

@@ -25,7 +25,6 @@ int skynet_socket_send(struct skynet_context *ctx, int id, void *buffer, int sz)
void skynet_socket_send_lowpriority(struct skynet_context *ctx, int id, void *buffer, int sz);
int skynet_socket_listen(struct skynet_context *ctx, const char *host, int port, int backlog);
int skynet_socket_connect(struct skynet_context *ctx, const char *host, int port);
int skynet_socket_block_connect(struct skynet_context *ctx, const char *host, int port);
int skynet_socket_bind(struct skynet_context *ctx, int fd);
void skynet_socket_close(struct skynet_context *ctx, int id);
void skynet_socket_start(struct skynet_context *ctx, int id);

View File

@@ -7,6 +7,7 @@
#include "skynet_timer.h"
#include "skynet_monitor.h"
#include "skynet_socket.h"
#include "skynet_daemon.h"
#include <pthread.h>
#include <unistd.h>
@@ -120,8 +121,10 @@ _worker(void *p) {
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;
@@ -181,7 +184,7 @@ _start(int thread) {
}
static void
bootstrap(const char * cmdline) {
bootstrap(struct skynet_context * logger, const char * cmdline) {
int sz = strlen(cmdline);
char name[sz+1];
char args[sz+1];
@@ -189,12 +192,18 @@ bootstrap(const char * cmdline) {
struct skynet_context *ctx = skynet_context_new(name, args);
if (ctx == NULL) {
skynet_error(NULL, "Bootstrap error : %s\n", cmdline);
skynet_context_dispatchall(logger);
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();
@@ -202,8 +211,17 @@ skynet_start(struct skynet_config * config) {
skynet_timer_init();
skynet_socket_init();
bootstrap(config->bootstrap);
struct skynet_context *ctx = skynet_context_new("logger", config->logger);
if (ctx == NULL) {
fprintf(stderr, "Can't launch logger service\n");
exit(1);
}
bootstrap(ctx, 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
@@ -45,13 +48,14 @@ struct timer {
int time;
uint32_t current;
uint32_t starttime;
uint64_t current_point;
uint64_t origin_point;
};
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 +64,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 +91,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 +126,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));
@@ -212,18 +220,41 @@ skynet_timeout(uint32_t handle, int time, int session) {
return session;
}
static uint32_t
_gettime(void) {
uint32_t t;
// centisecond: 1/100 second
static void
systime(uint32_t *sec, uint32_t *cs) {
#if !defined(__APPLE__)
struct timespec ti;
clock_gettime(CLOCK_MONOTONIC, &ti);
t = (uint32_t)(ti.tv_sec & 0xffffff) * 100;
clock_gettime(CLOCK_REALTIME, &ti);
*sec = (uint32_t)ti.tv_sec;
*cs = (uint32_t)(ti.tv_nsec / 10000000);
#else
struct timeval tv;
gettimeofday(&tv, NULL);
*sec = tv.tv_sec;
*cs = tv.tv_usec / 10000;
#endif
}
static uint64_t
gettime() {
uint64_t t;
#if !defined(__APPLE__)
#ifdef CLOCK_MONOTONIC_RAW
#define CLOCK_TIMER CLOCK_MONOTONIC_RAW
#else
#define CLOCK_TIMER CLOCK_MONOTONIC
#endif
struct timespec ti;
clock_gettime(CLOCK_TIMER, &ti);
t = (uint64_t)ti.tv_sec * 100;
t += ti.tv_nsec / 10000000;
#else
struct timeval tv;
gettimeofday(&tv, NULL);
t = (uint32_t)(tv.tv_sec & 0xffffff) * 100;
t = (uint64_t)tv.tv_sec * 100;
t += tv.tv_usec / 10000;
#endif
return t;
@@ -231,10 +262,19 @@ _gettime(void) {
void
skynet_updatetime(void) {
uint32_t ct = _gettime();
if (ct != TI->current) {
int diff = ct>=TI->current?ct-TI->current:(0xffffff+1)*100-TI->current+ct;
TI->current = ct;
uint64_t cp = gettime();
if(cp < TI->current_point) {
skynet_error(NULL, "time diff error: change from %lld to %lld", cp, TI->current_point);
} else if (cp != TI->current_point) {
uint32_t diff = (uint32_t)(cp - TI->current_point);
TI->current_point = cp;
uint32_t oc = TI->current;
TI->current += diff;
if (TI->current < oc) {
// when cs > 0xffffffff(about 497 days), time rewind
TI->starttime += 0xffffffff / 100;
}
int i;
for (i=0;i<diff;i++) {
timer_update(TI);
@@ -255,18 +295,9 @@ skynet_gettime(void) {
void
skynet_timer_init(void) {
TI = timer_create_timer();
TI->current = _gettime();
#if !defined(__APPLE__)
struct timespec ti;
clock_gettime(CLOCK_REALTIME, &ti);
uint32_t sec = (uint32_t)ti.tv_sec;
#else
struct timeval tv;
gettimeofday(&tv, NULL);
uint32_t sec = (uint32_t)tv.tv_sec;
#endif
uint32_t mono = _gettime() / 100;
TI->starttime = sec - mono;
systime(&TI->starttime, &TI->current);
uint64_t point = gettime();
TI->current_point = point;
TI->origin_point = point;
}

View File

@@ -147,6 +147,8 @@ reserve_id(struct socket_server *ss) {
struct socket *s = &ss->slot[id % MAX_SOCKET];
if (s->type == SOCKET_TYPE_INVALID) {
if (__sync_bool_compare_and_swap(&s->type, SOCKET_TYPE_INVALID, SOCKET_TYPE_RESERVE)) {
s->id = id;
s->fd = -1;
return id;
} else {
// retry
@@ -287,7 +289,7 @@ new_fd(struct socket_server *ss, int id, int fd, uintptr_t opaque, bool add) {
// return -1 when connecting
static int
open_socket(struct socket_server *ss, struct request_open * request, struct socket_message *result, bool blocking) {
open_socket(struct socket_server *ss, struct request_open * request, struct socket_message *result) {
int id = request->id;
result->opaque = request->opaque;
result->id = id;
@@ -316,18 +318,14 @@ open_socket(struct socket_server *ss, struct request_open * request, struct sock
continue;
}
socket_keepalive(sock);
if (!blocking) {
sp_nonblocking(sock);
}
sp_nonblocking(sock);
status = connect( sock, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
if ( status != 0 && errno != EINPROGRESS) {
close(sock);
sock = -1;
continue;
}
if (blocking) {
sp_nonblocking(sock);
}
sp_nonblocking(sock);
break;
}
@@ -512,7 +510,7 @@ send_socket(struct socket_server *ss, struct request_send * request, struct sock
return -1;
}
assert(s->type != SOCKET_TYPE_PLISTEN && s->type != SOCKET_TYPE_LISTEN);
if (send_buffer_empty(s)) {
if (send_buffer_empty(s) && s->type == SOCKET_TYPE_CONNECTED) {
int n = write(s->fd, request->buffer, request->sz);
if (n<0) {
switch(errno) {
@@ -687,7 +685,7 @@ ctrl_cmd(struct socket_server *ss, struct socket_message *result) {
case 'K':
return close_socket(ss,(struct request_close *)buffer, result);
case 'O':
return open_socket(ss, (struct request_open *)buffer, result, false);
return open_socket(ss, (struct request_open *)buffer, result);
case 'X':
result->opaque = 0;
result->id = 0;
@@ -765,7 +763,9 @@ report_connect(struct socket_server *ss, struct socket *s, struct socket_message
result->opaque = s->opaque;
result->id = s->id;
result->ud = 0;
sp_write(ss->event_fd, s->fd, s, false);
if (send_buffer_empty(s)) {
sp_write(ss->event_fd, s->fd, s, false);
}
union sockaddr_all u;
socklen_t slen = sizeof(u);
if (getpeername(s->fd, &u.s, &slen) == 0) {
@@ -939,19 +939,6 @@ socket_server_connect(struct socket_server *ss, uintptr_t opaque, const char * a
return request.u.open.id;
}
int
socket_server_block_connect(struct socket_server *ss, uintptr_t opaque, const char * addr, int port) {
struct request_package request;
struct socket_message result;
open_request(ss, &request, opaque, addr, port);
int ret = open_socket(ss, &request.u.open, &result, true);
if (ret == SOCKET_OPEN) {
return result.id;
} else {
return -1;
}
}
// return -1 when error
int64_t
socket_server_send(struct socket_server *ss, int id, const void * buffer, int sz) {
@@ -959,7 +946,6 @@ socket_server_send(struct socket_server *ss, int id, const void * buffer, int sz
if (s->id != id || s->type == SOCKET_TYPE_INVALID) {
return -1;
}
assert(s->type != SOCKET_TYPE_RESERVE);
struct request_package request;
request.u.send.id = id;
@@ -976,7 +962,6 @@ socket_server_send_lowpriority(struct socket_server *ss, int id, const void * bu
if (s->id != id || s->type == SOCKET_TYPE_INVALID) {
return;
}
assert(s->type != SOCKET_TYPE_RESERVE);
struct request_package request;
request.u.send.id = id;

View File

@@ -36,6 +36,4 @@ int socket_server_listen(struct socket_server *, uintptr_t opaque, const char *
int socket_server_connect(struct socket_server *, uintptr_t opaque, const char * addr, int port);
int socket_server_bind(struct socket_server *, uintptr_t opaque, int fd);
int socket_server_block_connect(struct socket_server *, uintptr_t opaque, const char * addr, int port);
#endif

View File

@@ -14,7 +14,7 @@ end)
else
skynet.start(function()
local test = skynet.newservice("testdeadcall", "test") -- launch self in test mode
local test = skynet.newservice(SERVICE_NAME, "test") -- launch self in test mode
print(pcall(function()
skynet.send(test,"lua", "hello world")

11
test/testharborlink.lua Normal file
View File

@@ -0,0 +1,11 @@
local skynet = require "skynet"
local harbor = require "skynet.harbor"
skynet.start(function()
print("wait for harbor 2")
print("run skynet examples/config_log please")
harbor.connect(2)
print("harbor 2 connected")
harbor.link(2)
print("disconnected")
end)

View File

@@ -27,7 +27,7 @@ skynet.start(function()
local channel = mc.new()
print("New channel", channel)
for i=1,10 do
local sub = skynet.newservice("testmulticast", "sub")
local sub = skynet.newservice(SERVICE_NAME, "sub")
skynet.call(sub, "lua", "init", channel.channel)
end

53
test/testredis2.lua Normal file
View File

@@ -0,0 +1,53 @@
local skynet = require "skynet"
local redis = require "redis"
local db
function add1(key, count)
local t = {}
for i = 1, count do
t[2*i -1] = "key" ..i
t[2*i] = "value" .. i
end
db:hmset(key, table.unpack(t))
end
function add2(key, count)
local t = {}
for i = 1, count do
t[2*i -1] = "key" ..i
t[2*i] = "value" .. i
end
table.insert(t, 1, key)
db:hmset(t)
end
function __init__()
db = redis.connect {
host = "127.0.0.1",
port = 6300,
db = 0,
auth = "foobared"
}
print("dbsize:", db:dbsize())
local ok, msg = xpcall(add1, debug.traceback, "test1", 250000)
if not ok then
print("add1 failed", msg)
else
print("add1 succeed")
end
local ok, msg = xpcall(add2, debug.traceback, "test2", 250000)
if not ok then
print("add2 failed", msg)
else
print("add2 succeed")
end
print("dbsize:", db:dbsize())
print("redistest launched")
end
skynet.start(__init__)

22
test/time.lua Normal file
View File

@@ -0,0 +1,22 @@
local skynet = require "skynet"
skynet.start(function()
print(skynet.starttime())
print(skynet.now())
skynet.timeout(1, function()
print("in 1", skynet.now())
end)
skynet.timeout(2, function()
print("in 2", skynet.now())
end)
skynet.timeout(3, function()
print("in 3", skynet.now())
end)
skynet.timeout(4, function()
print("in 4", skynet.now())
end)
skynet.timeout(100, function()
print("in 100", skynet.now())
end)
end)