diff mbox

[v2,1/2] Timer implementation first draft

Message ID 1394624339-6724-1-git-send-email-petri.savolainen@linaro.org
State Accepted, archived
Headers show

Commit Message

Petri Savolainen March 12, 2014, 11:38 a.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

Anders Roxell March 13, 2014, 9:03 a.m. UTC | #1
On 2014-03-12 13:38, Petri Savolainen 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..0b6dc51 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 odp_buffer_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..4bcc479 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 tmo_buf;
> +}
> +
> +
> +
> +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/1394624339-6724-1-git-send-email-petri.savolainen%40linaro.org.
> For more options, visit https://groups.google.com/a/linaro.org/d/optout.

Patch does not apply can't find files include/odp_timer.h and
platform/linux-generic/source/odp_timer.c

$ git am ~/incoming/lng-odp_PATCH_v2_1-2_Timer_implementation_first_draft.mbox
Applying: Timer implementation first draft
error: include/odp_timer.h: does not exist in index
error: platform/linux-generic/source/odp_timer.c: does not exist in index
Patch failed at 0001 Timer implementation first draft
The copy of the patch that failed is found in:
   /home/anders/src/odp/.git/rebase-apply/patch
When you have resolved this problem, run "git am --resolved".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".
Petri Savolainen March 13, 2014, 10:16 a.m. UTC | #2
Hi,

Patch does not apply can't find files include/odp_timer.h and 
> platform/linux-generic/source/odp_timer.c 
>
>
 You need to apply the timer API patch first (
0001-Timer-API-first-draft.patch).

-Petri
Santosh Shukla March 13, 2014, 1:31 p.m. UTC | #3
Patch set merged in to odp.git repository.

0001-Packet-IO-test-core-count-option.patch
0001-Timer-API-first-draft.patch
0001-Timer-implementation-first-draft.patch
0002-Add-STD_LIBS-into-test-app-makefiles.patch

On 13 March 2014 15:46, Petri Savolainen <petri.savolainen@linaro.org> wrote:
> Hi,
>
>
>> Patch does not apply can't find files include/odp_timer.h and
>> platform/linux-generic/source/odp_timer.c
>>
>
>  You need to apply the timer API patch first
> (0001-Timer-API-first-draft.patch).
>
> -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/bebf0671-7fbf-4f7a-a137-f555d5645f48%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..0b6dc51 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 odp_buffer_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..4bcc479 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 tmo_buf;
+}
+
+
+
+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.
 	 */