#include "lua-timer.h" #include #include #include #include #include #if defined(__APPLE__) #include #include #include #include #endif #include "lua.h" #include "lauxlib.h" #include "skynet_malloc.h" typedef void (*timer_execute_func)(void *ud, void *arg); #define TIME_NEAR_SHIFT 8 #define TIME_NEAR (1 << TIME_NEAR_SHIFT) #define TIME_LEVEL_SHIFT 6 #define TIME_LEVEL (1 << TIME_LEVEL_SHIFT) #define TIME_NEAR_MASK (TIME_NEAR - 1) #define TIME_LEVEL_MASK (TIME_LEVEL - 1) struct timer_event { short rear; int64_t level; int64_t near; }; struct timer_node { struct timer_node *next; uint32_t expire; uint64_t id; }; struct link_list { struct timer_node head; struct timer_node *tail; }; struct timer { struct link_list near[TIME_NEAR]; struct link_list t[4][TIME_LEVEL]; uint32_t time; uint32_t starttime; uint64_t current; uint64_t current_point; uint64_t cnt; }; static inline struct timer_node * link_clear(struct link_list *list) { struct timer_node *ret = list->head.next; list->head.next = 0; list->tail = &(list->head); return ret; } static inline void link(struct link_list *list, struct timer_node *node) { list->tail->next = node; list->tail = node; node->next = 0; } static inline void add_node(struct timer *T, struct timer_node *node) { struct timer_event *tevent = (struct timer_event *)(node + 1); // 添加节点 uint32_t time = node->expire; uint32_t current_time = T->time; if ((time | TIME_NEAR_MASK) == (current_time | TIME_NEAR_MASK)) { link(&T->near[time & TIME_NEAR_MASK], node); tevent->near = time & TIME_NEAR_MASK; // printf("near ----%d,%d,%d,%d\n",time,current_time,time|TIME_NEAR_MASK,current_time|TIME_NEAR_MASK); } else { int i; uint32_t mask = TIME_NEAR << TIME_LEVEL_SHIFT; for (i = 0; i < 3; i++) { if ((time | (mask - 1)) == (current_time | (mask - 1))) { break; } mask <<= TIME_LEVEL_SHIFT; } int64_t level = (time >> (TIME_NEAR_SHIFT + i * TIME_LEVEL_SHIFT)) & TIME_LEVEL_MASK; tevent->rear = i; tevent->level = level; // printf("REAR ----%d,%d,| %d,%d\n",time,current_time,level,i); link(&T->t[i][level], node); } } static inline void * timer_add(struct timer *T, void *arg, size_t sz, int time) { struct timer_node *node = (struct timer_node *)skynet_malloc(sizeof(*node) + sz); memcpy(node + 1, arg, sz); node->expire = time + T->time; // printf(" add == %d, t->time=%d , expire =%d\n",time,T->time,node->expire); add_node(T, node); return node; } static inline void move_list(struct timer *T, int level, int idx) { struct timer_node *current = link_clear(&T->t[level][idx]); while (current) { struct timer_node *temp = current->next; add_node(T, current); current = temp; } } static inline void timer_shift(struct timer *T) { int mask = TIME_NEAR; uint32_t ct = ++T->time; if (ct == 0) { move_list(T, 3, 0); } else { uint32_t time = ct >> TIME_NEAR_SHIFT; int i = 0; while ((ct & (mask - 1)) == 0) { int idx = time & TIME_LEVEL_MASK; if (idx != 0) { move_list(T, i, idx); break; } mask <<= TIME_LEVEL_SHIFT; time >>= TIME_LEVEL_SHIFT; ++i; } } } static inline void dispatch_list(lua_State *L, struct timer_node *current, int *tidx) { do { // 获取数据 *tidx = *tidx + 1; lua_pushinteger(L, current->id); lua_rawseti(L, -2, *tidx); // printf("dispatch tidx=%d=%d\n ",*tidx,current->id); struct timer_node *temp = current; current = current->next; skynet_free(temp); } while (current); } static inline void timer_execute(lua_State *L, struct timer *T, short *hasdata, int *tidx) { int idx = T->time & TIME_NEAR_MASK; // printf("exectue ==%d,idx=%d \n",T->time,idx); while (T->near[idx].head.next) { if (*hasdata == 0) { *hasdata = 1; lua_newtable(L); } struct timer_node *current = link_clear(&T->near[idx]); dispatch_list(L, current, tidx); } } static inline struct timer * timer_create_timer() { struct timer *r = (struct timer *)skynet_malloc(sizeof(struct timer)); memset(r, 0, sizeof(*r)); int i, j; for (i = 0; i < TIME_NEAR; i++) { link_clear(&r->near[i]); } for (i = 0; i < 4; i++) { for (j = 0; j < TIME_LEVEL; j++) { link_clear(&r->t[i][j]); } } r->current = 0; return r; } // centisecond: 1/100 second static inline void systime(uint32_t *sec, uint32_t *cs) { #if !defined(__APPLE__) || defined(AVAILABLE_MAC_OS_X_VERSION_10_12_AND_LATER) struct timespec ti; clock_gettime(CLOCK_REALTIME, &ti); *sec = (uint32_t)ti.tv_sec; *cs = (uint32_t)(ti.tv_nsec / 10000000); #else struct timeval tv; gettimeofday(&tv, NULL); *sec = tv.tv_sec; *cs = tv.tv_usec / 10000; #endif } static inline uint64_t gettime() { uint64_t t; #if !defined(__APPLE__) || defined(AVAILABLE_MAC_OS_X_VERSION_10_12_AND_LATER) struct timespec ti; clock_gettime(CLOCK_MONOTONIC, &ti); t = (uint64_t)ti.tv_sec * 100; t += ti.tv_nsec / 10000000; #else struct timeval tv; gettimeofday(&tv, NULL); t = (uint64_t)tv.tv_sec * 100; t += tv.tv_usec / 10000; #endif return t; } // for profile #define NANOSEC 1000000000 #define MICROSEC 1000000 uint64_t skynet_thread_time(void) { #if !defined(__APPLE__) || defined(AVAILABLE_MAC_OS_X_VERSION_10_12_AND_LATER) struct timespec ti; clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ti); return (uint64_t)ti.tv_sec * MICROSEC + (uint64_t)ti.tv_nsec / (NANOSEC / MICROSEC); #else struct task_thread_times_info aTaskInfo; mach_msg_type_number_t aTaskInfoCount = TASK_THREAD_TIMES_INFO_COUNT; if (KERN_SUCCESS != task_info(mach_task_self(), TASK_THREAD_TIMES_INFO, (task_info_t)&aTaskInfo, &aTaskInfoCount)) { return 0; } return (uint64_t)(aTaskInfo.user_time.seconds) + (uint64_t)aTaskInfo.user_time.microseconds; #endif } static inline struct timer * _to_timer(lua_State *L) { struct timer **t = lua_touserdata(L, 1); if (t == NULL) { luaL_error(L, "must be timer object"); } return *t; } #define INVALID_VALUE -1 static inline int _add(lua_State *L) { struct timer *pt = _to_timer(L); int second = luaL_checkinteger(L, 2); pt->cnt++; // 添加计时器 struct timer_event event; // 初始赋值 event.near = INVALID_VALUE; event.level = INVALID_VALUE; event.rear = INVALID_VALUE; struct timer_node *ptnode = timer_add(pt, &event, sizeof(event), second); ptnode->id = pt->cnt; //记录ID lua_pushinteger(L, pt->cnt); lua_pushlightuserdata(L, ptnode); return 2; } static inline int _del(lua_State *L) { struct timer *pt = _to_timer(L); struct timer_node *ptnode = lua_touserdata(L, 2); struct timer_event *pevent = (struct timer_event *)(ptnode + 1); struct link_list *list = 0; if (pevent->near != INVALID_VALUE) { list = &pt->near[pevent->near]; } else if (pevent->rear != INVALID_VALUE && pevent->level != INVALID_VALUE) { list = &pt->t[pevent->rear][pevent->level]; } short found = 0; // 头节点 struct timer_node *current = link_clear(list); while (current) { if (current == ptnode) { //去掉当前节点,并跳过当前节点,直接遍历下个节点 found = 1; struct timer_node *temp = current->next; struct timer_node *removenode = current; skynet_free(removenode); current = temp; } else { struct timer_node *temp = current->next; add_node(pt, current); current = temp; } } lua_pushboolean(L, found); return 1; } static int _new(lua_State *L) { struct timer *pt = timer_create_timer(); uint32_t current = 0; systime(&pt->starttime, ¤t); pt->current = current; pt->current_point = gettime(); pt->cnt = 0; struct timer **t = (struct timer **)lua_newuserdata(L, sizeof(struct timer *)); *t = pt; lua_pushvalue(L, lua_upvalueindex(1)); lua_setmetatable(L, -2); return 1; } static inline int _release(lua_State *L) { struct timer *pt = _to_timer(L); // printf("collect pt:%p\n", pt); skynet_free(pt); return 0; } static inline int _inc(lua_State *L) { struct timer *pt = _to_timer(L); pt->cnt++; lua_pushinteger(L, pt->cnt); return 1; } static inline int _update(lua_State *L) { struct timer *pt = _to_timer(L); // 返回数据 int tidx = 0; uint64_t cp = gettime(); if (cp < pt->current_point) { // printf("time diff error: change from %lld to %lld", cp, pt->current_point); pt->current_point = cp; } else if (cp != pt->current_point) { uint32_t diff = (uint32_t)(cp - pt->current_point); pt->current_point = cp; pt->current += diff; // printf("current=%d,cpoint=%d,diff=%d,cp=%d\n",pt->current,pt->current_point,diff,cp); int i; short hasdata = 0; for (i = 0; i < diff; i++) { timer_execute(L, pt, &hasdata, &tidx); timer_shift(pt); timer_execute(L, pt, &hasdata, &tidx); } } if (tidx == 0) { lua_pushnil(L); } return 1; } int luaopen_shiftimer_c(lua_State *L) { luaL_checkversion(L); luaL_Reg l[] = { {"add", _add}, {"del", _del}, {"nextid", _inc}, {"update", _update}, {NULL, NULL}}; lua_createtable(L, 0, 2); luaL_newlib(L, l); lua_setfield(L, -2, "__index"); lua_pushcfunction(L, _release); lua_setfield(L, -2, "__gc"); lua_pushcclosure(L, _new, 1); return 1; }