// skynet_server.c
struct skynet_context {
void * instance; // 对应的模块的实体(例如snlua 的实体)
struct skynet_module * mod; // 模块的create,init,release,signal对应的处理函数
void * cb_ud; // 对应模块用来绑定的结构体对象(例如:service_logger.c下的logger 结构体对象,service_snlua.c下的snlua结构体对象)
skynet_cb cb; // context对应callback函数
struct message_queue *queue; // 消息队列列表
FILE * logfile;
char result[32];
uint32_t handle; // 对应的handle,唯一标识,可通过handle查找到对应的消息队列,在skynet_handle.c中也可以通过handle找到对应的context
int session_id; // 当前处理的消息id,用来判断是否卡死在同一个消息处理中
int ref;
bool init;
bool endless;
CHECKCALLING_DECL // 锁信息
};
context结构体包含了skynet_module, skynet_handle产生的handle,消息队列列表,对应模块的实体,callback信息。虽然命名是context,但是并不像操作系统中的线程上下文记录很多线程相关信息,只是记录了一个协程所用到的模块及处理,消息队列,handle。
// skynet_handle.c
struct handle_name {
char * name;
uint32_t handle;
};
struct handle_storage {
struct rwlock lock;
uint32_t harbor;
uint32_t handle_index;
int slot_size;
struct skynet_context ** slot; // 记录着所有的skynet_context
int name_cap;
int name_count;
struct handle_name *name;
};
static struct handle_storage *H = NULL;
所有的context在new出来的时候都会注册到skynet_handle.c中的静态 handle_storage* H 结构体的slot中,在消息处理中可以通过handle或者name查找到对应skynet_context,从而找到想要的消息队列handle。
在消息派发的处理中,通过handle获取到context,从而派发到相应的次级队列的过程如下:
// skynet_server.c
struct message_queue *
skynet_context_message_dispatch(struct skynet_monitor *sm, struct message_queue *q, int weight) {
...
uint32_t handle = skynet_mq_handle(q); // 获取消息处理对应的handle
struct skynet_context * ctx = skynet_handle_grab(handle); // 通过handle获得context
...
int i,n=1;
struct skynet_message msg;
for (i=0;i<n;i++) {
...
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);
}
...
}