struct message_queue {
//锁
struct spinlock lock;
//消息队列所属服务的句柄(用于消息处理)
uint32_t handle;
//队列容量
int cap;
//取出标志
int head;
//存入标志
int tail;
//队列是否已被释放表示(0为未释放,1为已释放)
int release;
//是否存入全局消息队列标志
int in_global;
int overload;
int overload_threshold;
//skynet_message消息队列(其实是一个数组通过queue[序号]从队列中获取指定的消息)
struct skynet_message *queue;
//与其他消息队列的关联(非空表示在全局消息队列中)
struct message_queue *next;
};
不难看出来,全局消息队列看起来像是一个 单链表 ,每个节点都带着一个struct message_queue *next;指向下一个节点
//全局消息队列结构
struct global_queue {
struct message_queue *head;
struct message_queue *tail;
//锁(自旋锁或互斥锁)
struct spinlock lock;
};
//全局消息队列的静态结构指针
static struct global_queue *Q = NULL;
全局消息队列其实就是一个头,Windows叫HeadList,此队列向尾部添加,从头部取出删除,分别用 struct message_queue *head;和 struct message_queue *tail;记录其首尾下标。
真正创建全局消息队列的实现在下面函数中:
//初始化全局消息队列
void skynet_mq_init() {
//创建全局消息队列
struct global_queue *q = skynet_malloc(sizeof(*q));
//将*q指针所占存储全部初始化为0
memset(q,0,sizeof(*q));
//初始化全局消息队列中的锁
SPIN_INIT(q);
//将创建结果保存在静态指针中
Q=q;
}
可见,只创建一个struct global_queue 结构并赋值给全局指针对象Q
//将服务的消息队列放入全局消息队列尾
void
skynet_globalmq_push(struct message_queue * queue) {
struct global_queue *q= Q;
SPIN_LOCK(q)
assert(queue->next == NULL);
if(q->tail) {
q->tail->next = queue;//挂载到Q->tail的next上
q->tail = queue;//然后把queue赋值给q->tail,这样其他message_queue *就可以挂到当前节点queue的next上了
} else {
q->head = q->tail = queue; //没挂载任何struct message_queue *,使其首位相连
}
SPIN_UNLOCK(q)
}
//从全局消息队列头中弹出一条二级消息队列
struct message_queue *
skynet_globalmq_pop() {
struct global_queue *q = Q;
SPIN_LOCK(q)
struct message_queue *mq = q->head;
if(mq) {
q->head = mq->next;
if(q->head == NULL) {
assert(mq == q->tail);
q->tail = NULL;
}
mq->next = NULL;
}
SPIN_UNLOCK(q)
return mq;
}