前言
handle句柄,每一个context出生都会注册一个handle唯一id,skynet_handle集中记录存活的context地址还有设置本地别名,提供别名查找handleid,handle_id拿到context地址。
skynet_context_new(const char * name, const char *param) {
...
ctx->handle = skynet_handle_register(ctx);
...
}
skynet_handle.h
#ifndef SKYNET_CONTEXT_HANDLE_H
#define SKYNET_CONTEXT_HANDLE_H
#include <stdint.h>
// reserve high 8 bits for remote id
#define HANDLE_MASK 0xffffff
#define HANDLE_REMOTE_SHIFT 24
struct skynet_context;
uint32_t skynet_handle_register(struct skynet_context *); //注册
int skynet_handle_retire(uint32_t handle); //注销
struct skynet_context * skynet_handle_grab(uint32_t handle); //增加context引用计数
void skynet_handle_retireall();
uint32_t skynet_handle_findname(const char * name); //通过别名查找handleid
const char * skynet_handle_namehandle(uint32_t handle, const char *name); //注册handle别名
void skynet_handle_init(int harbor);
#endif
skynet_handle.h
数据结构
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;
int name_cap;
int name_count;
struct handle_name *name;
};
static struct handle_storage *H = NULL;
slot用于缓存context数据地址。
name用户管理别名与handle_id绑定。
skynet_handle_register实现解析
uint32_t
skynet_handle_register(struct skynet_context *ctx) {
struct handle_storage *s = H;
rwlock_wlock(&s->lock);
for (;;) {
int i;
uint32_t handle = s->handle_index;
for (i=0;i<s->slot_size;i++,handle++) {
if (handle > HANDLE_MASK) {
// 0 is reserved
handle = 1;
}
int hash = handle & (s->slot_size-1);
if (s->slot[hash] == NULL) {
s->slot[hash] = ctx;
s->handle_index = handle + 1;
rwlock_wunlock(&s->lock);
handle |= s->harbor;
return handle;
}
}
assert((s->slot_size*2 - 1) <= HANDLE_MASK);
struct skynet_context ** new_slot = skynet_malloc(s->slot_size * 2 * sizeof(struct skynet_context *));
memset(new_slot, 0, s->slot_size * 2 * sizeof(struct skynet_context *));
for (i=0;i<s->slot_size;i++) {
int hash = skynet_context_handle(s->slot[i]) & (s->slot_size * 2 - 1);
assert(new_slot[hash] == NULL);
new_slot[hash] = s->slot[i];
}
skynet_free(s->slot);
s->slot = new_slot;
s->slot_size *= 2;
}
}
初始会分配4个长度的指针缓存区,每当缓存区满时,会重新分配2倍之前大小的缓存区。
时间复杂度分析:
结构是用的数组哈希。
哈希函数为:int hash = handle & (s->slot_size-1);
一个坑位一个值,复杂度为o1。
没有注销服务:handle_index 每注册一个 都会更新到handle + 1。
下次注册的时间复杂度为o1。
有注销服务:注册时间复杂度最高达到on。
注册别名
static void
_insert_name_before(struct handle_storage *s, char *name, uint32_t handle, int before) {
if (s->name_count >= s->name_cap) {
s->name_cap *= 2;
assert(s->name_cap <= MAX_SLOT_SIZE);
struct handle_name * n = skynet_malloc(s->name_cap * sizeof(struct handle_name));
int i;
for (i=0;i<before;i++) {
n[i] = s->name[i];
}
for (i=before;i<s->name_count;i++) {
n[i+1] = s->name[i];
}
skynet_free(s->name);
s->name = n;
} else {
int i;
for (i=s->name_count;i>before;i--) {
s->name[i] = s->name[i-1];
}
}
s->name[before].name = name;
s->name[before].handle = handle;
s->name_count ++;
}
static const char *
_insert_name(struct handle_storage *s, const char * name, uint32_t handle) {
int begin = 0;
int end = s->name_count - 1;
while (begin<=end) {
int mid = (begin+end)/2;
struct handle_name *n = &s->name[mid];
int c = strcmp(n->name, name);
if (c==0) {
return NULL;
}
if (c<0) {
begin = mid + 1;
} else {
end = mid - 1;
}
}
char * result = skynet_strdup(name);
_insert_name_before(s, result, handle, begin);
return result;
}
const char *
skynet_handle_namehandle(uint32_t handle, const char *name) {
rwlock_wlock(&H->lock);
const char * ret = _insert_name(H, name, handle);
rwlock_wunlock(&H->lock);
return ret;
}
name缓存区数组,采用的是2分查找,插入的时候会保持他的有序性。插入查找时间复杂度为o(logn)