You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

449 lines
9.0 KiB
C

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include "lua-timer.h"
#include <time.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#if defined(__APPLE__)
#include <AvailabilityMacros.h>
#include <sys/time.h>
#include <mach/task.h>
#include <mach/mach.h>
#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, &current);
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;
}