前言
sknyet_monitor的作用是监视work线程对消息的处理有没有疑似出现死循环。当出现这种情况时,会把目标context endless字段置为true,lua层通过skynet.lenless()判断当前处理是否出现死循环。
monitor结构
struct skynet_monitor {
ATOM_INT version; //原子累加的版本号
int check_version; //检查版本号
uint32_t source; //消息来源
uint32_t destination; //目标
};
API
struct skynet_monitor * skynet_monitor_new(); //新建
void skynet_monitor_delete(struct skynet_monitor *); //删除
void skynet_monitor_trigger(struct skynet_monitor *, uint32_t source, uint32_t destination); //赋值来源目标
void skynet_monitor_check(struct skynet_monitor *); //检查函数
主循环
每5秒对所以worker线程进行一次检查调用。
static void *
thread_monitor(void *p) {
struct monitor * m = p;
int i;
int n = m->count;
skynet_initthread(THREAD_MONITOR);
for (;;) {
CHECK_ABORT
for (i=0;i<n;i++) {
skynet_monitor_check(m->m[i]);
}
for (i=0;i<5;i++) {
CHECK_ABORT
sleep(1);
}
}
return NULL;
}
设置from to
调用消息处理回调时,先设置好检查的来源handle和目标handle,消息处理完了清除来源和目标信息。
过程中version会累计2次。
struct message_queue *
skynet_context_message_dispatch(struct skynet_monitor *sm, struct message_queue *q, int weight) {
skynet_monitor_trigger(sm, msg.source , handle);
if (ctx->cb == NULL) {
skynet_free(msg.data);
} else {
dispatch_message(ctx, &msg);
}
skynet_monitor_trigger(sm, 0,0);
}
check函数
当检查到超时处理超时,会把字段endless赋值为0,然后打印一个日志。
void
skynet_context_endless(uint32_t handle) {
struct skynet_context * ctx = skynet_handle_grab(handle);
if (ctx == NULL) {
return;
}
ctx->endless = true;
skynet_context_release(ctx);
}
void
skynet_monitor_check(struct skynet_monitor *sm) {
if (sm->version == sm->check_version) {
if (sm->destination) {
skynet_context_endless(sm->destination);
skynet_error(NULL, "A message from [ :%08x ] to [ :%08x ] maybe in an endless loop (version = %d)", sm->source , sm->destination, sm->version);
}
} else {
sm->check_version = sm->version;
}
}
lua sknyet.endless处理流程
skynet.endless返回true时,context数据endless字段会赋值为false
那么问题来了:如果第一次消息处理时间超时,endless已经置为true了,第二消息处理时我们调用sknyet.endless的返回值是啥呢?
--skynet.lua
function skynet.endless()
return (c.intcommand("STAT", "endless") == 1)
end
--lua-skynet.c
static int
lintcommand(lua_State *L) {
...
result = skynet_command(context, cmd, parm);
...
}
--skynet_server.c
static const char *
cmd_stat(struct skynet_context * context, const char * param) {
....
} else if (strcmp(param, "endless") == 0) {
if (context->endless) {
strcpy(context->result, "1");
context->endless = false;
} else {
strcpy(context->result, "0");
}
}
...
return context->result;
}
问题测试
local skynet = require "skynet"
local function test_func()
print(skynet.endless())
end
skynet.start(function ()
skynet.error("test_service begin")
local cur_time = os.time()
while true do
local now_time = os.time()
if now_time - cur_time > 10 then
skynet.timeout(100,test_func)
break
end
end
skynet.error("test_service end")
end)
测试结果为true