diff mbox

Timer implementation first draft

Message ID 1394547100-30886-1-git-send-email-petri.savolainen@linaro.org
State Accepted, archived
Commit e6870ef41925e220812d0d6a1a2c036eaf154fa3
Headers show

Commit Message

Petri Savolainen March 11, 2014, 2:11 p.m. UTC
Very simple implementation. Does not implement cancel_tmo. Test included into example app.

Signed-off-by: Petri Savolainen <petri.savolainen@linaro.org>
---
 include/odp.h                             |   1 +
 include/odp_timer.h                       |   2 +-
 platform/linux-generic/source/odp_timer.c | 291 ++++++++++++++++++++++++++++++
 test/example/odp_example.c                |  68 +++++++
 4 files changed, 361 insertions(+), 1 deletion(-)

Comments

Petri Savolainen March 11, 2014, 2:19 p.m. UTC | #1
Hi,

Here's starting point for linux-generic timer implementation. It works 
already, although cancel_tmo is missing. Also it would need a purpose built 
test application (now testing #ifdef in example app), better testing and 
maybe also multicore optimization. Anyway, it should demonstrate an 
implementation of the API.

It has couple of volatiles. Both are set in single thread and read in 
another/multiple others.

-Petri
Santosh Shukla March 11, 2014, 2:26 p.m. UTC | #2
Patch doesn't apply on top of latest odp commit
2da43ab537b39578c0a07af842d356a13930a7d3.. have you rebased with
latest commit?

On 11 March 2014 19:41, Petri Savolainen <petri.savolainen@linaro.org> wrote:
> Very simple implementation. Does not implement cancel_tmo. Test included into example app.
>
> Signed-off-by: Petri Savolainen <petri.savolainen@linaro.org>
> ---
>  include/odp.h                             |   1 +
>  include/odp_timer.h                       |   2 +-
>  platform/linux-generic/source/odp_timer.c | 291 ++++++++++++++++++++++++++++++
>  test/example/odp_example.c                |  68 +++++++
>  4 files changed, 361 insertions(+), 1 deletion(-)
>
> diff --git a/include/odp.h b/include/odp.h
> index 6a52346..9bb68a2 100644
> --- a/include/odp.h
> +++ b/include/odp.h
> @@ -292,6 +292,7 @@ extern "C" {
>  #include <odp_queue.h>
>  #include <odp_ticketlock.h>
>  #include <odp_time.h>
> +#include <odp_timer.h>
>  #include <odp_schedule.h>
>  #include <odp_sync.h>
>  #include <odp_packet.h>
> diff --git a/include/odp_timer.h b/include/odp_timer.h
> index ff54b8e..f051f71 100644
> --- a/include/odp_timer.h
> +++ b/include/odp_timer.h
> @@ -35,7 +35,7 @@ typedef uint32_t odp_timer_t;
>  /**
>  * ODP timeout handle
>  */
> -typedef uint32_t odp_timer_tmo_t;
> +typedef uint64_t odp_timer_tmo_t;
>
>  #define ODP_TIMER_TMO_INVALID 0
>
> diff --git a/platform/linux-generic/source/odp_timer.c b/platform/linux-generic/source/odp_timer.c
> index b090257..fceb086 100644
> --- a/platform/linux-generic/source/odp_timer.c
> +++ b/platform/linux-generic/source/odp_timer.c
> @@ -6,9 +6,300 @@
>
>  #include <odp_timer.h>
>  #include <odp_internal.h>
> +#include <odp_atomic.h>
> +#include <odp_spinlock.h>
> +#include <odp_sync.h>
> +#include <odp_debug.h>
> +
> +#include <signal.h>
> +#include <time.h>
> +
> +#include <string.h>
> +
> +
> +#define NUM_TIMERS    1
> +#define MAX_TICKS     1024
> +#define RESOLUTION_NS 1000000
> +
> +struct timeout_t;
> +
> +typedef struct timeout_t {
> +       struct timeout_t *next;
> +       int               timer_id;
> +       int               tick;
> +       uint64_t          tmo_tick;
> +       odp_queue_t       queue;
> +       odp_buffer_t      buf;
> +       odp_buffer_t      tmo_buf;
> +} timeout_t;
> +
> +typedef struct {
> +       odp_spinlock_t lock;
> +       timeout_t      *list;
> +} tick_t;
> +
> +typedef struct {
> +       volatile int      active;
> +       volatile uint64_t cur_tick;
> +       timer_t           timerid;
> +       odp_buffer_pool_t pool;
> +       uint64_t          resolution_ns;
> +       uint64_t          max_ticks;
> +       tick_t            tick[MAX_TICKS];
> +
> +} timer_ring_t;
> +
> +
> +typedef struct {
> +       timer_ring_t     timer[NUM_TIMERS];
> +       odp_atomic_int_t num_timers;
> +} timer_global_t;
> +
> +
> +
> +/* Global */
> +timer_global_t odp_timer;
> +
> +
> +static void add_tmo(tick_t *tick, timeout_t *tmo)
> +{
> +       odp_spinlock_lock(&tick->lock);
> +
> +       tmo->next  = tick->list;
> +       tick->list = tmo;
> +
> +       odp_spinlock_unlock(&tick->lock);
> +}
> +
> +
> +static timeout_t *rem_tmo(tick_t *tick)
> +{
> +       timeout_t *tmo;
> +
> +       odp_spinlock_lock(&tick->lock);
> +
> +       tmo = tick->list;
> +
> +       if (tmo)
> +               tick->list = tmo->next;
> +
> +       odp_spinlock_unlock(&tick->lock);
> +
> +       if (tmo)
> +               tmo->next = NULL;
> +
> +       return tmo;
> +}
> +
> +
> +
> +static void notify_function(union sigval sigval)
> +{
> +       (void) sigval;
> +       uint64_t cur_tick;
> +       timeout_t *tmo;
> +       tick_t *tick;
> +
> +       if (odp_timer.timer[0].active == 0)
> +               return;
> +
> +       /* ODP_DBG("Tick\n"); */
> +
> +       cur_tick = odp_timer.timer[0].cur_tick++;
> +
> +       tick = &odp_timer.timer[0].tick[cur_tick % MAX_TICKS];
> +
> +       while ((tmo = rem_tmo(tick)) != NULL) {
> +               odp_queue_t  queue;
> +               odp_buffer_t buf;
> +
> +               queue = tmo->queue;
> +               buf   = tmo->buf;
> +
> +               if (buf != tmo->tmo_buf)
> +                       odp_buffer_free(tmo->tmo_buf);
> +
> +               odp_queue_enq(queue, buf);
> +       }
> +}
> +
> +
> +static void timer_init(void)
> +{
> +       struct sigevent   sigev;
> +       struct itimerspec ispec;
> +
> +       ODP_DBG("Timer thread starts\n");
> +
> +       memset(&sigev, 0, sizeof(sigev));
> +       memset(&ispec, 0, sizeof(ispec));
> +
> +       sigev.sigev_notify          = SIGEV_THREAD;
> +       sigev.sigev_notify_function = notify_function;
> +
> +       if (timer_create(CLOCK_MONOTONIC, &sigev,
> +                        &odp_timer.timer[0].timerid)) {
> +               ODP_DBG("Timer create failed\n");
> +               return;
> +       }
> +
> +       ispec.it_interval.tv_sec  = 0;
> +       ispec.it_interval.tv_nsec = RESOLUTION_NS;
> +       ispec.it_value.tv_sec     = 0;
> +       ispec.it_value.tv_nsec    = RESOLUTION_NS;
> +
> +       if (timer_settime(odp_timer.timer[0].timerid, 0, &ispec, NULL)) {
> +               ODP_DBG("Timer set failed\n");
> +               return;
> +       }
> +
> +       return;
> +}
>
>
>  int odp_timer_init_global(void)
>  {
> +       int i;
> +
> +       memset(&odp_timer, 0, sizeof(timer_global_t));
> +
> +       for (i = 0; i < MAX_TICKS; i++)
> +               odp_spinlock_init(&odp_timer.timer[0].tick[i].lock);
> +
> +       timer_init();
> +
> +
>         return 0;
>  }
> +
> +
> +
> +odp_timer_t odp_timer_create(const char *name, odp_buffer_pool_t pool,
> +                            uint64_t resolution, uint64_t min_tmo,
> +                            uint64_t max_tmo)
> +{
> +       int id;
> +       (void) name; (void) resolution; (void) min_tmo; (void) max_tmo;
> +
> +       if (odp_timer.num_timers >= NUM_TIMERS)
> +               return ODP_TIMER_INVALID;
> +
> +       id = odp_atomic_fetch_inc_int(&odp_timer.num_timers);
> +
> +       if (id >= NUM_TIMERS)
> +               return ODP_TIMER_INVALID;
> +
> +       odp_timer.timer[id].pool          = pool;
> +       odp_timer.timer[id].resolution_ns = RESOLUTION_NS;
> +       odp_timer.timer[id].max_ticks     = MAX_TICKS;
> +
> +       odp_sync_stores();
> +
> +       odp_timer.timer[id].active = 1;
> +
> +       return id + 1;
> +}
> +
> +
> +odp_timer_tmo_t odp_timer_absolute_tmo(odp_timer_t timer, uint64_t tmo_tick,
> +                                      odp_queue_t queue, odp_buffer_t buf)
> +{
> +       int id;
> +       uint64_t tick;
> +       uint64_t cur_tick;
> +       timeout_t *new_tmo;
> +       odp_buffer_t tmo_buf;
> +
> +       id = timer - 1;
> +
> +       cur_tick = odp_timer.timer[id].cur_tick;
> +
> +       if (tmo_tick <= cur_tick) {
> +               ODP_DBG("timeout too close\n");
> +               return ODP_TIMER_TMO_INVALID;
> +       }
> +
> +       tick = tmo_tick - cur_tick;
> +
> +       if (tick > MAX_TICKS) {
> +               ODP_DBG("timeout too far\n");
> +               return ODP_TIMER_TMO_INVALID;
> +       }
> +
> +       tick = (cur_tick + tick) % MAX_TICKS;
> +
> +       tmo_buf = odp_buffer_alloc(odp_timer.timer[id].pool);
> +
> +       if (tmo_buf == ODP_BUFFER_INVALID) {
> +               ODP_DBG("alloc failed\n");
> +               return ODP_TIMER_TMO_INVALID;
> +       }
> +
> +       new_tmo = (timeout_t *)odp_buffer_addr(tmo_buf);
> +
> +       new_tmo->timer_id = id;
> +       new_tmo->tick     = (int)tick;
> +       new_tmo->tmo_tick = tmo_tick;
> +       new_tmo->queue    = queue;
> +       new_tmo->tmo_buf  = tmo_buf;
> +
> +       if (buf != ODP_BUFFER_INVALID)
> +               new_tmo->buf = buf;
> +       else
> +               new_tmo->buf = tmo_buf;
> +
> +       add_tmo(&odp_timer.timer[id].tick[tick], new_tmo);
> +
> +       return (odp_timer_tmo_t) new_tmo;
> +}
> +
> +
> +
> +uint64_t odp_timer_tick_to_ns(odp_timer_t timer, uint64_t ticks)
> +{
> +       int id;
> +
> +       id = timer - 1;
> +
> +       return ticks * odp_timer.timer[id].resolution_ns;
> +}
> +
> +
> +uint64_t odp_timer_ns_to_tick(odp_timer_t timer, uint64_t ns)
> +{
> +       int id;
> +
> +       id = timer - 1;
> +
> +       return ns / odp_timer.timer[id].resolution_ns;
> +}
> +
> +
> +uint64_t odp_timer_resolution(odp_timer_t timer)
> +{
> +       int id;
> +
> +       id = timer - 1;
> +
> +       return odp_timer.timer[id].resolution_ns;
> +}
> +
> +
> +uint64_t odp_timer_maximum_tmo(odp_timer_t timer)
> +{
> +       int id;
> +
> +       id = timer - 1;
> +
> +       return odp_timer.timer[id].max_ticks;
> +}
> +
> +
> +uint64_t odp_timer_current_tick(odp_timer_t timer)
> +{
> +       int id;
> +
> +       id = timer - 1;
> +
> +       return odp_timer.timer[id].cur_tick;
> +}
> diff --git a/test/example/odp_example.c b/test/example/odp_example.c
> index 0f421a3..4764657 100644
> --- a/test/example/odp_example.c
> +++ b/test/example/odp_example.c
> @@ -53,6 +53,11 @@ typedef struct {
>
>  static odp_barrier_t test_barrier;
>
> +/* #define TEST_TIMEOUTS */
> +#ifdef TEST_TIMEOUTS
> +static odp_timer_t test_timer;
> +#endif
> +
>  /*
>   * Clear all scheduled queues. Retry to be sure that all
>   * buffers have been scheduled.
> @@ -71,6 +76,48 @@ static void clear_sched_queues(void)
>         }
>  }
>
> +#ifdef TEST_TIMEOUTS
> +static void test_timeouts(int thr)
> +{
> +       uint64_t tick;
> +       odp_queue_t queue;
> +       odp_buffer_t buf;
> +       int num = 10;
> +
> +       ODP_DBG("  [%i] test_timeouts\n", thr);
> +
> +       queue = odp_queue_lookup("timer_queue");
> +
> +       tick = odp_timer_current_tick(test_timer);
> +
> +       ODP_DBG("  [%i] current tick %"PRIu64"\n", thr, tick);
> +
> +       tick += 100;
> +
> +       odp_timer_absolute_tmo(test_timer, tick,
> +                              queue, ODP_BUFFER_INVALID);
> +
> +
> +       while (1) {
> +               while ((buf = odp_queue_deq(queue) == ODP_BUFFER_INVALID))
> +                       ;
> +
> +               /* ODP_DBG("  [%i] timeout\n", thr); */
> +
> +               odp_buffer_free(buf);
> +
> +               num--;
> +
> +               if (num == 0)
> +                       break;
> +
> +               tick = odp_timer_current_tick(test_timer) + 100;
> +
> +               odp_timer_absolute_tmo(test_timer, tick,
> +                                      queue, ODP_BUFFER_INVALID);
> +       }
> +}
> +#endif
>
>  /*
>   * Test single buffer alloc and free
> @@ -522,6 +569,12 @@ static void *run_thread(void *arg)
>                                      ODP_SCHED_PRIO_LOWEST))
>                 return NULL;
>
> +#ifdef TEST_TIMEOUTS
> +       odp_barrier_sync(&test_barrier);
> +
> +       test_timeouts(thr);
> +#endif
> +
>         printf("Thread %i exits\n", thr);
>         fflush(NULL);
>         return arg;
> @@ -724,6 +777,21 @@ int main(int argc, char *argv[])
>         }
>
>
> +#ifdef TEST_TIMEOUTS
> +       /*
> +        * Create a queue for timer test
> +        */
> +       queue = odp_queue_create("timer_queue", ODP_QUEUE_TYPE_SCHED, NULL);
> +
> +       if (queue == ODP_QUEUE_INVALID) {
> +               ODP_ERR("Timer queue create failed.\n");
> +               return -1;
> +       }
> +
> +       test_timer = odp_timer_create("test_timer", pool,
> +                                     1000000, 1000000, 1000000000000);
> +#endif
> +
>         /*
>          * Create queues for schedule test. QUEUES_PER_PRIO per priority.
>          */
> --
> 1.8.5.3
>
> --
> You received this message because you are subscribed to the Google Groups "LNG ODP Sub-team - lng-odp@linaro.org" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to lng-odp+unsubscribe@linaro.org.
> To post to this group, send email to lng-odp@linaro.org.
> Visit this group at http://groups.google.com/a/linaro.org/group/lng-odp/.
> To view this discussion on the web visit https://groups.google.com/a/linaro.org/d/msgid/lng-odp/1394547100-30886-1-git-send-email-petri.savolainen%40linaro.org.
> For more options, visit https://groups.google.com/a/linaro.org/d/optout.
Petri Savolainen March 11, 2014, 2:29 p.m. UTC | #3
On Tuesday, 11 March 2014 16:26:00 UTC+2, Santosh Shukla wrote:
>
> Patch doesn't apply on top of latest odp commit 
> 2da43ab537b39578c0a07af842d356a13930a7d3.. have you rebased with 
> latest commit? 
>
>
Forgot to mention that you need to apply my timer API patch first.

-Petri
Santosh Shukla March 11, 2014, 2:34 p.m. UTC | #4
On 11 March 2014 19:59, Petri Savolainen <petri.savolainen@linaro.org> wrote:
>
>
> On Tuesday, 11 March 2014 16:26:00 UTC+2, Santosh Shukla wrote:
>>
>> Patch doesn't apply on top of latest odp commit
>> 2da43ab537b39578c0a07af842d356a13930a7d3.. have you rebased with
>> latest commit?
>>
>
> Forgot to mention that you need to apply my timer API patch first.
>

Also, perhaps patch misses some "-lrt" inclusion, without this I got
build failure..

> -Petri
>
> --
> You received this message because you are subscribed to the Google Groups
> "LNG ODP Sub-team - lng-odp@linaro.org" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to lng-odp+unsubscribe@linaro.org.
> To post to this group, send email to lng-odp@linaro.org.
> Visit this group at http://groups.google.com/a/linaro.org/group/lng-odp/.
> To view this discussion on the web visit
> https://groups.google.com/a/linaro.org/d/msgid/lng-odp/1ec1bf3a-e897-4534-b2f6-9247edb2de4a%40linaro.org.
>
> For more options, visit https://groups.google.com/a/linaro.org/d/optout.
Petri Savolainen March 11, 2014, 2:44 p.m. UTC | #5
Also, perhaps patch misses some "-lrt" inclusion, without this I got 
> build failure.. 
>
>
>
I think "-lrt" should be in makefiles already. Did you apply those two 
patches on top of the latest code in git?

-Petri
Santosh Shukla March 11, 2014, 4:10 p.m. UTC | #6
On 11 March 2014 20:14, Petri Savolainen <petri.savolainen@linaro.org> wrote:
>
>
>> Also, perhaps patch misses some "-lrt" inclusion, without this I got
>> build failure..
>>
>>
>
> I think "-lrt" should be in makefiles already. Did you apply those two
> patches on top of the latest code in git?
>

No, I guess its missing..By adding this change in file "b/test/Makefile.inc"

+ODP_LIB = $(DESTDIR)/lib/libodp.a $(STD_LIBS)

It able to build.

> -Petri
>
>
> --
> You received this message because you are subscribed to the Google Groups
> "LNG ODP Sub-team - lng-odp@linaro.org" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to lng-odp+unsubscribe@linaro.org.
> To post to this group, send email to lng-odp@linaro.org.
> Visit this group at http://groups.google.com/a/linaro.org/group/lng-odp/.
> To view this discussion on the web visit
> https://groups.google.com/a/linaro.org/d/msgid/lng-odp/3b390bff-121c-432f-b5ad-4e397070e706%40linaro.org.
>
> For more options, visit https://groups.google.com/a/linaro.org/d/optout.
diff mbox

Patch

diff --git a/include/odp.h b/include/odp.h
index 6a52346..9bb68a2 100644
--- a/include/odp.h
+++ b/include/odp.h
@@ -292,6 +292,7 @@  extern "C" {
 #include <odp_queue.h>
 #include <odp_ticketlock.h>
 #include <odp_time.h>
+#include <odp_timer.h>
 #include <odp_schedule.h>
 #include <odp_sync.h>
 #include <odp_packet.h>
diff --git a/include/odp_timer.h b/include/odp_timer.h
index ff54b8e..f051f71 100644
--- a/include/odp_timer.h
+++ b/include/odp_timer.h
@@ -35,7 +35,7 @@  typedef uint32_t odp_timer_t;
 /**
 * ODP timeout handle
 */
-typedef uint32_t odp_timer_tmo_t;
+typedef uint64_t odp_timer_tmo_t;
 
 #define ODP_TIMER_TMO_INVALID 0
 
diff --git a/platform/linux-generic/source/odp_timer.c b/platform/linux-generic/source/odp_timer.c
index b090257..fceb086 100644
--- a/platform/linux-generic/source/odp_timer.c
+++ b/platform/linux-generic/source/odp_timer.c
@@ -6,9 +6,300 @@ 
 
 #include <odp_timer.h>
 #include <odp_internal.h>
+#include <odp_atomic.h>
+#include <odp_spinlock.h>
+#include <odp_sync.h>
+#include <odp_debug.h>
+
+#include <signal.h>
+#include <time.h>
+
+#include <string.h>
+
+
+#define NUM_TIMERS    1
+#define MAX_TICKS     1024
+#define RESOLUTION_NS 1000000
+
+struct timeout_t;
+
+typedef struct timeout_t {
+	struct timeout_t *next;
+	int               timer_id;
+	int               tick;
+	uint64_t          tmo_tick;
+	odp_queue_t       queue;
+	odp_buffer_t      buf;
+	odp_buffer_t      tmo_buf;
+} timeout_t;
+
+typedef struct {
+	odp_spinlock_t lock;
+	timeout_t      *list;
+} tick_t;
+
+typedef struct {
+	volatile int      active;
+	volatile uint64_t cur_tick;
+	timer_t           timerid;
+	odp_buffer_pool_t pool;
+	uint64_t          resolution_ns;
+	uint64_t          max_ticks;
+	tick_t            tick[MAX_TICKS];
+
+} timer_ring_t;
+
+
+typedef struct {
+	timer_ring_t     timer[NUM_TIMERS];
+	odp_atomic_int_t num_timers;
+} timer_global_t;
+
+
+
+/* Global */
+timer_global_t odp_timer;
+
+
+static void add_tmo(tick_t *tick, timeout_t *tmo)
+{
+	odp_spinlock_lock(&tick->lock);
+
+	tmo->next  = tick->list;
+	tick->list = tmo;
+
+	odp_spinlock_unlock(&tick->lock);
+}
+
+
+static timeout_t *rem_tmo(tick_t *tick)
+{
+	timeout_t *tmo;
+
+	odp_spinlock_lock(&tick->lock);
+
+	tmo = tick->list;
+
+	if (tmo)
+		tick->list = tmo->next;
+
+	odp_spinlock_unlock(&tick->lock);
+
+	if (tmo)
+		tmo->next = NULL;
+
+	return tmo;
+}
+
+
+
+static void notify_function(union sigval sigval)
+{
+	(void) sigval;
+	uint64_t cur_tick;
+	timeout_t *tmo;
+	tick_t *tick;
+
+	if (odp_timer.timer[0].active == 0)
+		return;
+
+	/* ODP_DBG("Tick\n"); */
+
+	cur_tick = odp_timer.timer[0].cur_tick++;
+
+	tick = &odp_timer.timer[0].tick[cur_tick % MAX_TICKS];
+
+	while ((tmo = rem_tmo(tick)) != NULL) {
+		odp_queue_t  queue;
+		odp_buffer_t buf;
+
+		queue = tmo->queue;
+		buf   = tmo->buf;
+
+		if (buf != tmo->tmo_buf)
+			odp_buffer_free(tmo->tmo_buf);
+
+		odp_queue_enq(queue, buf);
+	}
+}
+
+
+static void timer_init(void)
+{
+	struct sigevent   sigev;
+	struct itimerspec ispec;
+
+	ODP_DBG("Timer thread starts\n");
+
+	memset(&sigev, 0, sizeof(sigev));
+	memset(&ispec, 0, sizeof(ispec));
+
+	sigev.sigev_notify          = SIGEV_THREAD;
+	sigev.sigev_notify_function = notify_function;
+
+	if (timer_create(CLOCK_MONOTONIC, &sigev,
+			 &odp_timer.timer[0].timerid)) {
+		ODP_DBG("Timer create failed\n");
+		return;
+	}
+
+	ispec.it_interval.tv_sec  = 0;
+	ispec.it_interval.tv_nsec = RESOLUTION_NS;
+	ispec.it_value.tv_sec     = 0;
+	ispec.it_value.tv_nsec    = RESOLUTION_NS;
+
+	if (timer_settime(odp_timer.timer[0].timerid, 0, &ispec, NULL)) {
+		ODP_DBG("Timer set failed\n");
+		return;
+	}
+
+	return;
+}
 
 
 int odp_timer_init_global(void)
 {
+	int i;
+
+	memset(&odp_timer, 0, sizeof(timer_global_t));
+
+	for (i = 0; i < MAX_TICKS; i++)
+		odp_spinlock_init(&odp_timer.timer[0].tick[i].lock);
+
+	timer_init();
+
+
 	return 0;
 }
+
+
+
+odp_timer_t odp_timer_create(const char *name, odp_buffer_pool_t pool,
+			     uint64_t resolution, uint64_t min_tmo,
+			     uint64_t max_tmo)
+{
+	int id;
+	(void) name; (void) resolution; (void) min_tmo; (void) max_tmo;
+
+	if (odp_timer.num_timers >= NUM_TIMERS)
+		return ODP_TIMER_INVALID;
+
+	id = odp_atomic_fetch_inc_int(&odp_timer.num_timers);
+
+	if (id >= NUM_TIMERS)
+		return ODP_TIMER_INVALID;
+
+	odp_timer.timer[id].pool          = pool;
+	odp_timer.timer[id].resolution_ns = RESOLUTION_NS;
+	odp_timer.timer[id].max_ticks     = MAX_TICKS;
+
+	odp_sync_stores();
+
+	odp_timer.timer[id].active = 1;
+
+	return id + 1;
+}
+
+
+odp_timer_tmo_t odp_timer_absolute_tmo(odp_timer_t timer, uint64_t tmo_tick,
+				       odp_queue_t queue, odp_buffer_t buf)
+{
+	int id;
+	uint64_t tick;
+	uint64_t cur_tick;
+	timeout_t *new_tmo;
+	odp_buffer_t tmo_buf;
+
+	id = timer - 1;
+
+	cur_tick = odp_timer.timer[id].cur_tick;
+
+	if (tmo_tick <= cur_tick) {
+		ODP_DBG("timeout too close\n");
+		return ODP_TIMER_TMO_INVALID;
+	}
+
+	tick = tmo_tick - cur_tick;
+
+	if (tick > MAX_TICKS) {
+		ODP_DBG("timeout too far\n");
+		return ODP_TIMER_TMO_INVALID;
+	}
+
+	tick = (cur_tick + tick) % MAX_TICKS;
+
+	tmo_buf = odp_buffer_alloc(odp_timer.timer[id].pool);
+
+	if (tmo_buf == ODP_BUFFER_INVALID) {
+		ODP_DBG("alloc failed\n");
+		return ODP_TIMER_TMO_INVALID;
+	}
+
+	new_tmo = (timeout_t *)odp_buffer_addr(tmo_buf);
+
+	new_tmo->timer_id = id;
+	new_tmo->tick     = (int)tick;
+	new_tmo->tmo_tick = tmo_tick;
+	new_tmo->queue    = queue;
+	new_tmo->tmo_buf  = tmo_buf;
+
+	if (buf != ODP_BUFFER_INVALID)
+		new_tmo->buf = buf;
+	else
+		new_tmo->buf = tmo_buf;
+
+	add_tmo(&odp_timer.timer[id].tick[tick], new_tmo);
+
+	return (odp_timer_tmo_t) new_tmo;
+}
+
+
+
+uint64_t odp_timer_tick_to_ns(odp_timer_t timer, uint64_t ticks)
+{
+	int id;
+
+	id = timer - 1;
+
+	return ticks * odp_timer.timer[id].resolution_ns;
+}
+
+
+uint64_t odp_timer_ns_to_tick(odp_timer_t timer, uint64_t ns)
+{
+	int id;
+
+	id = timer - 1;
+
+	return ns / odp_timer.timer[id].resolution_ns;
+}
+
+
+uint64_t odp_timer_resolution(odp_timer_t timer)
+{
+	int id;
+
+	id = timer - 1;
+
+	return odp_timer.timer[id].resolution_ns;
+}
+
+
+uint64_t odp_timer_maximum_tmo(odp_timer_t timer)
+{
+	int id;
+
+	id = timer - 1;
+
+	return odp_timer.timer[id].max_ticks;
+}
+
+
+uint64_t odp_timer_current_tick(odp_timer_t timer)
+{
+	int id;
+
+	id = timer - 1;
+
+	return odp_timer.timer[id].cur_tick;
+}
diff --git a/test/example/odp_example.c b/test/example/odp_example.c
index 0f421a3..4764657 100644
--- a/test/example/odp_example.c
+++ b/test/example/odp_example.c
@@ -53,6 +53,11 @@  typedef struct {
 
 static odp_barrier_t test_barrier;
 
+/* #define TEST_TIMEOUTS */
+#ifdef TEST_TIMEOUTS
+static odp_timer_t test_timer;
+#endif
+
 /*
  * Clear all scheduled queues. Retry to be sure that all
  * buffers have been scheduled.
@@ -71,6 +76,48 @@  static void clear_sched_queues(void)
 	}
 }
 
+#ifdef TEST_TIMEOUTS
+static void test_timeouts(int thr)
+{
+	uint64_t tick;
+	odp_queue_t queue;
+	odp_buffer_t buf;
+	int num = 10;
+
+	ODP_DBG("  [%i] test_timeouts\n", thr);
+
+	queue = odp_queue_lookup("timer_queue");
+
+	tick = odp_timer_current_tick(test_timer);
+
+	ODP_DBG("  [%i] current tick %"PRIu64"\n", thr, tick);
+
+	tick += 100;
+
+	odp_timer_absolute_tmo(test_timer, tick,
+			       queue, ODP_BUFFER_INVALID);
+
+
+	while (1) {
+		while ((buf = odp_queue_deq(queue) == ODP_BUFFER_INVALID))
+			;
+
+		/* ODP_DBG("  [%i] timeout\n", thr); */
+
+		odp_buffer_free(buf);
+
+		num--;
+
+		if (num == 0)
+			break;
+
+		tick = odp_timer_current_tick(test_timer) + 100;
+
+		odp_timer_absolute_tmo(test_timer, tick,
+				       queue, ODP_BUFFER_INVALID);
+	}
+}
+#endif
 
 /*
  * Test single buffer alloc and free
@@ -522,6 +569,12 @@  static void *run_thread(void *arg)
 				     ODP_SCHED_PRIO_LOWEST))
 		return NULL;
 
+#ifdef TEST_TIMEOUTS
+	odp_barrier_sync(&test_barrier);
+
+	test_timeouts(thr);
+#endif
+
 	printf("Thread %i exits\n", thr);
 	fflush(NULL);
 	return arg;
@@ -724,6 +777,21 @@  int main(int argc, char *argv[])
 	}
 
 
+#ifdef TEST_TIMEOUTS
+	/*
+	 * Create a queue for timer test
+	 */
+	queue = odp_queue_create("timer_queue", ODP_QUEUE_TYPE_SCHED, NULL);
+
+	if (queue == ODP_QUEUE_INVALID) {
+		ODP_ERR("Timer queue create failed.\n");
+		return -1;
+	}
+
+	test_timer = odp_timer_create("test_timer", pool,
+				      1000000, 1000000, 1000000000000);
+#endif
+
 	/*
 	 * Create queues for schedule test. QUEUES_PER_PRIO per priority.
 	 */