diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7bbe0e7eeb8d113770d8e6dd3362600a96d24415..21036ed549c418224ba00d154058a6181bcf6a72 100755
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -92,7 +92,7 @@ endif()
 
 if(UNIX)
     add_subdirectory(src/unix)
-    target_link_libraries(${PROJECT_NAME} dap_core_unix)
+    target_link_libraries(${PROJECT_NAME} dap_core_unix rt)
 endif()
 
 if(DARWIN)
diff --git a/include/dap_common.h b/include/dap_common.h
index eb05d6f34ad08c5111c9ddd237420a6b06aa955f..50c7972928aa686f40eff5dc7968836f7905dd78 100755
--- a/include/dap_common.h
+++ b/include/dap_common.h
@@ -59,6 +59,10 @@ typedef uint8_t byte_t;
   #define FALSE false
 #endif
 
+#ifndef UNUSED
+  #define UNUSED(x) (void)(x)
+#endif
+
 #ifndef ROUNDUP
   #define ROUNDUP(n,width) (((n) + (width) - 1) & ~(unsigned)((width) - 1))
 #endif
@@ -229,6 +233,14 @@ typedef struct dap_log_history_str_s {
 
 } dap_log_history_str_t;
 
+#define DAP_INTERVAL_TIMERS_MAX 15
+
+typedef void (*dap_timer_callback_t)(void);
+typedef struct dap_timer_interface {
+    void *timer;
+    dap_timer_callback_t callback;
+} dap_timer_interface_t;
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -328,8 +340,6 @@ DAP_STATIC_INLINE void DAP_AtomicUnlock( dap_spinlock_t *lock )
     __sync_lock_release( lock );
 }
 
-
-
 extern char *g_sys_dir_path;
 
 //int dap_common_init( const char * a_log_file );
@@ -375,6 +385,8 @@ size_t dap_bin2hex(char *a_out, const void *a_in, size_t a_len);
 void dap_digit_from_string(const char *num_str, uint8_t *raw, size_t raw_len);
 void dap_digit_from_string2(const char *num_str, uint8_t *raw, size_t raw_len);
 
+void *dap_interval_timer_create(unsigned int a_msec, dap_timer_callback_t a_callback);
+int dap_interval_timer_delete(void *a_timer);
 
 #ifdef __MINGW32__
 int exec_silent(const char *a_cmd);
diff --git a/src/dap_common.c b/src/dap_common.c
index 847447a711b1b6b9452baff2f6bb528be64fb3e4..2ec2b9bf5d0e2dcdf340d8b97784103efd414611 100755
--- a/src/dap_common.c
+++ b/src/dap_common.c
@@ -41,6 +41,7 @@
 
   #include <pthread.h>
   #include <syslog.h>
+  #include <signal.h>
 
 #else // WIN32
 
@@ -92,7 +93,7 @@ static const char *s_log_level_tag[ 16 ] = {
 
 const char *s_ansi_seq_color[ 16 ] = {
 
-    "\x1b[0;37;40m",   // L_DEBUG     = 0 
+    "\x1b[0;37;40m",   // L_DEBUG     = 0
     "\x1b[1;32;40m",   // L_INFO      = 2,
     "\x1b[0;32;40m",   // L_NOTICE    = 1,
     "\x1b[1;33;40m",   // L_MESSAGE   = 3,
@@ -797,3 +798,120 @@ int exec_silent(const char * a_cmd) {
     return execl(".",a_cmd);
 #endif
 }
+
+static int s_timers_count = 0;
+static dap_timer_interface_t s_timers[DAP_INTERVAL_TIMERS_MAX];
+#ifdef _WIN32
+static CRITICAL_SECTION s_timers_lock;
+#else
+static pthread_mutex_t s_timers_lock;
+#endif
+
+static int s_timer_find(void *a_timer)
+{
+    for (int i = 0; i < s_timers_count; i++) {
+        if (s_timers[i].timer == a_timer) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+#ifdef _WIN32
+static void CALLBACK s_win_callback(PVOID a_arg, BOOLEAN a_always_true)
+{
+    UNUSED(a_always_true);
+    s_timers[(int)a_arg].callback();
+}
+#else
+static void s_posix_callback(union sigval a_arg)
+{
+    s_timers[a_arg.sival_int].callback();
+}
+#endif
+
+/*!
+ * \brief dap_interval_timer_create Create new timer object and set callback function to it
+ * \param a_msec Timer period
+ * \param a_callback Function to be called with timer period
+ * \return pointer to timer object if success, otherwise return NULL
+ */
+void *dap_interval_timer_create(unsigned int a_msec, dap_timer_callback_t a_callback)
+{
+    if (s_timers_count == DAP_INTERVAL_TIMERS_MAX) {
+        return NULL;
+    }
+#ifdef _WIN32
+    if (s_timers_count == 0) {
+        InitializeCriticalSection(&s_timers_lock);
+    }
+    HANDLE l_timer;
+    if (!CreateTimerQueueTimer(&l_timer, NULL, (WAITORTIMERCALLBACK)s_win_callback, (PVOID)s_timers_count, a_msec, a_msec, 0)) {
+        return NULL;
+    }
+    EnterCriticalSection(&s_timers_lock);
+#else
+    if (s_timers_count == 0) {
+        pthread_mutex_init(&s_timers_lock, NULL);
+    }
+    timer_t l_timer;
+    struct sigevent l_sig_event = { };
+    l_sig_event.sigev_notify = SIGEV_THREAD;
+    l_sig_event.sigev_value.sival_int = s_timers_count;
+    l_sig_event.sigev_notify_function = s_posix_callback;
+    if (timer_create(CLOCK_MONOTONIC, &l_sig_event, &l_timer)) {
+        return NULL;
+    }
+    struct itimerspec l_period = { };
+    l_period.it_interval.tv_sec = l_period.it_value.tv_sec = a_msec / 1000;
+    l_period.it_interval.tv_nsec = l_period.it_value.tv_nsec = (a_msec % 1000) * 1000000;
+    timer_settime(l_timer, 0, &l_period, NULL);
+    pthread_mutex_lock(&s_timers_lock);
+#endif
+    s_timers[s_timers_count].timer = (void *)l_timer;
+    s_timers[s_timers_count].callback = a_callback;
+    s_timers_count++;
+#ifdef _WIN32
+    LeaveCriticalSection(&s_timers_lock);
+#else
+    pthread_mutex_unlock(&s_timers_lock);
+#endif
+    return (void *)l_timer;
+}
+
+/*!
+ * \brief dap_interval_timer_delete Delete existed timer object and stop callback function calls
+ * \param a_timer A timer object created previously with dap_interval_timer_create
+ * \return 0 if success, -1 otherwise
+ */
+int dap_interval_timer_delete(void *a_timer)
+{
+    if (!s_timers_count) {
+        return -1;
+    }
+#ifdef _WIN32
+    EnterCriticalSection(&s_timers_lock);
+#else
+    pthread_mutex_lock(&s_timers_lock);
+#endif
+    int l_timer_idx = s_timer_find(a_timer);
+    if (l_timer_idx == -1) {
+        return -1;
+    }
+    for (int i = l_timer_idx; i < s_timers_count - 1; i++) {
+        s_timers[i] = s_timers[i + 1];
+    }
+    s_timers_count--;
+#ifdef _WIN32
+    LeaveCriticalSection(&s_timers_lock);
+    if (s_timers_count == 0) {
+        DeleteCriticalSection(&s_timers_lock);
+    }
+    return !DeleteTimerQueueTimer(NULL, (HANDLE)a_timer, NULL);
+#else
+    if (s_timers_count == 0) {
+        pthread_mutex_destroy(&s_timers_lock);
+    }
+    return timer_delete((timer_t)a_timer);
+#endif
+}