#include "skynet.h" #include "skynet_timer.h" #include "skynet_mq.h" #include "skynet_server.h" #include "skynet_handle.h" #include "spinlock.h" #include #include #include #include #include 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 { uint32_t handle; int session; }; struct timer_node { struct timer_node *next; uint32_t expire; }; 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]; struct spinlock lock; uint32_t time; uint32_t starttime; uint64_t current; uint64_t current_point; }; static struct timer * TI = NULL; 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 void add_node(struct timer *T,struct timer_node *node) { 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); } 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; } link(&T->t[i][((time>>(TIME_NEAR_SHIFT + i*TIME_LEVEL_SHIFT)) & TIME_LEVEL_MASK)],node); } } static 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); SPIN_LOCK(T); node->expire=time+T->time; add_node(T,node); SPIN_UNLOCK(T); } static 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 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(struct timer_node *current) { do { struct timer_event * event = (struct timer_event *)(current+1); struct skynet_message message; message.source = 0; message.session = event->session; message.data = NULL; message.sz = (size_t)PTYPE_RESPONSE << MESSAGE_TYPE_SHIFT; skynet_context_push(event->handle, &message); struct timer_node * temp = current; current=current->next; skynet_free(temp); } while (current); } static inline void timer_execute(struct timer *T) { int idx = T->time & TIME_NEAR_MASK; while (T->near[idx].head.next) { struct timer_node *current = link_clear(&T->near[idx]); SPIN_UNLOCK(T); // dispatch_list don't need lock T dispatch_list(current); SPIN_LOCK(T); } } static void timer_update(struct timer *T) { SPIN_LOCK(T); // try to dispatch timeout 0 (rare condition) timer_execute(T); // shift time first, and then dispatch timer message timer_shift(T); timer_execute(T); SPIN_UNLOCK(T); } static 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;inear[i]); } for (i=0;i<4;i++) { for (j=0;jt[i][j]); } } SPIN_INIT(r) r->current = 0; return r; } int skynet_timeout(uint32_t handle, int time, int session) { if (time <= 0) { struct skynet_message message; message.source = 0; message.session = session; message.data = NULL; message.sz = (size_t)PTYPE_RESPONSE << MESSAGE_TYPE_SHIFT; if (skynet_context_push(handle, &message)) { return -1; } } else { struct timer_event event; event.handle = handle; event.session = session; timer_add(TI, &event, sizeof(event), time); } return session; } // centisecond: 1/100 second static void systime(uint32_t *sec, uint32_t *cs) { struct timespec ti; clock_gettime(CLOCK_REALTIME, &ti); *sec = (uint32_t)ti.tv_sec; *cs = (uint32_t)(ti.tv_nsec / 10000000); } static uint64_t gettime() { uint64_t t; struct timespec ti; clock_gettime(CLOCK_MONOTONIC, &ti); t = (uint64_t)ti.tv_sec * 100; t += ti.tv_nsec / 10000000; return t; } void skynet_updatetime(void) { uint64_t cp = gettime(); if(cp < TI->current_point) { skynet_error(NULL, "time diff error: change from %lld to %lld", cp, TI->current_point); TI->current_point = cp; } else if (cp != TI->current_point) { uint32_t diff = (uint32_t)(cp - TI->current_point); TI->current_point = cp; TI->current += diff; int i; for (i=0;istarttime; } uint64_t skynet_now(void) { return TI->current; } void skynet_timer_init(void) { TI = timer_create_timer(); uint32_t current = 0; systime(&TI->starttime, ¤t); TI->current = current; TI->current_point = gettime(); } // for profile #define NANOSEC 1000000000 #define MICROSEC 1000000 uint64_t skynet_thread_time(void) { struct timespec ti; clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ti); return (uint64_t)ti.tv_sec * MICROSEC + (uint64_t)ti.tv_nsec / (NANOSEC / MICROSEC); }