skynet源代码学习 - 从全局队列中弹出/压入一个消息队列过程
学习云风的skynet源代码,简单记录下。
void skynet_globalmq_push(struct message_queue * queue) { struct global_queue *q= Q; 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 // 假设swap失败说明queue[] 满了,达到了64K个队列,出现的几率非常小 // 假设这种话,就把其保存在Q的list中 assert(queue->next == NULL); struct message_queue * last; do { last = q->list; queue->next = last; } while(!__sync_bool_compare_and_swap(&q->list, last, queue)); return; } } // 结构体global_queue中的 head, tail 字段分别控制着Q的取,存过程 // GP呢能够看做是一个hash的过程,以此来确定其维护的queues的index struct message_queue * skynet_globalmq_pop() { struct global_queue *q = Q; uint32_t head = q->head; if (head == q->tail) { // The queue is empty. return NULL; } uint32_t head_ptr = GP(head); struct message_queue * list = q->list; // 假设list非空,说明Q->queue以前满过,就把他们转移回queue[]中,由于那里速度更快 if (list) { // If q->list is not empty, try to load it back to the queue struct message_queue *newhead = list->next; if (__sync_bool_compare_and_swap(&q->list, list, newhead)) { // try load list only once, if success , push it back to the queue. list->next = NULL; skynet_globalmq_push(list); } } // 从头取一个消息队列 struct message_queue * mq = q->queue[head_ptr]; if (mq == NULL) { // globalmq push not complete return NULL; } // 取走一个消息后自然要将index往后移动一个位置,而且刚那个position置为空 if (!__sync_bool_compare_and_swap(&q->head, head, head+1)) { return NULL; } // 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; }