On 2014-09-04 01:01, Maxim Uvarov wrote: > On 09/03/2014 10:48 PM, Ola Liljedahl wrote: > >Any volunteers for reviewing this patch? > > > >-- Ola > > > > > Hello Ola, > > it will be really useful if it's possible to split this big patch. > At least for: > 1. timer api + test example > 2. priority queue > > priority queue also need some test case.
We should probably focus on writing tests for the public API before we start write tests for the internal implementation fuctions. Cheers, Anders > > Also we talked about your patch with Mike and Anders yesterday. You > provided example in doxygen. But it will > be great to move that example to source code. In that case we can > check that it always valid and works. > > A little bit confusing with C++ style comments "//" instead of C > style "/* .... */". But patchwork does not complain about > that. I think we should be consistent with comments too. > > Best regards, > Maxim. > > > > >On 2 September 2014 17:26, Ola Liljedahl <[email protected] > ><mailto:[email protected]>> wrote: > > > > Signed-off-by: Ola Liljedahl <[email protected] > > <mailto:[email protected]>> > > --- > > (This document/code contribution attached is provided under the > > terms of agreement LES-LTM-21309) > > New timer API and corresponding SW implementation for linux-generic. > > Read more about use cases and usage here: > > > > https://docs.google.com/a/linaro.org/document/d/1bfY_J8ecLJPsFTmYftb0NVmGnB9qkEc_NpcJ87yfaD8/edit# > > > > example/timer/odp_timer_test.c | 93 ++- > > platform/linux-generic/Makefile.am | 1 + > > platform/linux-generic/include/api/odp_timer.h | 478 ++++++++++-- > > .../linux-generic/include/odp_timer_internal.h | 71 +- > > platform/linux-generic/odp_timer.c | 823 > > +++++++++++++-------- > > platform/linux-generic/priority_queue.c | 289 ++++++++ > > platform/linux-generic/priority_queue.h | 107 +++ > > test/api_test/odp_timer_ping.c | 58 +- > > 8 files changed, 1455 insertions(+), 465 deletions(-) > > create mode 100644 platform/linux-generic/priority_queue.c > > create mode 100644 platform/linux-generic/priority_queue.h > > > > diff --git a/example/timer/odp_timer_test.c > > b/example/timer/odp_timer_test.c > > index bf1d7df..0b4fedf 100644 > > --- a/example/timer/odp_timer_test.c > > +++ b/example/timer/odp_timer_test.c > > @@ -35,7 +35,6 @@ > > typedef struct { > > int core_count; /**< Core count*/ > > int resolution_us; /**< Timeout resolution in usec*/ > > - int min_us; /**< Minimum timeout in usec*/ > > int max_us; /**< Maximum timeout in usec*/ > > int period_us; /**< Timeout period in usec*/ > > int tmo_count; /**< Timeout count*/ > > @@ -45,18 +44,16 @@ typedef struct { > > /** @private Barrier for test synchronisation */ > > static odp_barrier_t test_barrier; > > > > -/** @private Timer handle*/ > > -static odp_timer_t test_timer; > > +/** @private Timer pool handle*/ > > +static odp_timer_pool_t tp; > > > > > > /** @private test timeout */ > > static void test_abs_timeouts(int thr, test_args_t *args) > > { > > - uint64_t tick; > > - uint64_t period; > > + odp_timer_tick_t period; > > uint64_t period_ns; > > odp_queue_t queue; > > - odp_buffer_t buf; > > int num; > > > > ODP_DBG(" [%i] test_timeouts\n", thr); > > @@ -64,37 +61,51 @@ static void test_abs_timeouts(int thr, > > test_args_t *args) > > queue = odp_queue_lookup("timer_queue"); > > > > period_ns = args->period_us*USEC; > > - period = odp_timer_ns_to_tick(test_timer, period_ns); > > + period = odp_timer_ns_to_tick(tp, period_ns); > > > > ODP_DBG(" [%i] period %"PRIu64" ticks, %"PRIu64" ns\n", thr, > > period, period_ns); > > > > - tick = odp_timer_current_tick(test_timer); > > + ODP_DBG(" [%i] current tick %"PRIu64"\n", thr, > > + odp_timer_current_tick(tp)); > > > > - ODP_DBG(" [%i] current tick %"PRIu64"\n", thr, tick); > > - > > - tick += period; > > - > > - if (odp_timer_absolute_tmo(test_timer, tick, queue, > > ODP_BUFFER_INVALID) > > - == ODP_TIMER_TMO_INVALID){ > > - ODP_DBG("Timeout request failed\n"); > > + odp_timer_t test_timer; > > + test_timer = odp_timer_alloc(tp, queue, NULL); > > + if (test_timer == ODP_TIMER_INVALID) { > > + ODP_ERR("Failed to allocate timer\n"); > > return; > > } > > + odp_timer_set_rel(test_timer, period); > > > > num = args->tmo_count; > > > > while (1) { > > - odp_timeout_t tmo; > > - > > - buf = odp_schedule_one(&queue, ODP_SCHED_WAIT); > > + /* Local variables because received timeouts may not > > + originate from timer we created above */ > > + odp_timer_tmo_t tmo; > > + odp_timer_tick_t tick; > > + odp_timer_t hdl; > > + > > + /* Get the next ready buffer/timeout */ > > + tmo = odp_schedule_one(&queue, ODP_SCHED_WAIT); > > + switch (odp_timer_tmo_status(tmo)) { > > + case ODP_TMO_FRESH: > > + break; > > + case ODP_TMO_STALE: > > + ODP_DBG("[%i] Stale timeout received\n", thr); > > + break; > > + case ODP_TMO_ORPHAN: > > + ODP_DBG("[%i] Orphaned timeout received\n", > > + thr); > > + odp_buffer_free(tmo); > > + continue; > > + } > > > > - tmo = odp_timeout_from_buffer(buf); > > - tick = odp_timeout_tick(tmo); > > + tick = odp_timer_get_expiry(tmo); > > + hdl = odp_timer_get_handle(tmo); > > > > ODP_DBG(" [%i] timeout, tick %"PRIu64"\n", thr, > > tick); > > > > - odp_buffer_free(buf); > > - > > num--; > > > > if (num == 0) > > @@ -102,10 +113,13 @@ static void test_abs_timeouts(int thr, > > test_args_t *args) > > > > tick += period; > > > > - odp_timer_absolute_tmo(test_timer, tick, > > - queue, ODP_BUFFER_INVALID); > > + if (hdl != ODP_TIMER_INVALID) > > + odp_timer_set_abs(hdl, tick); > > } > > > > + odp_timer_cancel(test_timer); > > + odp_timer_free(test_timer); > > + > > if (odp_queue_sched_type(queue) == ODP_SCHED_SYNC_ATOMIC) > > odp_schedule_release_atomic(); > > } > > @@ -159,7 +173,6 @@ static void print_usage(void) > > printf("Options:\n"); > > printf(" -c, --count <number> core count, core IDs > > start from 1\n"); > > printf(" -r, --resolution <us> timeout resolution in > > usec\n"); > > - printf(" -m, --min <us> minimum timeout in usec\n"); > > printf(" -x, --max <us> maximum timeout in usec\n"); > > printf(" -p, --period <us> timeout period in usec\n"); > > printf(" -t, --timeouts <count> timeout repeat count\n"); > > @@ -183,7 +196,6 @@ static void parse_args(int argc, char *argv[], > > test_args_t *args) > > static struct option longopts[] = { > > {"count", required_argument, NULL, 'c'}, > > {"resolution", required_argument, NULL, 'r'}, > > - {"min", required_argument, NULL, 'm'}, > > {"max", required_argument, NULL, 'x'}, > > {"period", required_argument, NULL, 'p'}, > > {"timeouts", required_argument, NULL, 't'}, > > @@ -194,14 +206,13 @@ static void parse_args(int argc, char > > *argv[], test_args_t *args) > > /* defaults */ > > args->core_count = 0; /* all cores */ > > args->resolution_us = 10000; > > - args->min_us = args->resolution_us; > > args->max_us = 100000000; > > args->period_us = 1000000; > > args->tmo_count = 30; > > > > while (1) { > > opt = getopt_long(argc, argv, "+c:r:m:x:p:t:h", > > - longopts, &long_index); > > + longopts, &long_index); > > > > if (opt == -1) > > break; /* No more options */ > > @@ -213,9 +224,6 @@ static void parse_args(int argc, char *argv[], > > test_args_t *args) > > case 'r': > > args->resolution_us = atoi(optarg); > > break; > > - case 'm': > > - args->min_us = atoi(optarg); > > - break; > > case 'x': > > args->max_us = atoi(optarg); > > break; > > @@ -299,7 +307,6 @@ int main(int argc, char *argv[]) > > > > printf("first core: %i\n", first_core); > > printf("resolution: %i usec\n", args.resolution_us); > > - printf("min timeout: %i usec\n", args.min_us); > > printf("max timeout: %i usec\n", args.max_us); > > printf("period: %i usec\n", args.period_us); > > printf("timeouts: %i\n", args.tmo_count); > > @@ -323,10 +330,23 @@ int main(int argc, char *argv[]) > > ODP_BUFFER_TYPE_TIMEOUT); > > > > if (pool == ODP_BUFFER_POOL_INVALID) { > > - ODP_ERR("Pool create failed.\n"); > > + ODP_ERR("Buffer pool create failed.\n"); > > return -1; > > } > > > > + tp = odp_timer_pool_create("timer_pool", pool, > > + args.resolution_us*USEC, > > + args.max_us*USEC, > > + num_workers, /* One timer per > > worker */ > > + true, > > + ODP_CLOCK_DEFAULT); > > + if (tp == ODP_TIMER_POOL_INVALID) { > > + ODP_ERR("Timer pool create failed.\n"); > > + return -1; > > + } > > + > > + odp_shm_print_all(); > > + > > /* > > * Create a queue for timer test > > */ > > @@ -342,13 +362,6 @@ int main(int argc, char *argv[]) > > return -1; > > } > > > > - test_timer = odp_timer_create("test_timer", pool, > > - args.resolution_us*USEC, > > - args.min_us*USEC, > > - args.max_us*USEC); > > - > > - odp_shm_print_all(); > > - > > printf("CPU freq %"PRIu64" hz\n", odp_sys_cpu_hz()); > > printf("Cycles vs nanoseconds:\n"); > > ns = 0; > > diff --git a/platform/linux-generic/Makefile.am > > b/platform/linux-generic/Makefile.am > > index f4dfdc1..e2bc1a7 100644 > > --- a/platform/linux-generic/Makefile.am > > +++ b/platform/linux-generic/Makefile.am > > @@ -72,4 +72,5 @@ __LIB__libodp_la_SOURCES = \ > > odp_thread.c \ > > odp_ticketlock.c \ > > odp_time.c \ > > + priority_queue.c \ > > odp_timer.c > > diff --git a/platform/linux-generic/include/api/odp_timer.h > > b/platform/linux-generic/include/api/odp_timer.h > > index 01db839..e5f961c 100644 > > --- a/platform/linux-generic/include/api/odp_timer.h > > +++ b/platform/linux-generic/include/api/odp_timer.h > > @@ -1,4 +1,4 @@ > > -/* Copyright (c) 2013, Linaro Limited > > +/* Copyright (c) 2014, Linaro Limited > > * All rights reserved. > > * > > * SPDX-License-Identifier: BSD-3-Clause > > @@ -8,7 +8,175 @@ > > /** > > * @file > > * > > - * ODP timer > > + * ODP timer service > > + * > > + > > +//Example #1 Retransmission timer (e.g. for reliable connections) > > + > > +//Create timer pool for reliable connections > > +#define SEC 1000000000ULL //1s expressed in nanoseconds > > +odp_timer_pool_t tcp_tpid = > > + odp_timer_pool_create("TCP", > > + buffer_pool, > > + 1000000,//resolution 1ms > > + 7200 * SEC,//max tmo length 2hours > > + 40000,//num_timers > > + true,//shared > > + ODP_CLOCK_DEFAULT > > + ); > > +if (tcp_tpid == ODP_TIMER_POOL_INVALID) > > +{ > > + //Failed to create timer pool => fatal error > > +} > > + > > + > > +//Setting up a new connection > > +//Allocate retransmission timeout (identical for supervision timeout) > > +//The user pointer points back to the connection context > > +conn->ret_tim = odp_timer_alloc(tcp_tpid, queue, conn); > > +//Check if all resources were successfully allocated > > +if (conn->ret_tim == ODP_TIMER_INVALID) > > +{ > > + //Failed to allocate all resources for connection => tear down > > + //Destroy timeout > > + odp_timer_free(conn->ret_tim); > > + //Tear down connection > > + ... > > + return false; > > +} > > +//All necessary resources successfully allocated > > +//Compute initial retransmission length in timer ticks > > +conn->ret_len = odp_timer_ns_to_tick(tcp_tpid, 3 * SEC);//Per RFC1122 > > +//Arm the timer > > +odp_timer_set_rel(conn->ret_tim, conn->ret_len); > > +return true; > > + > > + > > +//A packet for the connection has just been transmitted > > +//Reset the retransmission timer > > +odp_timer_set_rel(conn->ret_tim, conn->ret_len); > > + > > + > > +//A retransmission timeout for the connection has been received > > +//Check if timeout is fresh or stale, for stale timeouts we need > > to reset the > > +//timer > > +switch (odp_timer_tmo_status(tmo)) > > +{ > > + case ODP_TMO_FRESH : > > + //Fresh timeout, last transmitted packet not acked in time => > > + retransmit > > + //Get connection from timeout event > > + conn = odp_timer_get_userptr(tmo); > > + //Retransmit last packet (e.g. TCP segment) > > + ... > > + //Re-arm timer using original delta value > > + odp_timer_set_rel(conn->ret_tim, conn->ret_len); > > + break; > > + case ODP_TMO_STALE : > > + break;//Do nothing > > + case ODP_TMO_ORPHAN : > > + odp_free_buffer(tmo); > > + return;//Get out of here > > +} > > + > > + > > +//Example #2 Periodic tick > > + > > +//Create timer pool for periodic ticks > > +odp_timer_pool_t per_tpid = > > + odp_timer_pool_create("periodic-tick", > > + buffer_pool, > > + 1,//resolution 1ns > > + 1000000000,//maximum timeout length 1s > > + 10,//num_timers > > + false,//not shared > > + ODP_CLOCK_DEFAULT > > + ); > > +if (per_tpid == ODP_TIMER_POOL_INVALID) > > +{ > > + //Failed to create timer pool => fatal error > > +} > > + > > + > > +//Allocate periodic timer > > +tim_1733 = odp_timer_alloc(per_tpid, queue, NULL); > > +//Check if all resources were successfully allocated > > +if (tim_1733 == ODP_TIMER_INVALID) > > +{ > > + //Failed to allocate all resources => tear down > > + //Destroy timeout > > + odp_timer_free(tim_1733); > > + //Tear down other state > > + ... > > + return false; > > +} > > +//All necessary resources successfully allocated > > +//Compute tick period in timer ticks > > +period_1733 = odp_timer_ns_to_tick(per_tpid, 1000000000U / > > 1733U);//1733Hz > > +//Compute when next tick should expire > > +next_1733 = odp_timer_current_tick(per_tpid) + period_1733; > > +//Arm the periodic timer > > +odp_timer_set_abs(tim_1733, next_1733); > > +return true; > > + > > + > > + > > +//A periodic timer timeout has been received > > +//Must call odp_timer_tmo_status() on timeout! > > +ret = odp_timer_tmo_status(tmo); > > +//We expect the timeout is fresh since we are not calling set or > > cancel on > > +//active or expired timers in this example > > +assert(ret == ODP_TMO_FRESH); > > +//Do processing driven by timeout *before* > > +... > > +do { > > + //Compute when the timer should expire next > > + next_1733 += period_1733; > > + //Check that this is in the future > > + if (likely(next_1733 > odp_timer_current_tick(per_tpid)) > > + break;//Yes, done > > + //Else we missed a timeout > > + //Optionally attempt some recovery and/or logging of the > > problem > > + ... > > +} while (0); > > +//Re-arm periodic timer > > +odp_timer_set_abs(tim_1733, next_1733); > > +//Or do processing driven by timeout *after* > > +... > > +return; > > + > > +//Example #3 Tear down of flow > > +//ctx points to flow context data structure owned by application > > +//Free the timer, cancelling any timeout > > +odp_timer_free(ctx->timer);//Any enqueued timeout will be made > > invalid > > +//Continue tearing down and eventually freeing context > > +... > > +return; > > + > > +//A timeout has been received, check status > > +switch (odp_timer_tmo_status(tmo)) > > +{ > > + case ODP_TMO_FRESH : > > + //A flow has timed out, tear it down > > + //Find flow context from timeout > > + ctx = (context *)odp_timer_get_userptr(tmo); > > + //Free the supervision timer, any enqueued timeout will remain > > + odp_timer_free(ctx->tim); > > + //Free other flow related resources > > + ... > > + //Flow torn down > > + break; > > + case ODP_TMO_STALE : > > + //A stale timeout was received, timer automatically reset > > + break; > > + case ODP_TMO_ORPHAN : > > + //Orphaned timeout (from previously torn down flow) > > + //No corresponding timer or flow context > > + //Free the timeout > > + odp_buffer_free(tmo); > > + break; > > +} > > + > > */ > > > > #ifndef ODP_TIMER_H_ > > @@ -23,139 +191,325 @@ extern "C" { > > #include <odp_buffer_pool.h> > > #include <odp_queue.h> > > > > +/** > > +* ODP timer pool handle (platform dependent) > > +*/ > > +struct odp_timer_pool; > > +typedef struct odp_timer_pool *odp_timer_pool_t; > > > > /** > > - * ODP timer handle > > + * Invalid timer pool handle (platform dependent) > > */ > > -typedef uint32_t odp_timer_t; > > +#define ODP_TIMER_POOL_INVALID NULL > > > > -/** Invalid timer */ > > -#define ODP_TIMER_INVALID 0 > > +typedef enum odp_timer_pool_clock_source_e { > > + ODP_CLOCK_DEFAULT = 0, > > + /* Platform dependent which clock sources exist beyond > > + ODP_CLOCK_DEFAULT */ > > + ODP_CLOCK_NONE = 1 > > +} odp_timer_pool_clock_source_t; > > > > +/** > > +* ODP timer handle (platform dependent) > > +*/ > > +struct odp_timer; > > +typedef struct odp_timer *odp_timer_t; > > > > /** > > - * ODP timeout handle > > + * Invalid timer handle (platform dependent) > > */ > > -typedef odp_buffer_t odp_timer_tmo_t; > > - > > -/** Invalid timeout */ > > -#define ODP_TIMER_TMO_INVALID 0 > > +#define ODP_TIMER_INVALID NULL > > > > +/** > > + * ODP timeout event handle > > + */ > > +typedef odp_buffer_t odp_timer_tmo_t; > > > > /** > > - * Timeout notification > > + * ODP timeout status > > */ > > -typedef odp_buffer_t odp_timeout_t; > > +typedef enum odp_timer_tmo_status_e { > > + ODP_TMO_FRESH, /* Timeout is fresh, process it */ > > + ODP_TMO_STALE, /* Timer reset or cancelled, do nothing */ > > + ODP_TMO_ORPHAN,/* Timer deleted, free timeout */ > > +} odp_timer_tmo_status_t; > > + > > +/** > > +* ODP tick value > > +*/ > > +typedef uint64_t odp_timer_tick_t; > > > > > > /** > > - * Create a timer > > + * Create a timer pool > > * > > - * Creates a new timer with requested properties. > > + * Create a new timer pool. > > + * odp_timer_pool_create() is typically called once or a couple > > of times during > > + * application initialisation. > > * > > * @param name Name > > - * @param pool Buffer pool for allocating timeout notifications > > + * @param buf_pool Buffer pool for allocating timers > > * @param resolution Timeout resolution in nanoseconds > > - * @param min_tmo Minimum timeout duration in nanoseconds > > - * @param max_tmo Maximum timeout duration in nanoseconds > > + * @param max_tmo Maximum relative timeout in nanoseconds > > + * @param num_timers Number of supported timers (minimum) > > + * @param shared Shared or private timer pool. > > + * Operations on shared timers will include the > > necessary > > + * mutual exclusion, operations on private timers > > may not > > + * (mutual exclusion is the responsibility of the > > caller). > > + * @param clk_src Clock source to use > > + * > > + * @return Timer pool handle if successful, otherwise > > ODP_TIMER_POOL_INVALID > > + * and errno set > > + */ > > +odp_timer_pool_t > > +odp_timer_pool_create(const char *name, > > + odp_buffer_pool_t buf_pool, > > + uint64_t resolution, > > + uint64_t max_tmo, > > + uint32_t num_timers, > > + bool shared, > > + odp_timer_pool_clock_source_t clk_src); > > + > > +/** > > + * Start a timer pool > > + * > > + * Start all created timer pools, enabling the allocation of timers. > > + * The purpose of this call is to coordinate the creation of > > multiple timer > > + * pools that may use the same underlying HW resources. > > + * This function may be called multiple times. > > + */ > > +void odp_timer_pool_start(void); > > + > > +/** > > + * Destroy a timer pool > > * > > - * @return Timer handle if successful, otherwise ODP_TIMER_INVALID > > + * Destroy a timer pool, freeing all resources. > > + * All timers must have been freed. > > + * > > + * @param tpid Timer pool identifier > > */ > > -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); > > +void odp_timer_pool_destroy(odp_timer_pool_t tpid); > > > > /** > > * Convert timer ticks to nanoseconds > > * > > - * @param timer Timer > > + * @param tpid Timer pool identifier > > * @param ticks Timer ticks > > * > > * @return Nanoseconds > > */ > > -uint64_t odp_timer_tick_to_ns(odp_timer_t timer, uint64_t ticks); > > +uint64_t odp_timer_tick_to_ns(odp_timer_pool_t tpid, > > odp_timer_tick_t ticks); > > > > /** > > * Convert nanoseconds to timer ticks > > * > > - * @param timer Timer > > + * @param tpid Timer pool identifier > > * @param ns Nanoseconds > > * > > * @return Timer ticks > > */ > > -uint64_t odp_timer_ns_to_tick(odp_timer_t timer, uint64_t ns); > > +odp_timer_tick_t odp_timer_ns_to_tick(odp_timer_pool_t tpid, > > uint64_t ns); > > > > /** > > - * Timer resolution in nanoseconds > > + * Current tick value > > * > > - * @param timer Timer > > + * @param tpid Timer pool identifier > > * > > - * @return Resolution in nanoseconds > > + * @return Current time in timer ticks > > + */ > > +odp_timer_tick_t odp_timer_current_tick(odp_timer_pool_t tpid); > > + > > +/** > > + * ODP timer configurations > > */ > > -uint64_t odp_timer_resolution(odp_timer_t timer); > > + > > +typedef enum odp_timer_pool_conf_e { > > + ODP_TIMER_NAME, /* Return name of timer pool */ > > + ODP_TIMER_RESOLUTION,/* Return the timer resolution (in ns) */ > > + ODP_TIMER_MAX_TMO, /* Return the maximum supported > > timeout (in ns) */ > > + ODP_TIMER_NUM_TIMERS,/* Return number of supported timers */ > > + ODP_TIMER_SHARED /* Return shared flag */ > > +} odp_timer_pool_conf_t; > > > > /** > > - * Maximum timeout in timer ticks > > + * Query different timer pool configurations, e.g. > > + * Timer resolution in nanoseconds > > + * Maximum timeout in timer ticks > > + * Number of supported timers > > + * Shared or private timer pool > > * > > - * @param timer Timer > > + * @param tpid Timer pool identifier > > + * @param item Configuration item being queried > > * > > - * @return Maximum timeout in timer ticks > > + * @return the requested piece of information or 0 for unknown item. > > */ > > -uint64_t odp_timer_maximum_tmo(odp_timer_t timer); > > +uint64_t odp_timer_pool_query_conf(odp_timer_pool_t tpid, > > + odp_timer_pool_conf_t item); > > > > /** > > - * Current timer tick > > + * Allocate a timer > > * > > - * @param timer Timer > > + * Create a timer (allocating all necessary resources e.g. > > timeout event) from > > + * the timer pool. > > * > > - * @return Current time in timer ticks > > + * @param tpid Timer pool identifier > > + * @param queue Destination queue for timeout notifications > > + * @param user_ptr User defined pointer or NULL (copied to timeouts) > > + * > > + * @return Timer handle if successful, otherwise > > ODP_TIMER_INVALID and > > + * errno set. > > + */ > > +odp_timer_t odp_timer_alloc(odp_timer_pool_t tpid, > > + odp_queue_t queue, > > + void *user_ptr); > > + > > +/** > > + * Free a timer > > + * > > + * Free (destroy) a timer, freeing all associated resources (e.g. > > default > > + * timeout event). An expired and enqueued timeout event will not > > be freed. > > + * It is the responsibility of the application to free this > > timeout when it > > + * is received. > > + * > > + * @param tim Timer handle > > */ > > -uint64_t odp_timer_current_tick(odp_timer_t timer); > > +void odp_timer_free(odp_timer_t tim); > > > > /** > > - * Request timeout with an absolute timer tick > > + * Set a timer (absolute time) with a user-defined timeout buffer > > * > > - * When tick reaches tmo_tick, the timer enqueues the timeout > > notification into > > - * the destination queue. > > + * Set (arm) the timer to expire at specific time. The user-defined > > + * buffer will be enqueued when the timer expires. > > + * Arming may fail (if the timer is in state EXPIRED), an earlier > > timeout > > + * will then be received. odp_timer_tmo_status() must be used to > > check if > > + * the received timeout is valid. > > * > > - * @param timer Timer > > - * @param tmo_tick Absolute timer tick value which triggers the > > timeout > > - * @param queue Destination queue for the timeout notification > > - * @param buf User defined timeout notification buffer. When > > - * ODP_BUFFER_INVALID, default timeout > > notification is used. > > + * Note: any invalid parameters will be treated as programming > > errors and will > > + * cause the application to abort. > > + * Note: a timeout too near in time may be delivered immediately. > > + * Note: a timeout too far away in time (beyond max_timeout) > > might be delivered > > + * early. > > * > > - * @return Timeout handle if successful, otherwise > > ODP_TIMER_TMO_INVALID > > + * @param tim Timer > > + * @param abs_tck Expiration time in absolute timer ticks > > + * @param user_buf The buffer to use as timeout event > > */ > > -odp_timer_tmo_t odp_timer_absolute_tmo(odp_timer_t timer, > > uint64_t tmo_tick, > > - odp_queue_t queue, > > odp_buffer_t buf); > > +void odp_timer_set_abs_w_buf(odp_timer_t tim, > > + odp_timer_tick_t abs_tck, > > + odp_buffer_t user_buf); > > > > /** > > - * Cancel a timeout > > + * Set a timer with an absolute expiration time > > + * > > + * Set (arm) the timer to expire at a specific time. > > + * Arming may fail (if the timer is in state EXPIRED), an earlier > > timeout > > + * will then be received. odp_timer_tmo_status() must be used to > > check if > > + * the received timeout is valid. > > * > > - * @param timer Timer > > - * @param tmo Timeout to cancel > > + * Note: any invalid parameters will be treated as programming > > errors and will > > + * cause the application to abort. > > + * Note: a timeout too near in time may be delivered immediately. > > + * Note: a timeout too far away in time (beyond max_timeout) > > might be delivered > > + * early, it will automatically be reset by odp_timer_tmo_status(). > > * > > - * @return 0 if successful > > + * @param tim Timer > > + * @param abs_tck Expiration time in absolute timer ticks > > */ > > -int odp_timer_cancel_tmo(odp_timer_t timer, odp_timer_tmo_t tmo); > > +void odp_timer_set_abs(odp_timer_t tim, odp_timer_tick_t abs_tck); > > > > /** > > - * Convert buffer handle to timeout handle > > + * Set a timer with a relative expiration time > > * > > - * @param buf Buffer handle > > + * Set (arm) the timer to expire at a relative future time. > > + * Arming may fail (if the timer is in state EXPIRED), > > + * an earlier timeout will then be received. > > odp_timer_tmo_status() must > > + * be used to check if the received timeout is valid. > > * > > - * @return Timeout buffer handle > > + * Note: any invalid parameters will be treated as programming > > errors and will > > + * cause the application to abort. > > + * Note: a timeout too near in time may be delivered immediately. > > + * Note: a timeout too far away in time (beyond max_timeout) > > might be delivered > > + * early, it will automatically be reset by odp_timer_tmo_status(). > > + * > > + * @param tim Timer > > + * @param rel_tck Expiration time in timer ticks relative to > > current time of > > + * the timer pool the timer belongs to > > */ > > -odp_timeout_t odp_timeout_from_buffer(odp_buffer_t buf); > > +void odp_timer_set_rel(odp_timer_t tim, odp_timer_tick_t rel_tck); > > > > /** > > - * Return absolute timeout tick > > + * Cancel a timer > > + * > > + * Cancel a timer, preventing future expiration and delivery. > > + * > > + * A timer that has already expired and been enqueued for > > delivery may be > > + * impossible to cancel and will instead be delivered to the > > destination queue. > > + * Use odp_timer_tmo_status() the check whether a received > > timeout is fresh or > > + * stale (cancelled). Stale timeouts will automatically be recycled. > > * > > - * @param tmo Timeout buffer handle > > + * Note: any invalid parameters will be treated as programming > > errors and will > > + * cause the application to abort. > > * > > - * @return Absolute timeout tick > > + * @param tim Timer handle > > */ > > -uint64_t odp_timeout_tick(odp_timeout_t tmo); > > +void odp_timer_cancel(odp_timer_t tim); > > + > > +/** > > + * Return fresh/stale/orphan status of timeout. > > + * > > + * Check a received timeout for orphaness (i.e. parent timer > > freed) and > > + * staleness (i.e. parent timer has been reset or cancelled after > > timeout > > + * was enqueued). > > + * If the timeout is fresh, it should be processed. > > + * If the timeout is stale, the timer will automatically be reset > > unless it > > + * was cancelled. > > + * If the timeout is orphaned, it should be freed (by the caller). > > + * > > + * Note: odp_timer_tmo_status() must be called on all received (not > > + * user-defined) timeouts! > > + * > > + * @param tmo Timeout > > + * > > + * @return ODP_TMO_FRESH, ODP_TMO_STALE, ODP_TMO_ORPHAN > > + */ > > +odp_timer_tmo_status_t odp_timer_tmo_status(odp_timer_tmo_t tmo); > > + > > +/** > > + * Get timer handle > > + * > > + * Return Handle of parent timer. > > + * > > + * @param tmo Timeout > > + * > > + * @return Timer handle or ODP_TIMER_INVALID for orphaned timeouts > > + */ > > +odp_timer_t odp_timer_get_handle(odp_timer_tmo_t tmo); > > + > > +/** > > + * Get expiration time > > + * > > + * Return (actual) expiration time of timeout. > > + * > > + * @param tmo Timeout > > + * > > + * @return Expiration time > > + */ > > +odp_timer_tick_t odp_timer_get_expiry(odp_timer_tmo_t tmo); > > + > > +/** > > + * Get user pointer > > + * > > + * Return User pointer of timer associated with timeout. > > + * The user pointer is often used to point to some associated > > context. > > + * > > + * @param tmo Timeout > > + * > > + * @return User pointer > > + */ > > +void *odp_timer_get_userptr(odp_timer_tmo_t tmo); > > + > > +/* Helper functions */ > > +unsigned odp_timer_pool_expire(odp_timer_pool_t tpid, > > odp_timer_tick_t tick); > > > > #ifdef __cplusplus > > } > > diff --git a/platform/linux-generic/include/odp_timer_internal.h > > b/platform/linux-generic/include/odp_timer_internal.h > > index ad28f53..ff8f209 100644 > > --- a/platform/linux-generic/include/odp_timer_internal.h > > +++ b/platform/linux-generic/include/odp_timer_internal.h > > @@ -1,4 +1,4 @@ > > -/* Copyright (c) 2013, Linaro Limited > > +/* Copyright (c) 2014, Linaro Limited > > * All rights reserved. > > * > > * SPDX-License-Identifier: BSD-3-Clause > > @@ -8,72 +8,53 @@ > > /** > > * @file > > * > > - * ODP timer timeout descriptor - implementation internal > > + * ODP timeout descriptor - implementation internal > > */ > > > > #ifndef ODP_TIMER_INTERNAL_H_ > > #define ODP_TIMER_INTERNAL_H_ > > > > -#ifdef __cplusplus > > -extern "C" { > > -#endif > > - > > -#include <odp_std_types.h> > > -#include <odp_queue.h> > > -#include <odp_buffer.h> > > +#include <odp_align.h> > > +#include <odp_debug.h> > > #include <odp_buffer_internal.h> > > #include <odp_buffer_pool_internal.h> > > #include <odp_timer.h> > > > > -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; > > - > > - > > -struct odp_timeout_hdr_t; > > - > > /** > > - * Timeout notification header > > + * Internal Timeout header > > */ > > -typedef struct odp_timeout_hdr_t { > > +typedef struct { > > + /* common buffer header */ > > odp_buffer_hdr_t buf_hdr; > > > > - timeout_t meta; > > - > > + /* Requested expiration time */ > > + odp_timer_tick_t expiration; > > + /* User ptr inherited from parent timer */ > > + void *user_ptr; > > + /* Parent timer */ > > + odp_timer_t timer; > > + /* Tag inherited from parent timer at time of expiration */ > > + uint32_t tag; > > + /* Gen-cnt inherited from parent timer at time of creation */ > > + uint32_t gc; > > uint8_t buf_data[]; > > } odp_timeout_hdr_t; > > > > - > > - > > +/* C++ doesn't allow offsetof() on "non-POD" datatypes. Don't > > know why > > + odp_timeout_hdr_t is classified as non-POD, perhaps because of the > > + inheritance? */ > > ODP_STATIC_ASSERT(sizeof(odp_timeout_hdr_t) == > > - ODP_OFFSETOF(odp_timeout_hdr_t, buf_data), > > - "ODP_TIMEOUT_HDR_T__SIZE_ERR"); > > - > > + ODP_OFFSETOF(odp_timeout_hdr_t, buf_data), > > + "sizeof(odp_timeout_hdr_t) == > > ODP_OFFSETOF(odp_timeout_hdr_t, buf_data)"); > > ODP_STATIC_ASSERT(sizeof(odp_timeout_hdr_t) % sizeof(uint64_t) == 0, > > - "ODP_TIMEOUT_HDR_T__SIZE_ERR2"); > > - > > + "sizeof(odp_timeout_hdr_t) % sizeof(uint64_t) == > > 0"); > > > > /** > > - * Return timeout header > > + * Return the timeout header > > */ > > -static inline odp_timeout_hdr_t *odp_timeout_hdr(odp_timeout_t tmo) > > +static inline odp_timeout_hdr_t *odp_timeout_hdr(odp_buffer_t buf) > > { > > - odp_buffer_hdr_t *buf_hdr = odp_buf_to_hdr((odp_buffer_t)tmo); > > - return (odp_timeout_hdr_t *)(uintptr_t)buf_hdr; > > -} > > - > > - > > - > > -#ifdef __cplusplus > > + return (odp_timeout_hdr_t *)odp_buf_to_hdr(buf); > > } > > -#endif > > > > #endif > > diff --git a/platform/linux-generic/odp_timer.c > > b/platform/linux-generic/odp_timer.c > > index 1bf37f9..cea47a3 100644 > > --- a/platform/linux-generic/odp_timer.c > > +++ b/platform/linux-generic/odp_timer.c > > @@ -1,402 +1,631 @@ > > -/* Copyright (c) 2013, Linaro Limited > > +/* Copyright (c) 2014, Linaro Limited > > * All rights reserved. > > * > > * SPDX-License-Identifier: BSD-3-Clause > > */ > > > > -#include <odp_timer.h> > > -#include <odp_timer_internal.h> > > -#include <odp_buffer_pool_internal.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> > > +/** > > + * @file > > + * > > + * ODP timer service > > + * > > + */ > > > > +#include <assert.h> > > +#include <errno.h> > > #include <string.h> > > - > > -#define NUM_TIMERS 1 > > -#define MAX_TICKS 1024 > > -#define RESOLUTION_NS 1000000 > > - > > - > > -typedef struct { > > - odp_spinlock_t lock; > > - timeout_t *list; > > -} tick_t; > > - > > -typedef struct { > > - int allocated; > > - volatile int active; > > - volatile uint64_t cur_tick; > > - timer_t timerid; > > - odp_timer_t timer_hdl; > > - odp_buffer_pool_t pool; > > - uint64_t resolution_ns; > > - uint64_t max_ticks; > > - tick_t tick[MAX_TICKS]; > > - > > -} timer_ring_t; > > - > > -typedef struct { > > - odp_spinlock_t lock; > > - int num_timers; > > - timer_ring_t timer[NUM_TIMERS]; > > - > > -} timer_global_t; > > - > > -/* Global */ > > -static timer_global_t odp_timer; > > - > > -static void add_tmo(tick_t *tick, timeout_t *tmo) > > +#include <stdlib.h> > > +#include <time.h> > > +#include <signal.h> > > +#include "odp_std_types.h" > > +#include "odp_buffer.h" > > +#include "odp_buffer_pool.h" > > +#include "odp_queue.h" > > +#include "odp_hints.h" > > +#include "odp_sync.h" > > +#include "odp_spinlock.h" > > +#include "odp_debug.h" > > +#include "odp_align.h" > > +#include "odp_shared_memory.h" > > +#include "odp_hints.h" > > +#include "odp_internal.h" > > +#include "odp_timer.h" > > +#include "odp_timer_internal.h" > > +#include "priority_queue.h" > > + > > +typedef struct odp_timer { > > + pq_element pqelem;/* Base class */ > > + odp_timer_tick_t req_tmo;/* Requested timeout tick */ > > + odp_buffer_t tmo_buf;/* ODP_BUFFER_INVALID if timeout > > enqueued */ > > + odp_queue_t queue;/* ODP_QUEUE_INVALID if timer is free */ > > + uint32_t tag;/* Reusing tag as next pointer/index when > > timer is free */ > > + uint32_t gc; > > + bool user_buf; /* User-defined buffer? */ > > +} odp_timer; > > + > > +/* Constructor for array of objects */ > > +static inline void odp_timer_con(odp_timer *this) > > { > > - odp_spinlock_lock(&tick->lock); > > - > > - tmo->next = tick->list; > > - tick->list = tmo; > > - > > - odp_spinlock_unlock(&tick->lock); > > + pq_element_con(&this->pqelem); > > + this->tmo_buf = ODP_BUFFER_INVALID; > > + this->queue = ODP_QUEUE_INVALID; > > + this->gc = 0; > > } > > > > -static timeout_t *rem_tmo(tick_t *tick) > > +/* Destructor */ > > +static inline void odp_timer_des(odp_timer *this) > > { > > - timeout_t *tmo; > > - > > - odp_spinlock_lock(&tick->lock); > > - > > - tmo = tick->list; > > - > > - if (tmo) > > - tick->list = tmo->next; > > - > > - odp_spinlock_unlock(&tick->lock); > > + assert(this->tmo_buf == ODP_BUFFER_INVALID); > > + assert(this->queue == ODP_QUEUE_INVALID); > > + pq_element_des(&this->pqelem); > > +} > > > > - if (tmo) > > - tmo->next = NULL; > > +/* Setup when timer is allocated */ > > +static void setup(odp_timer *this, > > + odp_queue_t _q, > > + void *_up, > > + odp_buffer_t _tmo) > > +{ > > + this->req_tmo = INVALID_PRIORITY; > > + this->tmo_buf = _tmo; > > + this->queue = _q; > > + this->tag = 0; > > + this->user_buf = false; > > + /* Initialise constant fields of timeout event */ > > + odp_timeout_hdr_t *tmo_hdr = > > + (odp_timeout_hdr_t *)odp_buf_to_hdr(this->tmo_buf); > > + tmo_hdr->gc = this->gc; > > + tmo_hdr->timer = this; > > + tmo_hdr->user_ptr = _up; > > + /* tmo_hdr->tag set at expiration time */ > > + /* tmo_hdr->expiration set at expiration time */ > > + assert(this->queue != ODP_QUEUE_INVALID); > > +} > > > > - return tmo; > > +/* Teardown when timer is freed */ > > +static odp_buffer_t teardown(odp_timer *this) > > +{ > > + /* Increase generation count to make pending timeout > > orphaned */ > > + ++this->gc; > > + odp_buffer_t buf = this->tmo_buf; > > + this->tmo_buf = ODP_BUFFER_INVALID; > > + this->queue = ODP_QUEUE_INVALID; > > + return buf; > > } > > > > -/** > > - * Search and delete tmo entry from timeout list > > - * return -1 : on error.. handle not in list > > - * 0 : success > > - */ > > -static int find_and_del_tmo(timeout_t **tmo, odp_timer_tmo_t handle) > > +static inline uint32_t get_next_free(odp_timer *this) > > { > > - timeout_t *cur, *prev; > > - prev = NULL; > > + assert(this->queue == ODP_QUEUE_INVALID); > > + return this->tag; > > +} > > > > - for (cur = *tmo; cur != NULL; prev = cur, cur = cur->next) { > > - if (cur->tmo_buf == handle) { > > - if (prev == NULL) > > - *tmo = cur->next; > > - else > > - prev->next = cur->next; > > +static inline void set_next_free(odp_timer *this, uint32_t nf) > > +{ > > + assert(this->queue == ODP_QUEUE_INVALID); > > + this->tag = nf; > > +} > > > > - break; > > +static inline void expire(odp_timer *this, odp_timer_tick_t tick) > > +{ > > + /* Timer expired, is there actually any timeout event */ > > + /* we can enqueue? */ > > + if (odp_likely(this->tmo_buf != ODP_BUFFER_INVALID)) { > > + /* Swap out timeout buffer */ > > + odp_buffer_t buf = this->tmo_buf; > > + this->tmo_buf = ODP_BUFFER_INVALID; > > + if (odp_likely(!this->user_buf)) { > > + odp_timeout_hdr_t *tmo_hdr = > > + (odp_timeout_hdr_t > > *)odp_buf_to_hdr(buf); > > + /* Copy tag from timer */ > > + /* and actual expiration tick from timer > > pool */ > > + tmo_hdr->tag = this->tag; > > + tmo_hdr->expiration = tick; > > } > > + /* Else don't touch user-defined buffer */ > > + int rc = odp_queue_enq(this->queue, buf); > > + if (rc != 0) > > + abort(); > > } > > - > > - if (!cur) > > - /* couldn't find tmo in list */ > > - return -1; > > - > > - /* application to free tmo_buf provided by absolute_tmo > > call */ > > - return 0; > > + /* No, timeout event already enqueued */ > > } > > > > -int odp_timer_cancel_tmo(odp_timer_t timer_hdl, odp_timer_tmo_t tmo) > > +typedef struct odp_timer_pool { > > + priority_queue pq; > > + uint64_t tick; > > + bool shared; > > + odp_spinlock_t lock; > > + const char *name; > > + odp_buffer_pool_t buf_pool; > > + uint64_t resolution_ns; > > + uint64_t max_timeout; > > + odp_timer *timers; > > + uint32_t num_alloc;/* Current number of allocated timers */ > > + uint32_t max_timers;/* Max number of timers */ > > + uint32_t first_free;/* 0..max_timers-1 => free timer */ > > + timer_t timerid; > > + odp_timer_pool_clock_source_t clk_src; > > +} odp_timer_pool; > > + > > +/* Forward declarations */ > > +static void timer_init(odp_timer_pool *tp); > > +static void timer_exit(odp_timer_pool *tp); > > + > > +static void odp_timer_pool_con(odp_timer_pool *this, > > + const char *_n, > > + odp_buffer_pool_t _bp, > > + uint64_t _r, > > + uint64_t _m, > > + uint32_t _mt, > > + bool _s, > > + odp_timer_pool_clock_source_t _cs) > > { > > - int id; > > - uint64_t tick_idx; > > - timeout_t *cancel_tmo; > > - odp_timeout_hdr_t *tmo_hdr; > > - tick_t *tick; > > - > > - /* get id */ > > - id = timer_hdl - 1; > > - > > - tmo_hdr = odp_timeout_hdr((odp_timeout_t) tmo); > > - /* get tmo_buf to cancel */ > > - cancel_tmo = &tmo_hdr->meta; > > - > > - tick_idx = cancel_tmo->tick; > > - tick = &odp_timer.timer[id].tick[tick_idx]; > > - > > - odp_spinlock_lock(&tick->lock); > > - /* search and delete tmo from tick list */ > > - if (find_and_del_tmo(&tick->list, tmo) != 0) { > > - odp_spinlock_unlock(&tick->lock); > > - ODP_DBG("Couldn't find the tmo (%d) in tick > > list\n", (int)tmo); > > - return -1; > > + priority_queue_con(&this->pq, _mt); > > + this->tick = 0; > > + this->shared = _s; > > + this->name = strdup(_n); > > + this->buf_pool = _bp; > > + this->resolution_ns = _r; > > + this->max_timeout = _m; > > + this->num_alloc = 0; > > + this->max_timers = _mt; > > + this->first_free = 0; > > + this->clk_src = _cs; > > + this->timers = malloc(sizeof(odp_timer) * this->max_timers); > > + if (this->timers == NULL) { > > + ODP_ERR("%s: malloc failed\n", _n); > > + abort(); > > } > > - odp_spinlock_unlock(&tick->lock); > > - > > - return 0; > > + uint32_t i; > > + for (i = 0; i < this->max_timers; i++) > > + odp_timer_con(&this->timers[i]); > > + for (i = 0; i < this->max_timers; i++) > > + set_next_free(&this->timers[i], i + 1); > > + odp_spinlock_init(&this->lock); > > + if (this->clk_src == ODP_CLOCK_DEFAULT) > > + timer_init(this); > > + /* Make sure timer pool initialisation is globally > > observable */ > > + /* before we return a pointer to it */ > > + odp_sync_stores(); > > } > > > > -static void notify_function(union sigval sigval) > > +static odp_timer_pool *odp_timer_pool_new( > > + const char *_n, > > + odp_buffer_pool_t _bp, > > + uint64_t _r, > > + uint64_t _m, > > + uint32_t _mt, > > + bool _s, > > + odp_timer_pool_clock_source_t _cs) > > { > > - uint64_t cur_tick; > > - timeout_t *tmo; > > - tick_t *tick; > > - timer_ring_t *timer; > > - > > - timer = sigval.sival_ptr; > > - > > - if (timer->active == 0) { > > - ODP_DBG("Timer (%u) not active\n", timer->timer_hdl); > > - return; > > + odp_timer_pool *this = malloc(sizeof(odp_timer_pool)); > > + if (odp_unlikely(this == NULL)) { > > + ODP_ERR("%s: malloc failed\n", _n); > > + abort(); > > } > > + odp_timer_pool_con(this, _n, _bp, _r, _m, _mt, _s, _cs); > > + return this; > > +} > > > > - /* ODP_DBG("Tick\n"); */ > > +static void odp_timer_pool_des(odp_timer_pool *this) > > +{ > > + if (this->shared) > > + odp_spinlock_lock(&this->lock); > > + if (this->num_alloc != 0) { > > + /* It's a programming error to attempt to destroy a */ > > + /* timer pool which is still in use */ > > + ODP_ERR("%s: timers in use\n", this->name); > > + abort(); > > + } > > + if (this->clk_src == ODP_CLOCK_DEFAULT) > > + timer_exit(this); > > + uint32_t i; > > + for (i = 0; i < this->max_timers; i++) > > + odp_timer_des(&this->timers[i]); > > + free(this->timers); > > + priority_queue_des(&this->pq); > > + odp_sync_stores(); > > +} > > > > - cur_tick = timer->cur_tick++; > > +static void odp_timer_pool_del(odp_timer_pool *this) > > +{ > > + odp_timer_pool_des(this); > > + free(this); > > +} > > > > - odp_sync_stores(); > > +static inline odp_timer *timer_alloc(odp_timer_pool *this, > > + odp_queue_t queue, > > + void *user_ptr, > > + odp_buffer_t tmo_buf) > > +{ > > + odp_timer *tim = ODP_TIMER_INVALID; > > + if (odp_likely(this->shared)) > > + odp_spinlock_lock(&this->lock); > > + if (odp_likely(this->num_alloc < this->max_timers)) { > > + this->num_alloc++; > > + /* Remove first unused timer from free list */ > > + assert(this->first_free != this->max_timers); > > + tim = &this->timers[this->first_free]; > > + this->first_free = get_next_free(tim); > > + /* Insert timer into priority queue */ > > + if (odp_unlikely(!pq_register_element(&this->pq, > > + &tim->pqelem))) { > > + /* Unexpected internal error */ > > + abort(); > > + } > > + /* Create timer */ > > + setup(tim, queue, user_ptr, tmo_buf); > > + } else { > > + errno = ENFILE; /* Reusing file table overvlow */ > > + } > > + if (odp_likely(this->shared)) > > + odp_spinlock_unlock(&this->lock); > > + return tim; > > +} > > > > - tick = &timer->tick[cur_tick % MAX_TICKS]; > > +static inline void timer_free(odp_timer_pool *this, odp_timer *tim) > > +{ > > + if (odp_likely(this->shared)) > > + odp_spinlock_lock(&this->lock); > > + /* Destroy timer */ > > + odp_buffer_t buf = teardown(tim); > > + /* Remove timer from priority queue */ > > + pq_unregister_element(&this->pq, &tim->pqelem); > > + /* Insert timer into free list */ > > + set_next_free(tim, this->first_free); > > + this->first_free = (tim - &this->timers[0]) / > > sizeof(this->timers[0]); > > + assert(this->num_alloc != 0); > > + this->num_alloc--; > > + if (odp_likely(this->shared)) > > + odp_spinlock_unlock(&this->lock); > > + if (buf != ODP_BUFFER_INVALID) > > + odp_buffer_free(buf); > > +} > > > > - while ((tmo = rem_tmo(tick)) != NULL) { > > - odp_queue_t queue; > > - odp_buffer_t buf; > > +static inline void timer_reset(odp_timer_pool *this, > > + odp_timer *tim, > > + odp_timer_tick_t abs_tck) > > +{ > > + if (odp_likely(this->shared)) > > + odp_spinlock_lock(&this->lock); > > + /* Increase timer tag to make any pending timeout stale */ > > + tim->tag++; > > + /* Save requested timeout */ > > + tim->req_tmo = abs_tck; > > + /* Update timer position in priority queue */ > > + pq_reset_element(&this->pq, &tim->pqelem, abs_tck); > > + if (odp_likely(this->shared)) > > + odp_spinlock_unlock(&this->lock); > > +} > > > > - queue = tmo->queue; > > - buf = tmo->buf; > > +static inline void timer_reset_w_buf(odp_timer_pool *this, > > + odp_timer *tim, > > + odp_timer_tick_t abs_tck, > > + odp_buffer_t user_buf) > > +{ > > + if (odp_likely(this->shared)) > > + odp_spinlock_lock(&this->lock); > > + /* Increase timer tag to make any pending timeout stale */ > > + tim->tag++; > > + /* Save requested timeout */ > > + tim->req_tmo = abs_tck; > > + /* Set flag indicating presence of user defined buffer */ > > + tim->user_buf = true; > > + /* Swap in new buffer, get any old buffer pointer */ > > + odp_buffer_t old_buf = tim->tmo_buf; > > + tim->tmo_buf = user_buf; > > + /* Update timer position in priority queue */ > > + pq_reset_element(&this->pq, &tim->pqelem, abs_tck); > > + if (odp_likely(this->shared)) > > + odp_spinlock_unlock(&this->lock); > > + /* Free old buffer if present */ > > + if (odp_unlikely(old_buf != ODP_BUFFER_INVALID)) > > + odp_buffer_free(old_buf); > > +} > > > > - if (buf != tmo->tmo_buf) > > - odp_buffer_free(tmo->tmo_buf); > > +static inline void timer_cancel(odp_timer_pool *this, > > + odp_timer *tim) > > +{ > > + odp_buffer_t tmo_buf = ODP_BUFFER_INVALID; > > + if (odp_likely(this->shared)) > > + odp_spinlock_lock(&this->lock); > > + if (odp_unlikely(tim->user_buf)) { > > + /* Swap out old user buffer */ > > + tmo_buf = tim->tmo_buf; > > + tim->tmo_buf = ODP_BUFFER_INVALID; > > + tim->user_buf = false; > > + } > > + /* Else a normal timer (no user-defined buffer) */ > > + /* Increase timer tag to make any pending timeout stale */ > > + tim->tag++; > > + /* Clear requested timeout */ > > + tim->req_tmo = INVALID_PRIORITY; > > + /* Remove timer from the priority queue */ > > + pq_deactivate_element(&this->pq, &tim->pqelem); > > + if (odp_likely(this->shared)) > > + odp_spinlock_unlock(&this->lock); > > + /* Free user-defined buffer if present */ > > + if (odp_unlikely(tmo_buf != ODP_BUFFER_INVALID)) > > + odp_buffer_free(tmo_buf); > > +} > > > > - odp_queue_enq(queue, buf); > > +unsigned odp_timer_pool_expire(odp_timer_pool_t tpid, > > odp_timer_tick_t tick) > > +{ > > + odp_spinlock_lock(&tpid->lock); > > + unsigned nexp = 0; > > + odp_timer_t tim; > > + tpid->tick = tick; > > + while ((tim = (odp_timer_t)pq_release_element(&tpid->pq, > > tick)) != > > + ODP_TIMER_INVALID) { > > + assert(get_prio(&tim->pqelem) <= tick); > > + expire(tim, tick); > > + nexp++; > > } > > + odp_spinlock_unlock(&tpid->lock); > > + return nexp; > > } > > > > -static void timer_start(timer_ring_t *timer) > > +/* Functions that use Linux/POSIX per-process timers and related > > facilities */ > > +static void timer_notify(union sigval sigval) > > +{ > > + odp_timer_pool *tp = (odp_timer_pool *)sigval.sival_ptr; > > + uint64_t new_tick = tp->tick + 1; > > + (void)odp_timer_pool_expire(tp, new_tick); > > +} > > + > > +static void timer_init(odp_timer_pool *tp) > > { > > struct sigevent sigev; > > struct itimerspec ispec; > > > > - ODP_DBG("\nTimer (%u) starts\n", timer->timer_hdl); > > + ODP_DBG("Creating POSIX timer for timer pool %s, period %" > > + PRIu64" ns\n", tp->name, tp->resolution_ns); > > > > memset(&sigev, 0, sizeof(sigev)); > > memset(&ispec, 0, sizeof(ispec)); > > > > sigev.sigev_notify = SIGEV_THREAD; > > - sigev.sigev_notify_function = notify_function; > > - sigev.sigev_value.sival_ptr = timer; > > + sigev.sigev_notify_function = timer_notify; > > + sigev.sigev_value.sival_ptr = tp; > > > > - if (timer_create(CLOCK_MONOTONIC, &sigev, &timer->timerid)) { > > - ODP_DBG("Timer create failed\n"); > > - return; > > + if (timer_create(CLOCK_MONOTONIC, &sigev, &tp->timerid)) { > > + perror("timer_create"); > > + abort(); > > } > > > > ispec.it_interval.tv_sec = 0; > > - ispec.it_interval.tv_nsec = RESOLUTION_NS; > > + ispec.it_interval.tv_nsec = tp->resolution_ns; > > ispec.it_value.tv_sec = 0; > > - ispec.it_value.tv_nsec = RESOLUTION_NS; > > + ispec.it_value.tv_nsec = tp->resolution_ns; > > > > - if (timer_settime(timer->timerid, 0, &ispec, NULL)) { > > - ODP_DBG("Timer set failed\n"); > > - return; > > + if (timer_settime(&tp->timerid, 0, &ispec, NULL)) { > > + perror("timer_settime"); > > + abort(); > > } > > - > > - return; > > } > > > > -int odp_timer_init_global(void) > > +static void timer_exit(odp_timer_pool *tp) > > { > > - ODP_DBG("Timer init ..."); > > - > > - memset(&odp_timer, 0, sizeof(timer_global_t)); > > - > > - odp_spinlock_init(&odp_timer.lock); > > - > > - ODP_DBG("done\n"); > > - > > - return 0; > > + if (timer_delete(tp->timerid) != 0) { > > + perror("timer_delete"); > > + abort(); > > + } > > } > > > > -int odp_timer_disarm_all(void) > > +odp_timer_pool_t > > +odp_timer_pool_create(const char *name, > > + odp_buffer_pool_t buf_pool, > > + uint64_t resolution_ns, > > + uint64_t max_timeout, > > + uint32_t num_timers, > > + bool shared, > > + odp_timer_pool_clock_source_t clk_src) > > { > > - int timers; > > - struct itimerspec ispec; > > - > > - odp_spinlock_lock(&odp_timer.lock); > > - > > - timers = odp_timer.num_timers; > > - > > - ispec.it_interval.tv_sec = 0; > > - ispec.it_interval.tv_nsec = 0; > > - ispec.it_value.tv_sec = 0; > > - ispec.it_value.tv_nsec = 0; > > - > > - for (; timers >= 0; timers--) { > > - if (timer_settime(odp_timer.timer[timers].timerid, > > - 0, &ispec, NULL)) { > > - ODP_DBG("Timer reset failed\n"); > > - odp_spinlock_unlock(&odp_timer.lock); > > - return -1; > > - } > > - odp_timer.num_timers--; > > + /* Verify that buffer pool can be used for timeouts */ > > + odp_buffer_t buf = odp_buffer_alloc(buf_pool); > > + if (buf == ODP_BUFFER_INVALID) { > > + ODP_ERR("%s: Failed to allocate buffer\n", name); > > + abort(); > > } > > + if (odp_buffer_type(buf) != ODP_BUFFER_TYPE_TIMEOUT) { > > + ODP_ERR("%s: Buffer pool wrong type\n", name); > > + abort(); > > + } > > + odp_buffer_free(buf); > > + odp_timer_pool_t tp = odp_timer_pool_new(name, buf_pool, > > resolution_ns, > > + max_timeout, num_timers, > > + shared, clk_src); > > + return tp; > > +} > > > > - odp_spinlock_unlock(&odp_timer.lock); > > +void odp_timer_pool_start(void) > > +{ > > + /* Nothing to do here */ > > +} > > > > - return 0; > > +void odp_timer_pool_destroy(odp_timer_pool_t tpid) > > +{ > > + odp_timer_pool_del(tpid); > > } > > > > -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) > > +uint64_t odp_timer_tick_to_ns(odp_timer_pool_t tpid, > > odp_timer_tick_t ticks) > > { > > - uint32_t id; > > - timer_ring_t *timer; > > - odp_timer_t timer_hdl; > > - int i; > > - (void) name; (void) resolution; (void) min_tmo; (void) > > max_tmo; > > + return ticks * tpid->resolution_ns; > > +} > > > > - odp_spinlock_lock(&odp_timer.lock); > > +odp_timer_tick_t odp_timer_ns_to_tick(odp_timer_pool_t tpid, > > uint64_t ns) > > +{ > > + return (odp_timer_tick_t)(ns / tpid->resolution_ns); > > +} > > > > - if (odp_timer.num_timers >= NUM_TIMERS) { > > - odp_spinlock_unlock(&odp_timer.lock); > > - return ODP_TIMER_INVALID; > > - } > > +odp_timer_tick_t odp_timer_current_tick(odp_timer_pool_t tpid) > > +{ > > + return tpid->tick; > > +} > > > > - for (id = 0; id < NUM_TIMERS; id++) { > > - if (odp_timer.timer[id].allocated == 0) > > - break; > > +uint64_t odp_timer_pool_query_conf(odp_timer_pool_t tpid, > > + odp_timer_pool_conf_t item) > > +{ > > + switch (item) { > > + case ODP_TIMER_NAME: > > + return (uint64_t)(tpid->name); > > + case ODP_TIMER_RESOLUTION: > > + return tpid->resolution_ns; > > + case ODP_TIMER_MAX_TMO: > > + return tpid->max_timeout; > > + case ODP_TIMER_NUM_TIMERS: > > + return tpid->max_timers; > > + case ODP_TIMER_SHARED: > > + return tpid->shared; > > + default: > > + return 0; > > } > > +} > > > > - timer = &odp_timer.timer[id]; > > - timer->allocated = 1; > > - odp_timer.num_timers++; > > - > > - odp_spinlock_unlock(&odp_timer.lock); > > - > > - timer_hdl = id + 1; > > - > > - timer->timer_hdl = timer_hdl; > > - timer->pool = pool; > > - timer->resolution_ns = RESOLUTION_NS; > > - timer->max_ticks = MAX_TICKS; > > - > > - for (i = 0; i < MAX_TICKS; i++) { > > - odp_spinlock_init(&timer->tick[i].lock); > > - timer->tick[i].list = NULL; > > +odp_timer_t odp_timer_alloc(odp_timer_pool_t tpid, > > + odp_queue_t queue, > > + void *user_ptr) > > +{ > > + /* We check this because ODP_QUEUE_INVALID is used */ > > + /* to indicate a free timer */ > > + if (odp_unlikely(queue == ODP_QUEUE_INVALID)) { > > + ODP_ERR("%s: Invalid queue identifier\n", tpid->name); > > + abort(); > > } > > - > > - timer->active = 1; > > - odp_sync_stores(); > > - > > - timer_start(timer); > > - > > - return timer_hdl; > > + odp_buffer_t tmo_buf = odp_buffer_alloc(tpid->buf_pool); > > + if (odp_likely(tmo_buf != ODP_BUFFER_INVALID)) { > > + odp_timer *tim = timer_alloc(tpid, queue, > > user_ptr, tmo_buf); > > + if (tim != ODP_TIMER_INVALID) { > > + /* Success */ > > + assert(tim->queue != ODP_QUEUE_INVALID); > > + return tim; > > + } > > + odp_buffer_free(tmo_buf); > > + } > > + /* Else failed to allocate timeout event */ > > + /* errno set by odp_buffer_alloc() or timer_alloc () */ > > + return ODP_TIMER_INVALID; > > } > > > > -odp_timer_tmo_t odp_timer_absolute_tmo(odp_timer_t timer_hdl, > > uint64_t tmo_tick, > > - odp_queue_t queue, > > odp_buffer_t buf) > > +void odp_timer_free(odp_timer_t tim) > > { > > - int id; > > - uint64_t tick; > > - uint64_t cur_tick; > > - timeout_t *new_tmo; > > - odp_buffer_t tmo_buf; > > - odp_timeout_hdr_t *tmo_hdr; > > - timer_ring_t *timer; > > - > > - id = timer_hdl - 1; > > - timer = &odp_timer.timer[id]; > > - > > - cur_tick = timer->cur_tick; > > - if (tmo_tick <= cur_tick) { > > - ODP_DBG("timeout too close\n"); > > - return ODP_TIMER_TMO_INVALID; > > + if (odp_unlikely(tim->queue == ODP_QUEUE_INVALID)) { > > + ODP_ERR("Invalid timer %p\n", tim); > > + abort(); > > } > > + odp_timer_pool *tp = (odp_timer_pool *)get_pq(&tim->pqelem); > > + timer_free(tp, tim); > > +} > > > > - tick = tmo_tick - cur_tick; > > - if (tick > MAX_TICKS) { > > - ODP_DBG("timeout too far\n"); > > - return ODP_TIMER_TMO_INVALID; > > +void odp_timer_set_abs_w_buf(odp_timer_t tim, > > + odp_timer_tick_t abs_tck, > > + odp_buffer_t user_buf) > > +{ > > + if (odp_unlikely(tim->queue == ODP_QUEUE_INVALID)) { > > + ODP_ERR("Invalid timer %p\n", tim); > > + abort(); > > } > > + odp_timer_pool *tp = (odp_timer_pool *)get_pq(&tim->pqelem); > > + timer_reset_w_buf(tp, tim, abs_tck, user_buf); > > +} > > > > - tick = (cur_tick + tick) % MAX_TICKS; > > - > > - tmo_buf = odp_buffer_alloc(timer->pool); > > - if (tmo_buf == ODP_BUFFER_INVALID) { > > - ODP_DBG("alloc failed\n"); > > - return ODP_TIMER_TMO_INVALID; > > +void odp_timer_set_abs(odp_timer_t tim, odp_timer_tick_t abs_tck) > > +{ > > + if (odp_unlikely(tim->queue == ODP_QUEUE_INVALID)) { > > + ODP_ERR("Invalid timer %p\n", tim); > > + abort(); > > } > > - > > - tmo_hdr = odp_timeout_hdr((odp_timeout_t) tmo_buf); > > - new_tmo = &tmo_hdr->meta; > > - > > - 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(&timer->tick[tick], new_tmo); > > - > > - return tmo_buf; > > + odp_timer_pool *tp = (odp_timer_pool *)get_pq(&tim->pqelem); > > + timer_reset(tp, tim, abs_tck); > > } > > > > -uint64_t odp_timer_tick_to_ns(odp_timer_t timer_hdl, uint64_t ticks) > > +void odp_timer_set_rel(odp_timer_t tim, odp_timer_tick_t rel_tck) > > { > > - uint32_t id; > > - > > - id = timer_hdl - 1; > > - return ticks * odp_timer.timer[id].resolution_ns; > > + if (odp_unlikely(tim->queue == ODP_QUEUE_INVALID)) { > > + ODP_ERR("Invalid timer %p\n", tim); > > + abort(); > > + } > > + odp_timer_pool *tp = (odp_timer_pool *)get_pq(&tim->pqelem); > > + timer_reset(tp, tim, tp->tick + rel_tck); > > } > > > > -uint64_t odp_timer_ns_to_tick(odp_timer_t timer_hdl, uint64_t ns) > > +void odp_timer_cancel(odp_timer_t tim) > > { > > - uint32_t id; > > - > > - id = timer_hdl - 1; > > - return ns / odp_timer.timer[id].resolution_ns; > > + if (odp_unlikely(tim->queue == ODP_QUEUE_INVALID)) { > > + ODP_ERR("Invalid timer %p\n", tim); > > + abort(); > > + } > > + odp_timer_pool *tp = (odp_timer_pool *)get_pq(&tim->pqelem); > > + timer_cancel(tp, tim); > > } > > > > -uint64_t odp_timer_resolution(odp_timer_t timer_hdl) > > +odp_timer_tmo_status_t odp_timer_tmo_status(odp_timer_tmo_t tmo_buf) > > { > > - uint32_t id; > > + odp_timeout_hdr_t *tmo_hdr = > > + (odp_timeout_hdr_t *)odp_buf_to_hdr(tmo_buf); > > + odp_timer *tim = tmo_hdr->timer; > > + > > + /* Make sure stores to '*tim' are visible */ > > + odp_sync_stores(); > > > > - id = timer_hdl - 1; > > - return odp_timer.timer[id].resolution_ns; > > + /* Compare generation count (gc) of timeout and parent > > timer (if any)*/ > > + if (odp_unlikely(tim == ODP_TIMER_INVALID || > > + tmo_hdr->gc != tim->gc)) { > > + /* Generation counters differ => timeout is > > orphaned */ > > + return ODP_TMO_ORPHAN; > > + } > > + /* Else gen-cnts match => parent timer exists */ > > + > > + /* Return timeout to timer so that it can be delivered > > again */ > > + tim->tmo_buf = tmo_buf; > > + /* FIXME do we need some kind of synchronisation or > > locking here? */ > > + > > + /* Compare tags of timeout and parent timer */ > > + /* Compare requested and actual timeout time */ > > + if (odp_likely(tim->tag == tmo_hdr->tag && > > + tim->req_tmo <= tmo_hdr->expiration)) { > > + /* Tags match, actual timeout is after requested > > => good! */ > > + return ODP_TMO_FRESH; > > + } else { > > + /* Tags don't match or actual timeout time is > > before */ > > + /* requested */ > > + /* Timer has been reset or cancelled and timeout > > is stale */ > > + /* or timeout expired too early */ > > + if (tim->req_tmo != INVALID_PRIORITY) { > > + /* Reset the timer for requested timeout */ > > + odp_timer_set_abs(tim, tim->req_tmo); > > + } > > + /* Else timer was cancelled, do nothing */ > > + return ODP_TMO_STALE; > > + } > > } > > > > -uint64_t odp_timer_maximum_tmo(odp_timer_t timer_hdl) > > +odp_timer_t odp_timer_get_handle(odp_timer_tmo_t tmo_buf) > > { > > - uint32_t id; > > - > > - id = timer_hdl - 1; > > - return odp_timer.timer[id].max_ticks; > > + odp_timeout_hdr_t *tmo_hdr = > > + (odp_timeout_hdr_t *)odp_buf_to_hdr(tmo_buf); > > + odp_timer_t tim = tmo_hdr->timer; > > + if (odp_likely(tim != ODP_TIMER_INVALID && tmo_hdr->gc == > > tim->gc)) > > + return tim; > > + else > > + return ODP_TIMER_INVALID; > > } > > > > -uint64_t odp_timer_current_tick(odp_timer_t timer_hdl) > > +odp_timer_tick_t odp_timer_get_expiry(odp_timer_tmo_t tmo_buf) > > { > > - uint32_t id; > > - > > - id = timer_hdl - 1; > > - return odp_timer.timer[id].cur_tick; > > + odp_timeout_hdr_t *tmo_hdr = > > + (odp_timeout_hdr_t *)odp_buf_to_hdr(tmo_buf); > > + return tmo_hdr->expiration; > > } > > > > -odp_timeout_t odp_timeout_from_buffer(odp_buffer_t buf) > > +void *odp_timer_get_userptr(odp_timer_tmo_t tmo_buf) > > { > > - return (odp_timeout_t) buf; > > + odp_timeout_hdr_t *tmo_hdr = > > + (odp_timeout_hdr_t *)odp_buf_to_hdr(tmo_buf); > > + return tmo_hdr->user_ptr; > > } > > > > -uint64_t odp_timeout_tick(odp_timeout_t tmo) > > +int odp_timer_init_global(void) > > { > > - odp_timeout_hdr_t *tmo_hdr = odp_timeout_hdr(tmo); > > - return tmo_hdr->meta.tmo_tick; > > + return 0; > > } > > diff --git a/platform/linux-generic/priority_queue.c > > b/platform/linux-generic/priority_queue.c > > new file mode 100644 > > index 0000000..ba4ba0e > > --- /dev/null > > +++ b/platform/linux-generic/priority_queue.c > > @@ -0,0 +1,289 @@ > > +#define NDEBUG /* Enabled by default by ODP build system */ > > +#include <assert.h> > > +#include <unistd.h> > > +#include <stdlib.h> > > +#include <string.h> > > +#include <strings.h> > > +#include <odp_hints.h> > > +#include <odp_align.h> > > +#include <odp_debug.h> > > + > > +#include "priority_queue.h" > > + > > + > > +#define NUM_CHILDREN 4 > > +#define CHILD(n) (NUM_CHILDREN * (n) + 1) > > +#define PARENT(n) (((n) - 1) / NUM_CHILDREN) > > + > > +/* Internal nodes in the array */ > > +typedef struct heap_node { > > + pq_element *elem; > > + /* Copy of elem->prio so we avoid unnecessary dereferencing */ > > + pq_priority_t prio; > > +} heap_node; > > + > > +static void pq_assert_heap(priority_queue *this); > > + > > +#define ALIGNMENT(p) (1U << ((unsigned)ffs((int)p) - 1U)) > > + > > +void priority_queue_con(priority_queue *this, uint32_t _max_elems) > > +{ > > + this->max_elems = _max_elems; > > + this->reg_elems = 0; > > + this->num_elems = 0; > > + this->org_ptr = malloc((_max_elems + 64 / sizeof(heap_node)) * > > + sizeof(heap_node)); > > + if (odp_unlikely(this->org_ptr == NULL)) { > > + ODP_ERR("malloc failed\n"); > > + abort(); > > + } > > + this->heap = this->org_ptr; > > + assert((size_t)&this->heap[1] % 8 == 0); > > + /* Increment base address until first child (index 1) is > > cache line */ > > + /* aligned and thus all children (e.g. index 1-4) stored > > in the */ > > + /* same cache line. We are not interested in the alignment > > of */ > > + /* heap[0] as this is a lone node */ > > + while ((size_t)&this->heap[1] % ODP_CACHE_LINE_SIZE != 0) { > > + /* Cast to ptr to struct member with the greatest > > alignment */ > > + /* requirement */ > > + this->heap = (heap_node *)((pq_priority_t > > *)this->heap + 1); > > + } > > +#if 0 > > + printf("Alignment of heap[1]=%u\n", > > ALIGNMENT((size_t)&heap[1])); > > + printf("Alignment of heap[CHILD(1)]=%u\n", > > + ALIGNMENT((size_t)&heap[CHILD(1)])); > > +#endif > > + pq_assert_heap(this); > > +} > > + > > +void priority_queue_des(priority_queue *this) > > +{ > > + pq_assert_heap(this); > > + free(this->org_ptr); > > +} > > + > > +#ifndef NDEBUG > > +static uint32_t > > +pq_assert_elem(priority_queue *this, uint32_t index, bool recurse) > > +{ > > + uint32_t num = 1; > > + const pq_element *elem = this->heap[index].elem; > > + assert(elem->index == index); > > + assert(elem->prio == this->heap[index].prio); > > + uint32_t child = CHILD(index); > > + uint32_t i; > > + for (i = 0; i < NUM_CHILDREN; i++, child++) { > > + if (valid_index(this, child)) { > > + assert(this->heap[child].elem != NULL); > > + assert(this->heap[child].prio >= elem->prio); > > + if (recurse) > > + num += pq_assert_elem(this, child, > > recurse); > > + } > > + } > > + return num; > > +} > > +#endif > > + > > +static void > > +pq_assert_heap(priority_queue *this) > > +{ > > + (void)this; > > +#ifndef NDEBUG > > + uint32_t num = 0; > > + if (odp_likely(this->num_elems != 0)) { > > + assert(this->heap[0].elem != NULL); > > + num += pq_assert_elem(this, 0, true); > > + } > > + assert(num == this->num_elems); > > + unsigned i; > > + for (i = 0; i < this->num_elems; i++) { > > + assert(this->heap[i].elem != NULL); > > + assert(this->heap[i].prio != INVALID_PRIORITY); > > + } > > +#endif > > +} > > + > > +/* Bubble up to proper position */ > > +void > > +pq_bubble_up(priority_queue *this, pq_element *elem) > > +{ > > + assert(this->heap[elem->index].elem == elem); > > + assert(this->heap[elem->index].prio == elem->prio); > > + uint32_t current = elem->index; > > + pq_priority_t prio = elem->prio; > > + assert(current == 0 || this->heap[PARENT(current)].elem != > > NULL); > > + /* Move up into proper position */ > > + while (current != 0 && this->heap[PARENT(current)].prio > > > prio) { > > + uint32_t parent = PARENT(current); > > + assert(this->heap[parent].elem != NULL); > > + /* Swap current with parent */ > > + /* 1) Move parent down */ > > + this->heap[current].elem = this->heap[parent].elem; > > + this->heap[current].prio = this->heap[parent].prio; > > + this->heap[current].elem->index = current; > > + /* 2) Move current up to parent */ > > + this->heap[parent].elem = elem; > > + this->heap[parent].prio = prio; > > + this->heap[parent].elem->index = parent; > > + /* Continue moving elem until it is in the right > > place */ > > + current = parent; > > + } > > + pq_assert_heap(this); > > +} > > + > > +/* Find the smallest child that is smaller than the specified > > priority */ > > +/* TODO very hot function! */ > > +uint32_t pq_smallest_child(priority_queue *this, > > + uint32_t index, > > + pq_priority_t val) > > +{ > > + uint32_t smallest = index; > > + uint32_t child = CHILD(index); > > +#if 1 > > + /* Unroll loop when all children exist */ > > + if (odp_likely(valid_index(this, child + 3))) { > > + if (this->heap[child + 0].prio < val) > > + val = this->heap[smallest = child + 0].prio; > > + if (this->heap[child + 1].prio < val) > > + val = this->heap[smallest = child + 1].prio; > > + if (this->heap[child + 2].prio < val) > > + val = this->heap[smallest = child + 2].prio; > > + if (this->heap[child + 3].prio < val) > > + val = this->heap[smallest = child + 3].prio; > > + return smallest; > > + } > > +#endif > > + uint32_t i; > > + for (i = 0; i < NUM_CHILDREN; i++) { > > + if (odp_unlikely(!valid_index(this, child + i))) > > + break; > > + if (this->heap[child + i].prio < val) { > > + smallest = child + i; > > + val = this->heap[smallest].prio; > > + } > > + } > > + return smallest; > > +} > > + > > +/* TODO very hot function, can it be optimised? */ > > +void > > +pq_bubble_down(priority_queue *this, pq_element *elem) > > +{ > > + assert(this->heap[elem->index].elem == elem); > > + assert(this->heap[elem->index].prio == elem->prio); > > + uint32_t current = elem->index; > > + pq_priority_t prio = elem->prio; > > + for (;;) { > > + uint32_t child = pq_smallest_child(this, current, > > prio); > > + if (current == child) { > > + /* No smaller child, we are done */ > > + pq_assert_heap(this); > > + return; > > + } > > + /* Element larger than smaller child, must move > > down */ > > + assert(this->heap[child].elem != NULL); > > + /* 1) Move child up to current */ > > + this->heap[current].elem = this->heap[child].elem; > > + this->heap[current].prio = this->heap[child].prio; > > + /* 2) Move current down to child */ > > + this->heap[child].elem = elem; > > + this->heap[child].prio = prio; > > + this->heap[child].elem->index = child; > > + > > + this->heap[current].elem->index = current; /* > > cache misses! */ > > + /* Continue moving element until it is in the > > right place */ > > + current = child; > > + } > > +} > > + > > +bool > > +pq_register_element(priority_queue *this, pq_element *elem) > > +{ > > + if (odp_likely(this->reg_elems < this->max_elems)) { > > + elem->pq = this; > > + this->reg_elems++; > > + return true; > > + } > > + return false; > > +} > > + > > +void > > +pq_unregister_element(priority_queue *this, pq_element *elem) > > +{ > > + assert(elem->pq == this); > > + if (is_active(elem)) > > + pq_deactivate_element(this, elem); > > + elem->pq = NULL; > > + this->reg_elems--; > > +} > > + > > +void > > +pq_activate_element(priority_queue *this, pq_element *elem, > > pq_priority_t prio) > > +{ > > + assert(elem->pq == this); > > + /* Insert element at end */ > > + uint32_t index = this->num_elems++; > > + this->heap[index].elem = elem; > > + this->heap[index].prio = prio; > > + elem->index = index; > > + elem->prio = prio; > > + pq_bubble_up(this, elem); > > +} > > + > > +void > > +pq_deactivate_element(priority_queue *this, pq_element *elem) > > +{ > > + assert(elem->pq == this); > > + if (odp_likely(is_active(elem))) { > > + /* Swap element with last element */ > > + uint32_t current = elem->index; > > + uint32_t last = --this->num_elems; > > + if (odp_likely(last != current)) { > > + /* Move last element to current */ > > + this->heap[current].elem = > > this->heap[last].elem; > > + this->heap[current].prio = > > this->heap[last].prio; > > + this->heap[current].elem->index = current; > > + /* Bubble down old 'last' element to its > > proper place*/ > > + if (this->heap[current].prio < elem->prio) > > + pq_bubble_up(this, > > this->heap[current].elem); > > + else > > + pq_bubble_down(this, > > this->heap[current].elem); > > + } > > + elem->index = INVALID_INDEX; > > + pq_assert_heap(this); > > + } > > +} > > + > > +void > > +pq_reset_element(priority_queue *this, pq_element *elem, > > pq_priority_t prio) > > +{ > > + assert(prio != INVALID_PRIORITY); > > + if (odp_likely(is_active(elem))) { > > + assert(prio >= elem->prio); > > + elem->prio = prio; > > + this->heap[elem->index].prio = prio;/* cache > > misses here! */ > > + pq_bubble_down(this, elem); > > + pq_assert_heap(this); > > + } else { > > + pq_activate_element(this, elem, prio); > > + } > > +} > > + > > +pq_priority_t pq_first_priority(const priority_queue *this) > > +{ > > + return this->num_elems != 0 ? this->heap[0].prio : > > INVALID_PRIORITY; > > +} > > + > > +pq_element * > > +pq_release_element(priority_queue *this, pq_priority_t threshold) > > +{ > > + if (odp_likely(this->num_elems != 0 && > > + this->heap[0].prio <= threshold)) { > > + pq_element *elem = this->heap[0].elem; > > + /* Remove element from heap */ > > + pq_deactivate_element(this, elem); > > + assert(elem->prio <= threshold); > > + return elem; > > + } > > + return NULL; > > +} > > diff --git a/platform/linux-generic/priority_queue.h > > b/platform/linux-generic/priority_queue.h > > new file mode 100644 > > index 0000000..c461590 > > --- /dev/null > > +++ b/platform/linux-generic/priority_queue.h > > @@ -0,0 +1,107 @@ > > +#ifndef _PRIORITY_QUEUE_H > > +#define _PRIORITY_QUEUE_H > > + > > +#include <assert.h> > > +#include <stddef.h> > > +#include <stdint.h> > > +#include <stdbool.h> > > + > > +#define INVALID_INDEX ~0U > > +#define INVALID_PRIORITY ((pq_priority_t)~0ULL) > > + > > +typedef uint64_t pq_priority_t; > > + > > +struct heap_node; > > + > > +typedef struct priority_queue { > > + uint32_t max_elems;/* Number of elements in heap */ > > + /* Number of registered elements (active + inactive) */ > > + uint32_t reg_elems; > > + uint32_t num_elems;/* Number of active elements */ > > + struct heap_node *heap; > > + struct heap_node *org_ptr; > > +} priority_queue; > > + > > +/* The user gets a pointer to this structure */ > > +typedef struct { > > + /* Set when pq_element registered with priority queue */ > > + priority_queue *pq; > > + uint32_t index;/* Index into heap array */ > > + pq_priority_t prio; > > +} pq_element; > > + > > +/*** Operations on pq_element ***/ > > + > > +static inline void pq_element_con(pq_element *this) > > +{ > > + this->pq = NULL; > > + this->index = INVALID_INDEX; > > + this->prio = 0U; > > +} > > + > > +static inline void pq_element_des(pq_element *this) > > +{ > > + (void)this; > > + assert(this->index == INVALID_INDEX); > > +} > > + > > +static inline priority_queue *get_pq(const pq_element *this) > > +{ > > + return this->pq; > > +} > > + > > +static inline pq_priority_t get_prio(const pq_element *this) > > +{ > > + return this->prio; > > +} > > + > > +static inline uint32_t get_index(const pq_element *this) > > +{ > > + return this->index; > > +} > > + > > +static inline bool is_active(const pq_element *this) > > +{ > > + return this->index != INVALID_INDEX; > > +} > > + > > +/*** Operations on priority_queue ***/ > > + > > +extern uint32_t pq_smallest_child(priority_queue *, uint32_t, > > pq_priority_t); > > +extern void pq_bubble_down(priority_queue *, pq_element *); > > +extern void pq_bubble_up(priority_queue *, pq_element *); > > + > > +static inline bool valid_index(priority_queue *this, uint32_t idx) > > +{ > > + return idx < this->num_elems; > > +} > > + > > +extern void priority_queue_con(priority_queue *, uint32_t > > _max_elems); > > +extern void priority_queue_des(priority_queue *); > > + > > +/* Register pq_element with priority queue */ > > +/* Return false if priority queue full */ > > +extern bool pq_register_element(priority_queue *, pq_element *); > > + > > +/* Activate and add pq_element to priority queue */ > > +/* Element must be disarmed */ > > +extern void pq_activate_element(priority_queue *, pq_element *, > > pq_priority_t); > > + > > +/* Reset (increase) priority for pq_element */ > > +/* Element may be active or inactive (released) */ > > +extern void pq_reset_element(priority_queue *, pq_element *, > > pq_priority_t); > > + > > +/* Deactivate and remove element from priority queue */ > > +/* Element may be active or inactive (released) */ > > +extern void pq_deactivate_element(priority_queue *, pq_element *); > > + > > +/* Unregister pq_element */ > > +extern void pq_unregister_element(priority_queue *, pq_element *); > > + > > +/* Return priority of first element (lowest numerical value) */ > > +extern pq_priority_t pq_first_priority(const priority_queue *); > > + > > +/* Deactivate and return first element if it's prio is <= > > threshold */ > > +extern pq_element *pq_release_element(priority_queue *, > > pq_priority_t thresh); > > + > > +#endif /* _PRIORITY_QUEUE_H */ > > diff --git a/test/api_test/odp_timer_ping.c > > b/test/api_test/odp_timer_ping.c > > index c1cc255..c4332e3 100644 > > --- a/test/api_test/odp_timer_ping.c > > +++ b/test/api_test/odp_timer_ping.c > > @@ -20,6 +20,7 @@ > > * Otherwise timeout may happen bcz of slow nw speed > > */ > > > > +#include <stdlib.h> > > #include <unistd.h> > > #include <fcntl.h> > > #include <errno.h> > > @@ -43,7 +44,8 @@ > > #define PING_CNT 10 > > #define PING_THRD 2 /* Send and Rx Ping thread */ > > > > -static odp_timer_t test_timer_ping; > > +static odp_timer_pool_t tp; > > +static odp_timer_t test_timer_ping = ODP_TIMER_INVALID; > > static odp_timer_tmo_t test_ping_tmo; > > > > #define PKTSIZE 64 > > @@ -123,15 +125,7 @@ static int listen_to_pingack(void) > > (socklen_t *)&len); > > if (bytes > 0) { > > /* pkt rxvd therefore cancel the > > timeout */ > > - if > > (odp_timer_cancel_tmo(test_timer_ping, > > - test_ping_tmo) != 0) { > > - ODP_ERR("cancel_tmo failed > > ..exiting listner thread\n"); > > - /* avoid exiting from here > > even if tmo > > - * failed for current ping, > > - * allow subsequent > > ping_rx request */ > > - err = -1; > > - > > - } > > + odp_timer_cancel(test_timer_ping); > > /* cruel bad hack used for sender, > > listner ipc.. > > * euwww.. FIXME .. > > */ > > @@ -153,7 +147,7 @@ static int send_ping_request(struct > > sockaddr_in *addr) > > int sd, cnt = 1; > > struct packet pckt; > > > > - uint64_t tick; > > + odp_timer_tick_t tick; > > odp_queue_t queue; > > odp_buffer_t buf; > > > > @@ -179,6 +173,12 @@ static int send_ping_request(struct > > sockaddr_in *addr) > > > > /* get the ping queue */ > > queue = odp_queue_lookup("ping_timer_queue"); > > + test_timer_ping = odp_timer_alloc(tp, queue, NULL); > > + if (test_timer_ping == ODP_TIMER_INVALID) { > > + ODP_ERR("Failed to allocate timer.\n"); > > + err = -1; > > + goto err; > > + } > > > > for (i = 0; i < PING_CNT; i++) { > > /* prepare icmp pkt */ > > @@ -204,12 +204,10 @@ static int send_ping_request(struct > > sockaddr_in *addr) > > printf(" icmp_sent msg_cnt %d\n", i); > > > > /* arm the timer */ > > - tick = odp_timer_current_tick(test_timer_ping); > > + tick = odp_timer_current_tick(tp); > > > > tick += 1000; > > - test_ping_tmo = > > odp_timer_absolute_tmo(test_timer_ping, tick, > > - queue, > > - ODP_BUFFER_INVALID); > > + odp_timer_set_abs(test_timer_ping, tick); > > /* wait for timeout event */ > > while ((buf = odp_queue_deq(queue)) == > > ODP_BUFFER_INVALID) { > > /* flag true means ack rxvd.. a cruel hack > > as I > > @@ -225,16 +223,24 @@ static int send_ping_request(struct > > sockaddr_in *addr) > > } > > } > > > > - /* free tmo_buf for timeout case */ > > - if (buf != ODP_BUFFER_INVALID) { > > + switch (odp_timer_tmo_status(buf)) { > > + case ODP_TMO_FRESH: > > ODP_DBG(" timeout msg_cnt [%i] \n", i); > > /* so to avoid seg fault commented */ > > - odp_buffer_free(buf); > > err = -1; > > + break; > > + case ODP_TMO_STALE: > > + /* Ignore stale timeouts */ > > + break; > > + case ODP_TMO_ORPHAN: > > + ODP_ERR("Received orphaned timeout!\n"); > > + abort(); > > } > > } > > > > err: > > + if (test_timer_ping != ODP_TIMER_INVALID) > > + odp_timer_free(test_timer_ping); > > return err; > > } > > > > @@ -335,7 +341,7 @@ int main(int argc ODP_UNUSED, char *argv[] > > ODP_UNUSED) > > ODP_CACHE_LINE_SIZE, > > ODP_BUFFER_TYPE_RAW); > > if (pool == ODP_BUFFER_POOL_INVALID) { > > - ODP_ERR("Pool create failed.\n"); > > + ODP_ERR("Buffer pool create failed.\n"); > > return -1; > > } > > > > @@ -350,8 +356,18 @@ int main(int argc ODP_UNUSED, char *argv[] > > ODP_UNUSED) > > return -1; > > } > > > > - test_timer_ping = odp_timer_create("ping_timer", pool, > > - 1000000, 1000000, > > 1000000000000UL); > > + /* > > + * Create timer pool > > + */ > > + tp = odp_timer_pool_create("timer_pool", pool, > > + 1000000U, /* 1 millisecond */ > > + 10000000000U, /* 10 seconds */ > > + 1, false, ODP_CLOCK_DEFAULT); > > + if (tp == ODP_TIMER_POOL_INVALID) { > > + ODP_ERR("Timer pool create failed.\n"); > > + return -1; > > + } > > + > > odp_shm_print_all(); > > > > pingarg.thrdarg.testcase = ODP_TIMER_PING_TEST; > > -- > > 1.9.1 > > > > > > > > > >_______________________________________________ > >lng-odp mailing list > >[email protected] > >http://lists.linaro.org/mailman/listinfo/lng-odp > > > _______________________________________________ > lng-odp mailing list > [email protected] > http://lists.linaro.org/mailman/listinfo/lng-odp -- Anders Roxell [email protected] M: +46 709 71 42 85 | IRC: roxell _______________________________________________ lng-odp mailing list [email protected] http://lists.linaro.org/mailman/listinfo/lng-odp
