skynet消息队列源码分析

来源:互联网 发布:adobe for mac 编辑:程序博客网 时间:2024/05/01 18:36

#include "skynet.h"
#include "skynet_mq.h"
#include "skynet_handle.h"

#include
#include
#include
#include
#include

#define DEFAULT_QUEUE_SIZE 64 //默认队列大小
#define MAX_GLOBAL_MQ 0x10000 //64k 单机服务上限是64k 因而global mq 数量最大值也是64k
         //服务的id空间是2^24 即16M

// 0 means mq is not in global mq.
// 1 means mq is in global mq , or the message is dispatching.
// 2 means message is dispatching with locked session set.
// 3 means mq is not in global mq, and locked session has been set.

#define MQ_IN_GLOBAL 1
#define MQ_DISPATCHING 2
#define MQ_LOCKED 3

// 消息队列(循环队列),容量不固定,按需增长
// 消息队列 mq 的结构
struct message_queue {
 uint32_t handle; // 所属服务handle
 int cap;   // 容量
 int head;   // 队头
 int tail;   // 队尾
 int lock;   // 用于实现自旋锁 加锁将这个值设置为1 释放锁将这个值设置为0
 int release;  // 消息队列释放标记,当要释放一个服务的时候清理标记
                        // 不能立即释放该服务对应的消息队列(有可能工作线程还在操作mq),就需要设置一个标记标记是否可以释放
 int lock_session; // 被某个session对应的消息锁定
 int in_global;  // 消息当前的状态(0 不在全局队列中 1 在全局队列中 2 锁定调度 3 锁定且不在全局队列中 )
 struct skynet_message *queue; //消息队列
};

// 全局队列(循环队列,无锁队列),容量固定64K 二级队列的实现
// 保存了 所有的消息 就是从这个队列中取消息出来做处理
struct global_queue {
 uint32_t head;
 uint32_t tail;
 struct message_queue ** queue;  // 消息队列列表,预留MAX_GLOBAL_MQ(64K)个空间
 bool * flag; // 与queue对应,预留MAX_GLOBAL_MQ(64K)个空间,用于标识相应位置是否有消息队列
                    // 当前实现的无锁队列,需要用到该标记 标记这个位置 tail已经用过了 已经完全将这个消息队列 压入全局的消息队里中
};

static struct global_queue *Q = NULL; // 全局的消息队列  Q

#define LOCK(q) while (__sync_lock_test_and_set(&(q)->lock,1)) {} // 利用__sync_lock_test_and_set实现的自旋锁
                 // 将q->lock设置为1,并返回修改前的值
#define UNLOCK(q) __sync_lock_release(&(q)->lock);     // 将q->lock置为0

#define GP(p) ((p) % MAX_GLOBAL_MQ)         // 得到在队列中的位置


static void
skynet_globalmq_push(struct message_queue * queue) {
 struct global_queue *q= Q;

 uint32_t tail = GP(__sync_fetch_and_add(&q->tail,1));
 q->queue[tail] = queue; // 将这个消息放入全局队列
 __sync_synchronize();
 q->flag[tail] = true; // 标记这个位置 tail已经用过了 已经完全将这个消息队列 压入全局的消息队里中
}

struct message_queue *
skynet_globalmq_pop() {
 struct global_queue *q = Q;
 uint32_t head =  q->head;
 uint32_t head_ptr = GP(head);
 
 // 队列为空
 if (head_ptr == GP(q->tail)) {
  return NULL;
 }
 
 // head所在位置没有mq
 if(!q->flag[head_ptr]) {
  return NULL;
 }

 __sync_synchronize(); // 同步指令 保证前面的指令执行完毕,才会执行后面的指令

 struct message_queue * mq = q->queue[head_ptr];
 
 // CAS原子性操作 如果q->head == head,则q->head=head+1; 移动头部
 if (!__sync_bool_compare_and_swap(&q->head, head, head+1)) {
  return NULL;
 }
 q->flag[head_ptr] = false; // 消息已经被取走

 return mq;
}

// 创建消息队列,初始容量 DEFAULT_QUEUE_SIZE 64个
struct message_queue *
skynet_mq_create(uint32_t handle) {
 struct message_queue *q = skynet_malloc(sizeof(*q));
 q->handle = handle;
 q->cap = DEFAULT_QUEUE_SIZE;
 q->head = 0;
 q->tail = 0;
 q->lock = 0; // 不锁
 q->in_global = MQ_IN_GLOBAL;
 q->release = 0;
 q->lock_session = 0;
 q->queue = skynet_malloc(sizeof(struct skynet_message) * q->cap);

 return q;
}

static void
_release(struct message_queue *q) {
 skynet_free(q->queue);
 skynet_free(q);
}

uint32_t
skynet_mq_handle(struct message_queue *q) {
 return q->handle;
}

int
skynet_mq_length(struct message_queue *q) {
 int head, tail,cap;

 LOCK(q)
 head = q->head;
 tail = q->tail;
 cap = q->cap;
 UNLOCK(q)
 
 if (head <= tail) {
  return tail - head; // 正常 没有循环回来
 }
 return tail + cap - head; // 循环回来了
}

int
skynet_mq_pop(struct message_queue *q, struct skynet_message *message) {
 int ret = 1;
 LOCK(q)
 
 // 消息队列不为空
 if (q->head != q->tail) {
  *message = q->queue[q->head];
  ret = 0;
  if ( ++ q->head >= q->cap) {
   q->head = 0;
  }
 }
 
 // 没有消息弹出,消息队列为空,则不再将消息队列压入全局队列 消息队列为空的就是就不再压入 globe_mq 中
 if (ret) {
  q->in_global = 0;
 }
 
 UNLOCK(q)

 return ret;
}

// 扩大mq 2倍的大小扩大
static void
expand_queue(struct message_queue *q) {
 struct skynet_message *new_queue = skynet_malloc(sizeof(struct skynet_message) * q->cap * 2);
 int i;
 
 // 将原队列消息搬到新队列
 for (i=0;icap;i++) {
  new_queue[i] = q->queue[(q->head + i) % q->cap];
 }
 q->head = 0;
 q->tail = q->cap;
 q->cap *= 2;  // 2倍的大小扩大
 
 skynet_free(q->queue); // 释放原来的空间
 q->queue = new_queue;
}

static void
_unlock(struct message_queue *q) {
 // this api use in push a unlock message, so the in_global flags must not be 0 ,
 // but the q is not exist in global queue.
 if (q->in_global == MQ_LOCKED) {
  skynet_globalmq_push(q);
  q->in_global = MQ_IN_GLOBAL;
 } else {
  assert(q->in_global == MQ_DISPATCHING);
 }
 q->lock_session = 0;
}

static void
_pushhead(struct message_queue *q, struct skynet_message *message) {
 int head = q->head - 1;
 if (head < 0) {
  head = q->cap - 1;
 }
 
 // 队列已满 扩大队列 容量2倍
 if (head == q->tail) {
  expand_queue(q);
  --q->tail;
  head = q->cap - 1;
 }

 q->queue[head] = *message;
 q->head = head;

 _unlock(q);
}

void
skynet_mq_push(struct message_queue *q, struct skynet_message *message) {
 assert(message);
 LOCK(q)
 
 if (q->lock_session !=0 && message->session == q->lock_session) {
  _pushhead(q,message);
 } else {
  q->queue[q->tail] = *message;
  if (++ q->tail >= q->cap) {
   q->tail = 0;
  }

  if (q->head == q->tail) {
   expand_queue(q);
  }

  if (q->lock_session == 0) {
   if (q->in_global == 0) {
    q->in_global = MQ_IN_GLOBAL;
    skynet_globalmq_push(q);
   }
  }
 }
 
 UNLOCK(q)
}

void
skynet_mq_lock(struct message_queue *q, int session) {
 LOCK(q)
 assert(q->lock_session == 0);
 assert(q->in_global == MQ_IN_GLOBAL);
 q->in_global = MQ_DISPATCHING; // 将消息状态置为dispatching
 q->lock_session = session;
 UNLOCK(q)
}

void
skynet_mq_unlock(struct message_queue *q) {
 LOCK(q)
 _unlock(q);
 UNLOCK(q)
}

// 初始化全局消息队列,容量固定64K
// 单机服务最大值64K,因而全局消息队列容量固定64K,方便全局消息队列实现为无锁队列
void
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 *)); // 64K的消息队列
 q->flag = skynet_malloc(MAX_GLOBAL_MQ * sizeof(bool)); // 标志这个位置是否用了
 memset(q->flag, 0, sizeof(bool) * 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);
 if (queue->in_global == MQ_DISPATCHING) {
  // lock message queue just now.
  queue->in_global = MQ_LOCKED;
 }
 if (queue->lock_session == 0) {
  skynet_globalmq_push(queue);
  queue->in_global = MQ_IN_GLOBAL;
 }
 UNLOCK(queue)
}

void
skynet_mq_mark_release(struct message_queue *q) {
 LOCK(q)
 assert(q->release == 0);
 q->release = 1;
 if (q->in_global != MQ_IN_GLOBAL) {
  skynet_globalmq_push(q);
 }
 UNLOCK(q)
}

static int
_drop_queue(struct message_queue *q) {
 // todo: send message back to message source
 struct skynet_message msg;
 int s = 0;
 while(!skynet_mq_pop(q, &msg)) {
  ++s;
  skynet_free(msg.data);
 }
 _release(q);
 return s;
}

int
skynet_mq_release(struct message_queue *q) {
 int ret = 0;
 LOCK(q)
 
 if (q->release) {
  UNLOCK(q)
  ret = _drop_queue(q);
 } else {
  skynet_mq_force_push(q);
  UNLOCK(q)
 }
 
 return ret;
}

0 0
原创粉丝点击