瀏覽代碼

add period, fix timeout

ithewei 6 年之前
父節點
當前提交
69c146af73
共有 9 個文件被更改,包括 344 次插入101 次删除
  1. 1 3
      base/hlog.c
  2. 2 1
      base/hplatform.h
  3. 4 0
      base/hsocket.h
  4. 150 14
      base/htime.c
  5. 27 2
      base/htime.h
  6. 0 1
      event/hevent.h
  7. 97 58
      event/hloop.c
  8. 55 22
      event/hloop.h
  9. 8 0
      examples/loop.c

+ 1 - 3
base/hlog.c

@@ -6,8 +6,6 @@
 
 #include "htime.h"  // for get_datetime
 
-#define SECONDS_PER_DAY     86400
-
 static char     s_logfile[256] = DEFAULT_LOG_FILE;
 static int      s_loglevel = DEFAULT_LOG_LEVEL;
 static bool     s_logcolor = false;
@@ -140,7 +138,7 @@ int hlog_printf(int level, const char* fmt, ...) {
         return -20;
     }
 
-    datetime_t now = get_datetime();
+    datetime_t now = datetime_now();
     int len = snprintf(s_logbuf, LOG_BUFSIZE, "[%04d-%02d-%02d %02d:%02d:%02d.%03d][%s]: ",
         now.year, now.month, now.day, now.hour, now.min, now.sec, now.ms, plevel);
 

+ 2 - 1
base/hplatform.h

@@ -93,8 +93,9 @@
     #ifndef WIN32_LEAN_AND_MEAN
     #define WIN32_LEAN_AND_MEAN
     #endif
-    #define _CRT_SECURE_NO_WARNINGS
     #define _CRT_NONSTDC_NO_DEPRECATE
+    #define _CRT_SECURE_NO_WARNINGS
+    #define _WINSOCK_DEPRECATED_NO_WARNINGS
     #include <winsock2.h>
     #include <windows.h>
     #include <process.h>    // for getpid,exec

+ 4 - 0
base/hsocket.h

@@ -4,6 +4,10 @@
 #include "hplatform.h"
 #include "hdef.h"
 
+#ifdef _MSC_VER
+#pragma comment(lib, "ws2_32.lib")
+#endif
+
 BEGIN_EXTERN_C
 
 // socket -> setsockopt -> bind -> listen

+ 150 - 14
base/htime.c

@@ -1,5 +1,22 @@
 #include "htime.h"
 
+#include <string.h>
+#ifdef _MSC_VER
+    #define strcasecmp stricmp
+    #define strncasecmp strnicmp
+#else
+    #include <strings.h>
+    #define stricmp     strcasecmp
+    #define strnicmp    strncasecmp
+#endif
+
+static const char* s_months[] = {"January", "February", "March", "April", "May", "June",
+    "July", "August", "September", "October", "November", "December"};
+
+static const uint8_t s_days[] = \
+//   1       3       5       7   8       10      12
+    {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
 inline unsigned long long gethrtime() {
 #ifdef OS_WIN
     static LONGLONG s_freq = 0;
@@ -23,7 +40,7 @@ inline unsigned long long gethrtime() {
 #endif
 }
 
-datetime_t get_datetime() {
+datetime_t datetime_now() {
     datetime_t  dt;
 #ifdef OS_WIN
     SYSTEMTIME tm;
@@ -53,21 +70,140 @@ datetime_t get_datetime() {
     return dt;
 }
 
-static const char* s_month[] = {"January", "February", "March", "April", "May", "June",
-    "July", "August", "September", "October", "November", "December"};
+time_t datetime_mktime(datetime_t* dt) {
+    struct tm tm;
+    tm.tm_yday  = dt->year   - 1900;
+    tm.tm_mon   = dt->month  - 1;
+    tm.tm_mday  = dt->day;
+    tm.tm_hour  = dt->hour;
+    tm.tm_min   = dt->min;
+    tm.tm_sec   = dt->sec;
+    return mktime(&tm);
+}
+
+int days_of_month(int month, int year) {
+    if (month < 1 || month > 12) {
+        return 0;
+    }
+    int days = s_days[month-1];
+    return (month == 2 && IS_LEAP_YEAR(year)) ? ++days : days;
+}
+
+datetime_t* datetime_past(datetime_t* dt, int days) {
+    assert(days >= 0);
+    int sub = days;
+    while (sub) {
+        if (dt->day > sub) {
+            dt->day -= sub;
+            break;
+        }
+        sub -= dt->day;
+        if (--dt->month == 0) {
+            dt->month = 12;
+            --dt->year;
+        }
+        dt->day = days_of_month(dt->month, dt->year);
+    }
+    return dt;
+}
+
+datetime_t* datetime_future(datetime_t* dt, int days) {
+    assert(days >= 0);
+    int sub = days;
+    int mdays;
+    while (sub) {
+        mdays = days_of_month(dt->month, dt->year);
+        if (dt->day + sub <= mdays) {
+            dt->day += sub;
+            break;
+        }
+        sub -= (mdays - dt->day + 1);
+        if (++dt->month > 12) {
+            dt->month = 1;
+            ++dt->year;
+        }
+        dt->day = 1;
+    }
+    return dt;
+}
+
+time_t calc_next_timeout(int minute, int hour, int day, int week, int month) {
+    enum {
+        UNKOWN,
+        HOURLY,
+        DAILY,
+        WEEKLY,
+        MONTHLY,
+        YEARLY,
+    } period_type = UNKOWN;
+    struct tm tm;
+    time_t tt;
+    time(&tt);
+    tm = *localtime(&tt);
+    time_t tt_round = 0;
+
+    tm.tm_sec = 0;
+    if (minute >= 0) {
+        period_type = HOURLY;
+        tm.tm_min = minute;
+    }
+    if (hour >= 0) {
+        period_type = DAILY;
+        tm.tm_hour = hour;
+    }
+    if (week >= 0) {
+        period_type = WEEKLY;
+    }
+    else if (day > 0) {
+        period_type = MONTHLY;
+        tm.tm_mday = day;
+        if (month > 0) {
+            period_type = YEARLY;
+            tm.tm_mon = month - 1;
+        }
+    }
+
+    if (period_type == UNKOWN) {
+        return -1;
+    }
+
+    tt_round = mktime(&tm);
+    if (week >= 0) {
+        tt_round = tt + (week-tm.tm_wday)*SECONDS_PER_DAY;
+    }
+    if (tt_round > tt) {
+        return tt_round;
+    }
+
+    switch(period_type) {
+    case HOURLY:
+        tt_round += SECONDS_PER_HOUR;
+        return tt_round;
+    case DAILY:
+        tt_round += SECONDS_PER_DAY;
+        return tt_round;
+    case WEEKLY:
+        tt_round += SECONDS_PER_WEEK;
+        return tt_round;
+    case MONTHLY:
+        if (++tm.tm_mon == 12) {
+            tm.tm_mon = 0;
+            ++tm.tm_year;
+        }
+        break;
+    case YEARLY:
+        ++tm.tm_year;
+        break;
+    default:
+        return -1;
+    }
+
+    return mktime(&tm);
+}
 
-#include <string.h>
-#ifdef _MSC_VER
-    #define strcasecmp stricmp
-    #define strncasecmp strnicmp
-#else
-    #include <strings.h>
-    #define stricmp     strcasecmp
-    #define strnicmp    strncasecmp
-#endif
 int month_atoi(const char* month) {
     for (size_t i = 0; i < 12; ++i) {
-        if (strnicmp(month, s_month[i], strlen(month)) == 0)
+        if (strnicmp(month, s_months[i], strlen(month)) == 0)
             return i+1;
     }
     return 0;
@@ -77,7 +213,7 @@ const char* month_itoa(int month) {
     if (month < 1 || month > 12) {
         return NULL;
     }
-    return s_month[month-1];
+    return s_months[month-1];
 }
 
 datetime_t get_compile_datetime() {

+ 27 - 2
base/htime.h

@@ -6,6 +6,14 @@ extern "C" {
 #endif
 
 #include "hplatform.h"
+#include "hdef.h"
+
+#define SECONDS_PER_HOUR    3600
+#define SECONDS_PER_DAY     86400   // 24*3600
+#define SECONDS_PER_WEEK    604800  // 7*24*3600
+
+#define IS_LEAP_YEAR(year) (((year)%4 == 0 && (year)%100 != 0) ||\
+                            (year)%100 == 0)
 
 typedef struct datetime_s {
     int year;
@@ -43,12 +51,29 @@ static inline unsigned int gettick() {
 // us
 unsigned long long gethrtime();
 
-datetime_t get_datetime();
-datetime_t get_compile_datetime();
+datetime_t datetime_now();
+time_t     datetime_mktime(datetime_t* dt);
+
+datetime_t* datetime_past(datetime_t* dt, int days DEFAULT(1));
+datetime_t* datetime_future(datetime_t* dt, int days DEFAULT(1));
 
+/*
+ * minute   hour    day     week    month       action
+ * 0~59     0~23    1~31    0~6     1~12
+ *  30      -1      -1      -1      -1          cron.hourly
+ *  30      1       -1      -1      -1          cron.daily
+ *  30      1       15      -1      -1          cron.monthly
+ *  30      1       -1       7      -1          cron.weekly
+ *  30      1        1      -1      10          cron.yearly
+ */
+time_t calc_next_timeout(int minute, int hour, int day, int week, int month);
+
+int days_of_month(int month, int year);
 int month_atoi(const char* month);
 const char* month_itoa(int month);
 
+datetime_t get_compile_datetime();
+
 #ifdef __cplusplus
 } // extern "C"
 #endif

+ 0 - 1
event/hevent.h

@@ -8,7 +8,6 @@
 #define EVENT_ENTRY(p)          container_of(p, hevent_t, pending_node)
 #define IDLE_ENTRY(p)           container_of(p, hidle_t,  node)
 #define TIMER_ENTRY(p)          container_of(p, htimer_t, node)
-#define TIMER_HEAP_ENTRY(p)     container_of(p, htimer_t, hnode)
 
 #define EVENT_ACTIVE(ev) \
     if (!ev->active) {\

+ 97 - 58
event/hloop.c

@@ -6,14 +6,13 @@
 #include "hlog.h"
 #include "hmath.h"
 
-#define PAUSE_TIME              10      // ms
-#define MIN_BLOCK_TIME          1       // ms
-#define MAX_BLOCK_TIME          10000   // ms
+#define PAUSE_TIME              10          // ms
+#define MAX_BLOCK_TIME          1000        // ms
 
 #define IO_ARRAY_INIT_SIZE      64
 
-static int timer_minheap_compare(const struct heap_node* lhs, const struct heap_node* rhs) {
-    return TIMER_HEAP_ENTRY(lhs)->timeout < TIMER_HEAP_ENTRY(rhs)->timeout;
+static int timers_compare(const struct heap_node* lhs, const struct heap_node* rhs) {
+    return TIMER_ENTRY(lhs)->next_timeout < TIMER_ENTRY(rhs)->next_timeout;
 }
 
 static int hloop_process_idles(hloop_t* loop) {
@@ -47,31 +46,36 @@ destroy:
 
 static int hloop_process_timers(hloop_t* loop) {
     int ntimers = 0;
-    struct list_node* node = loop->timers.next;
     htimer_t* timer = NULL;
-    while (node != &loop->timers) {
-        timer = TIMER_ENTRY(node);
+    uint64_t now_hrtime = hloop_now_hrtime(loop);
+    while (loop->timers.root) {
+        timer = TIMER_ENTRY(loop->timers.root);
         if (timer->destroy) goto destroy;
-        if (!timer->active) goto next;
         if (timer->repeat == 0) {
             htimer_del(timer);
-            //goto next;
             goto destroy;
         }
-        if (loop->cur_hrtime > timer->next_timeout) {
-            if (timer->repeat != INFINITE) {
-                --timer->repeat;
-            }
-            timer->next_timeout += timer->timeout*1000;
-            EVENT_PENDING(timer);
-            ++ntimers;
+        if (timer->next_timeout > now_hrtime) {
+            break;
         }
-next:
-        node = node->next;
+        if (timer->repeat != INFINITE) {
+            --timer->repeat;
+        }
+        heap_dequeue(&loop->timers);
+        if (timer->event_type == HEVENT_TYPE_TIMEOUT) {
+            timer->next_timeout += ((htimeout_t*)timer)->timeout*1000;
+        }
+        else if (timer->event_type == HEVENT_TYPE_PERIOD) {
+            hperiod_t* period = (hperiod_t*)timer;
+            timer->next_timeout = calc_next_timeout(period->minute, period->hour, period->day,
+                    period->week, period->month) * 1e6;
+        }
+        heap_insert(&loop->timers, &timer->node);
+        EVENT_PENDING(timer);
+        ++ntimers;
         continue;
 destroy:
-        node = node->next;
-        list_del(node->prev);
+        heap_dequeue(&loop->timers);
         free(timer);
     }
     return ntimers;
@@ -114,31 +118,34 @@ static int hloop_process_events(hloop_t* loop) {
     int nios, ntimers, nidles;
     nios = ntimers = nidles = 0;
 
-    int blocktime = MAX_BLOCK_TIME;
-    if (loop->timer_minheap.root) {
-        // if have timers, blocktime = min_timeout
-        blocktime = TIMER_HEAP_ENTRY(loop->timer_minheap.root)->timeout;
-        //if (!list_empty(&loop->idles))
-            blocktime /= 10;
+    int64_t blocktime = 0;
+    hloop_update_time(loop);
+    if (loop->timers.root) {
+        uint64_t next_min_timeout = TIMER_ENTRY(loop->timers.root)->next_timeout;
+        blocktime = next_min_timeout - hloop_now_hrtime(loop);
+        if (blocktime <= 0) goto process_timers;
+        blocktime /= 1000;
+        ++blocktime;
     }
-    blocktime = LIMIT(MIN_BLOCK_TIME, blocktime, MAX_BLOCK_TIME);
-    // if you want timer more precise, reduce blocktime
 
-    uint64_t last_hrtime = loop->cur_hrtime;
-    nios = hloop_process_ios(loop, blocktime);
+    blocktime = MIN(blocktime, MAX_BLOCK_TIME);
+    if (loop->nios) {
+        nios = hloop_process_ios(loop, blocktime);
+    }
+    else {
+        msleep(blocktime);
+    }
     hloop_update_time(loop);
-    ntimers = hloop_process_timers(loop);
+
+process_timers:
+    if (loop->ntimers) {
+        ntimers = hloop_process_timers(loop);
+    }
+
     if (loop->npendings == 0) {
-        loop->idle_time += last_hrtime - loop->cur_hrtime;
-        // avoid frequent call idles
-        if (loop->cur_hrtime - loop->last_idle_hrtime > 1e6) {
-            loop->last_idle_hrtime = loop->cur_hrtime;
+        if (loop->nidles) {
             nidles= hloop_process_idles(loop);
         }
-        else {
-            // hloop_process_ios maybe nonblock, so sleep here
-            msleep(blocktime);
-        }
     }
     printd("blocktime=%d nios=%d ntimers=%d nidles=%d nactives=%d npendings=%d\n", blocktime, nios, ntimers, nidles, loop->nactives, loop->npendings);
     return hloop_process_pendings(loop);
@@ -150,10 +157,12 @@ int hloop_init(hloop_t* loop) {
     // idles
     list_init(&loop->idles);
     // timers
-    list_init(&loop->timers);
-    heap_init(&loop->timer_minheap, timer_minheap_compare);
+    heap_init(&loop->timers, timers_compare);
     // iowatcher
     //iowatcher_init(loop);
+    // time
+    time(&loop->start_time);
+    loop->start_hrtime = loop->cur_hrtime = gethrtime();
     return 0;
 }
 
@@ -172,22 +181,18 @@ void hloop_cleanup(hloop_t* loop) {
     }
     list_init(&loop->idles);
     // timers
-    node = loop->timers.next;
     htimer_t* timer;
-    while (node != &loop->timers) {
+    while (loop->timers.root) {
         timer = TIMER_ENTRY(node);
-        node = node->next;
+        heap_dequeue(&loop->timers);
         free(timer);
     }
-    list_init(&loop->timers);
-    heap_init(&loop->timer_minheap, NULL);
+    heap_init(&loop->timers, NULL);
     // iowatcher
     iowatcher_cleanup(loop);
 };
 
 int hloop_run(hloop_t* loop) {
-    time(&loop->start_time);
-    loop->start_hrtime = gethrtime();
     loop->loop_cnt = 0;
     loop->status = HLOOP_STATUS_RUNNING;
     while (loop->status != HLOOP_STATUS_STOP) {
@@ -229,31 +234,62 @@ hidle_t* hidle_add(hloop_t* loop, hidle_cb cb, uint32_t repeat) {
     hidle_t* idle = (hidle_t*)malloc(sizeof(hidle_t));
     memset(idle, 0, sizeof(hidle_t));
     idle->event_type = HEVENT_TYPE_IDLE;
+    idle->priority = HEVENT_LOWEST_PRIORITY;
     idle->repeat = repeat;
     list_add(&idle->node, &loop->idles);
     EVENT_ADD(loop, idle, cb);
+    loop->nidles++;
     return idle;
 }
 
 void hidle_del(hidle_t* idle) {
+    if (idle->destroy) return;
+    idle->loop->nidles--;
     EVENT_DEL(idle);
 }
 
 htimer_t* htimer_add(hloop_t* loop, htimer_cb cb, uint64_t timeout, uint32_t repeat) {
-    htimer_t* timer = (htimer_t*)malloc(sizeof(htimer_t));
-    memset(timer, 0, sizeof(htimer_t));
-    timer->event_type = HEVENT_TYPE_TIMER;
+    if (timeout == 0)   return NULL;
+    htimeout_t* timer = (htimeout_t*)malloc(sizeof(htimeout_t));
+    memset(timer, 0, sizeof(htimeout_t));
+    timer->event_type = HEVENT_TYPE_TIMEOUT;
+    timer->priority = HEVENT_HIGHEST_PRIORITY;
     timer->repeat = repeat;
     timer->timeout = timeout;
-    timer->next_timeout = gethrtime() + timeout*1000;
-    list_add(&timer->node, &loop->timers);
-    heap_insert(&loop->timer_minheap, &timer->hnode);
+    hloop_update_time(loop);
+    timer->next_timeout = hloop_now_hrtime(loop) + timeout*1000;
+    heap_insert(&loop->timers, &timer->node);
     EVENT_ADD(loop, timer, cb);
-    return timer;
+    loop->ntimers++;
+    return (htimer_t*)timer;
+}
+
+htimer_t* htimer_add_period(hloop_t* loop, htimer_cb cb,
+                int8_t minute,  int8_t hour, int8_t day,
+                int8_t week, int8_t month, uint32_t repeat) {
+    if (minute > 59 || hour > 23 || day > 31 || week > 6 || month > 12) {
+        return NULL;
+    }
+    hperiod_t* timer = (hperiod_t*)malloc(sizeof(hperiod_t));
+    memset(timer, 0, sizeof(hperiod_t));
+    timer->event_type = HEVENT_TYPE_PERIOD;
+    timer->priority = HEVENT_HIGH_PRIORITY;
+    timer->repeat = repeat;
+    timer->minute = minute;
+    timer->hour   = hour;
+    timer->day    = day;
+    timer->month  = month;
+    timer->week   = week;
+    timer->next_timeout = calc_next_timeout(minute, hour, day, week, month) * 1e6;
+    heap_insert(&loop->timers, &timer->node);
+    EVENT_ADD(loop, timer, cb);
+    loop->ntimers++;
+    return (htimer_t*)timer;
 }
 
 void htimer_del(htimer_t* timer) {
-    heap_remove(&timer->loop->timer_minheap, &timer->hnode);
+    if (timer->destroy) return;
+    timer->loop->ntimers--;
     EVENT_DEL(timer);
 }
 
@@ -263,7 +299,7 @@ void hio_init(hio_t* io) {
     io->event_index[0] = io->event_index[1] = -1;
     // move to hwrite
     //write_queue_init(&io->write_queue, 4);;
-};
+}
 
 void hio_cleanup(hio_t* io) {
     offset_buf_t* pbuf = NULL;
@@ -295,6 +331,7 @@ hio_t* hio_add(hloop_t* loop, hio_cb cb, int fd, int events) {
     if (!io->active || io->destroy) {
         hio_init(io);
         EVENT_ADD(loop, io, cb);
+        loop->nios++;
     }
 
     io->fd = fd;
@@ -307,9 +344,11 @@ hio_t* hio_add(hloop_t* loop, hio_cb cb, int fd, int events) {
 }
 
 void hio_del(hio_t* io, int events) {
+    if (io->destroy) return;
     iowatcher_del_event(io->loop, io->fd, events);
     io->events &= ~events;
     if (io->events == 0) {
+        io->loop->nios--;
         hio_cleanup(io);
         EVENT_DEL(io);
     }

+ 55 - 22
event/hloop.h

@@ -15,9 +15,11 @@ BEGIN_EXTERN_C
 typedef struct hloop_s  hloop_t;
 typedef struct hevent_s hevent_t;
 
-typedef struct hidle_s  hidle_t;
-typedef struct htimer_s htimer_t;
-typedef struct hio_s    hio_t;
+typedef struct hidle_s      hidle_t;
+typedef struct htimer_s     htimer_t;
+typedef struct htimeout_s   htimeout_t;
+typedef struct hperiod_s    hperiod_t;
+typedef struct hio_s        hio_t;
 
 typedef void (*hevent_cb)   (hevent_t* ev);
 typedef void (*hidle_cb)    (hidle_t* idle);
@@ -32,16 +34,18 @@ typedef void (*hclose_cb)   (hio_t* io);
 
 typedef enum {
     HEVENT_TYPE_NONE    = 0,
-    HEVENT_TYPE_IDLE    = 0x0001,
-    HEVENT_TYPE_TIMER   = 0x0002,
-    HEVENT_TYPE_IO      = 0x0004,
+    HEVENT_TYPE_IDLE    = 0x00000010,
+    HEVENT_TYPE_TIMEOUT = 0x00000100,
+    HEVENT_TYPE_PERIOD  = 0x00000200,
+    HEVENT_TYPE_TIMER   = HEVENT_TYPE_TIMEOUT|HEVENT_TYPE_PERIOD,
+    HEVENT_TYPE_IO      = 0x00001000,
 } hevent_type_e;
 
-#define HEVENT_LOWEST_PRIORITY     -10
-#define HEVENT_LOW_PRIORITY        -5
+#define HEVENT_LOWEST_PRIORITY     -5
+#define HEVENT_LOW_PRIORITY        -3
 #define HEVENT_NORMAL_PRIORITY      0
-#define HEVENT_HIGH_PRIORITY        5
-#define HEVENT_HIGHEST_PRIORITY     10
+#define HEVENT_HIGH_PRIORITY        3
+#define HEVENT_HIGHEST_PRIORITY     5
 #define HEVENT_PRIORITY_SIZE  (HEVENT_HIGHEST_PRIORITY-HEVENT_LOWEST_PRIORITY+1)
 #define HEVENT_PRIORITY_INDEX(priority) (priority-HEVENT_LOWEST_PRIORITY)
 
@@ -71,14 +75,28 @@ struct hidle_s {
     struct list_node node;
 };
 
+#define HTIMER_FIELDS                   \
+    HEVENT_FIELDS                       \
+    uint32_t    repeat;                 \
+    uint64_t    next_timeout;           \
+    struct heap_node node;
+
 struct htimer_s {
-    HEVENT_FIELDS
-    uint32_t    repeat;
-    uint32_t    timeout;
-//private:
-    uint64_t    next_timeout;
-    struct list_node node;
-    struct heap_node hnode;
+    HTIMER_FIELDS
+};
+
+struct htimeout_s {
+    HTIMER_FIELDS
+    uint32_t    timeout;                \
+};
+
+struct hperiod_s {
+    HTIMER_FIELDS
+    int8_t      minute;
+    int8_t      hour;
+    int8_t      day;
+    int8_t      week;
+    int8_t      month;
 };
 
 QUEUE_DECL(offset_buf_t, write_queue);
@@ -124,20 +142,19 @@ struct hloop_s {
 //private:
     // events
     uint64_t                    event_counter;
-    uint32_t                    nevents;
     uint32_t                    nactives;
     uint32_t                    npendings;
     // pendings: with priority as array.index
     hevent_t*                   pendings[HEVENT_PRIORITY_SIZE];
     // idles
     struct list_head            idles;
-    uint64_t                    idle_time;
-    uint64_t                    last_idle_hrtime;
+    uint32_t                    nidles;
     // timers
-    struct list_head            timers;
-    struct heap                 timer_minheap;
+    struct heap                 timers;
+    uint32_t                    ntimers;
     // ios: with fd as array.index
     struct io_array             ios;
+    uint32_t                    nios;
     void*                       iowatcher;
 };
 
@@ -157,6 +174,10 @@ static inline time_t hloop_now(hloop_t* loop) {
     return loop->start_time + (loop->cur_hrtime - loop->start_hrtime) / 1000000;
 }
 
+static inline uint64_t hloop_now_hrtime(hloop_t* loop) {
+    return loop->start_time*1e6 + (loop->cur_hrtime - loop->start_hrtime);
+}
+
 // idle
 hidle_t*    hidle_add(hloop_t* loop, hidle_cb cb, uint32_t repeat DEFAULT(INFINITE));
 void        hidle_del(hidle_t* idle);
@@ -164,6 +185,18 @@ void        hidle_del(hidle_t* idle);
 // timer
 // @param timeout: unit(ms)
 htimer_t*   htimer_add(hloop_t* loop, htimer_cb cb, uint64_t timeout, uint32_t repeat DEFAULT(INFINITE));
+/*
+ * minute   hour    day     week    month       cb
+ * 0~59     0~23    1~31    0~6     1~12
+ *  30      -1      -1      -1      -1          cron.hourly
+ *  30      1       -1      -1      -1          cron.daily
+ *  30      1       15      -1      -1          cron.monthly
+ *  30      1       -1       7      -1          cron.weekly
+ *  30      1        1      -1      10          cron.yearly
+ */
+htimer_t*   htimer_add_period(hloop_t* loop, htimer_cb cb,
+                int8_t minute DEFAULT(0),  int8_t hour  DEFAULT(-1), int8_t day DEFAULT(-1),
+                int8_t week   DEFAULT(-1), int8_t month DEFAULT(-1), uint32_t repeat DEFAULT(INFINITE));
 void        htimer_del(htimer_t* timer);
 
 // io

+ 8 - 0
examples/loop.c

@@ -9,6 +9,12 @@ void on_timer(htimer_t* timer) {
         timer->event_id, timer->priority, (long)timer->userdata, hloop_now(timer->loop), timer->loop->cur_hrtime);
 }
 
+void cron_hourly(htimer_t* timer) {
+    time_t tt;
+    time(&tt);
+    printf("cron_hourly: %s\n", ctime(&tt));
+}
+
 int main() {
     hloop_t loop;
     hloop_init(&loop);
@@ -20,6 +26,8 @@ int main() {
         htimer_t* timer = htimer_add(&loop, on_timer, i*1000, i);
         timer->userdata = (void*)i;
     }
+    int minute = time(NULL)%3600/60;
+    htimer_add_period(&loop, cron_hourly, minute+1, -1, -1, -1, -1, INFINITE);
     hloop_run(&loop);
     return 0;
 }