diff options
Diffstat (limited to 'src/sandglass.c')
-rw-r--r-- | src/sandglass.c | 249 |
1 files changed, 249 insertions, 0 deletions
diff --git a/src/sandglass.c b/src/sandglass.c index 4acf983..57729f9 100644 --- a/src/sandglass.c +++ b/src/sandglass.c @@ -18,4 +18,253 @@ * <http://www.gnu.org/licenses/>. * *************************************************************************/ +#include "sandglass_impl.h" #include "sandglass.h" +#include <unistd.h> +#include <time.h> +#include <errno.h> + +static int sandglass_real_create(sandglass_t *sandglass, + const sandglass_attributes_t *attr); + +/* Create a timer */ +int +sandglass_create(sandglass_t *sandglass, + const sandglass_attributes_t *min, + const sandglass_attributes_t *max) +{ + sandglass_attributes_t realmin, realmax; + + /* Get our real min and max values */ + + if (min) + realmin = *min; + else { + /* min defaults to { SANDGLASS_INTROSPECTIVE, SANDGLASS_SYSTEM } */ + realmin.incrementation = SANDGLASS_INTROSPECTIVE; + realmin.resolution = SANDGLASS_SYSTEM; + } + + if (max) + realmax = *max; + else { + /* max defaults to the greater of min and { SANDGLASS_INTROSPECTIVE, + SANDGLASS_CPUTIME } */ + if (realmin.incrementation > SANDGLASS_INTROSPECTIVE + || (realmin.incrementation == SANDGLASS_INTROSPECTIVE + && realmin.resolution > SANDGLASS_CPUTIME)) + realmax = realmin; + else { + realmax.incrementation = SANDGLASS_INTROSPECTIVE; + realmax.resolution = SANDGLASS_CPUTIME; + } + } + + /* Ensure max >= min */ + if (realmax.incrementation < realmin.incrementation + || (realmax.incrementation == realmin.incrementation + && realmax.resolution < realmin.resolution)) + { + errno = EINVAL; + return -1; + } + + /* Now search for available timers, starting from max */ + + while (sandglass_real_create(sandglass, &realmax) != 0) { + /* Once we reach the minimum attributes, bail out */ + if (realmax.incrementation == realmin.incrementation + && realmax.resolution == realmin.resolution) + { + errno = ENOTSUP; + return -1; + } + + /* Try the next lowest allowable settings */ + if (realmax.resolution) + --realmax.resolution; + else { + if (realmax.incrementation) { + --realmax.incrementation; + realmax.resolution = SANDGLASS_CPUTIME; + } else { + errno = ENOTSUP; + return -1; + } + } + } + + return 0; +} + +static int +sandglass_real_create(sandglass_t *sandglass, + const sandglass_attributes_t *attr) +{ + switch (attr->incrementation) { + case SANDGLASS_MONOTONIC: + switch (attr->resolution) { + case SANDGLASS_REALTICKS: +#ifdef SANDGLASS_TSC + sandglass->resolution = sandglass_tsc_resolution(); + sandglass->loops = sandglass_tsc_loops(); + break; +#else + return -1; +#endif + + case SANDGLASS_CPUTIME: +#ifdef SANDGLASS_TSC + sandglass->resolution = sandglass_tsc_resolution(); + sandglass->loops = 1; + break; +#else + return -1; +#endif + + case SANDGLASS_SYSTEM: + sandglass->resolution = 1e9; + sandglass->loops = 1; + break; + + default: + return -1; + } + break; + + case SANDGLASS_INTROSPECTIVE: + switch (attr->resolution) { + case SANDGLASS_REALTICKS: + /* No such thing as an introspective raw TSC */ + return -1; + + case SANDGLASS_CPUTIME: + if (sysconf(_SC_THREAD_CPUTIME) > 0 || sysconf(_SC_CPUTIME) > 0) { + sandglass->resolution = 1e9; + sandglass->loops = 1; + } else + return -1; + break; + + case SANDGLASS_SYSTEM: + sandglass->resolution = CLOCKS_PER_SEC; + sandglass->loops = 1; + break; + + default: + return -1; + } + break; + + default: + return -1; + } + + sandglass->attributes = *attr; + return 0; +} + +/* Store a timer value in sandglass->grains */ +static int sandglass_real_gettime(sandglass_t *sandglass); + +/* Start timing */ +int +sandglass_begin(sandglass_t *sandglass) +{ + return sandglass_real_gettime(sandglass); +} + +/* Finish timing */ +int +sandglass_elapse(sandglass_t *sandglass) +{ + long oldgrains = sandglass->grains; + + if (sandglass_real_gettime(sandglass) != 0) + return -1; + + sandglass->grains -= oldgrains; + sandglass->grains /= sandglass->loops; + + return 0; +} + +/* Store a timer value in sandglass->grains */ +static int +sandglass_real_gettime(sandglass_t *sandglass) +{ + struct timespec ts; + clock_t clock_ticks; + + switch (sandglass->attributes.incrementation) { + case SANDGLASS_MONOTONIC: + switch (sandglass->attributes.resolution) { + case SANDGLASS_REALTICKS: +#ifdef SANDGLASS_TSC + sandglass->grains = sandglass_get_tsc(); + break; +#else + return -1; +#endif + + case SANDGLASS_CPUTIME: +#ifdef SANDGLASS_TSC + sandglass->grains = sandglass_get_tsc(); + break; +#else + return -1; +#endif + + case SANDGLASS_SYSTEM: + if (sysconf(_SC_MONOTONIC_CLOCK) > 0) { + if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) + return -1; + sandglass->grains = ts.tv_nsec; + } else { + if (clock_gettime(CLOCK_REALTIME, &ts) != 0) + return -1; + sandglass->grains = ts.tv_nsec; + } + break; + + default: + return -1; + } + break; + + case SANDGLASS_INTROSPECTIVE: + switch (sandglass->attributes.resolution) { + case SANDGLASS_REALTICKS: + /* No such thing as an introspective raw TSC */ + return -1; + + case SANDGLASS_CPUTIME: + if (sysconf(_SC_THREAD_CPUTIME) > 0) { + if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts) != 0) + return -1; + sandglass->grains = ts.tv_nsec; + } else if (sysconf(_SC_CPUTIME) > 0) { + if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts) != 0) + return -1; + sandglass->grains = ts.tv_nsec; + } else + return -1; + break; + + case SANDGLASS_SYSTEM: + if ((clock_ticks = clock()) == -1) + return -1; + sandglass->grains = clock(); + break; + + default: + return -1; + } + break; + + default: + return -1; + } + + return 0; +} |