p99_clib.h 9.47 KB
Newer Older
(no author)'s avatar
(no author) committed
1 2
/* This may look like nonsense, but it really is -*- mode: C -*-              */
/*                                                                            */
3
/* Except for parts copied from previous work and as explicitly stated below, */
(no author)'s avatar
(no author) committed
4 5 6 7 8 9 10 11 12 13
/* the authors and copyright holders for this work are as follows:            */
/* (C) copyright  2012 Jens Gustedt, INRIA, France                            */
/* (C) copyright  2012 William Morris                                         */
/*                                                                            */
/* This file is free software; it is part of the P99 project.                 */
/* You can redistribute it and/or modify it under the terms of the QPL as     */
/* given in the file LICENSE. It is distributed without any warranty;         */
/* without even the implied warranty of merchantability or fitness for a      */
/* particular purpose.                                                        */
/*                                                                            */
14 15 16 17
#ifndef P99_CLIB_H
#define P99_CLIB_H 1

#include "p99_new.h"
18
#include "p99_callback.h"
Jens Gustedt's avatar
Jens Gustedt committed
19
#include "p99_tss.h"
20
#include <time.h>
21 22 23 24 25 26 27

/**
 ** @addtogroup C11_library C11 additions to the C library
 **
 ** @{
 **/

28 29 30 31
#if __STDC_VERSION__ >= 201112L
# define p00_has_feature_aligned_alloc 1
# define p00_has_extension_aligned_alloc 1
#elif (_XOPEN_SOURCE >= 600) || defined(P00_DOXYGEN)
32

33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
#define p00_has_feature_aligned_alloc 1
#define p00_has_extension_aligned_alloc 1

/**
 ** @brief allocation with a chosen alignment
 **
 ** @remark this implementation relies on the presence of @c posix_memalign from POSIX
 **/
p99_inline
void *aligned_alloc(size_t p00_alignment, size_t p00_size) {
  void * p00_ret = 0;
  int err = posix_memalign(&p00_ret, p00_alignment, p00_size);
  /* If there was an error and a fake pointer has been returned, free
     this pointer and set it to 0. This is the only way to return an
     error for this C11 interface. */
  if (err && p00_ret) {
    free(p00_ret);
    p00_ret = 0;
  }
  return p00_ret;
}

55 56 57 58 59 60
# endif /* XOPEN */


# define p00_has_feature_quick_exit 1
# define p00_has_extension_quick_exit 1

61 62
#if __STDC_VERSION__ < 201112L

63 64
/* In both cases this is guaranteed to do the correct
   initialization. */
65
P99_WEAK(p00_cb)
Jens Gustedt's avatar
Jens Gustedt committed
66
p99_callback_stack p00_at_quick_exit = P99_LIFO_INITIALIZER(0);
67

68
/**
69
 ** @brief registers the function pointed to by @a p00_void_func, to be called
70 71
 ** without arguments should ::quick_exit be called.
 **/
72
p99_inline
73
int at_quick_exit(void (*p00_void_func)(void)) {
74
  return !P99_CALLBACK_PUSH(&p00_at_quick_exit, p00_void_func);
75 76
}

77
/**
78
 ** @brief causes normal program termination.
79 80 81 82 83 84
 **
 ** No functions registered by the @c atexit function or signal
 ** handlers registered by the signal function are called.
 **
 ** @see at_quick_exit
 **/
85 86
p99_inline
_Noreturn void quick_exit(int status) {
87
  p99_callback(&p00_at_quick_exit);
88 89 90
  _Exit(status);
}

91 92
#endif /* C < C11 */

93 94
P99_SETJMP_INLINE(p00_run_at_thrd_exit)
void p00_run_at_thrd_exit(void * li) {
95
  p99_callback(li);
96 97
}

98
P99_TSS_DECLARE_LOCAL(p99_callback_stack, p00_at_thrd_exit, p00_run_at_thrd_exit);
99
# define P00_AT_THRD_EXIT P99_TSS_LOCAL(p00_at_thrd_exit)
100 101

/**
102
 ** @brief Add @a p00_void_func to the functions that are called on exit of
103 104 105 106 107 108
 ** the current thread.
 **
 ** This is an extension of the C11 thread functions and works
 ** analogously to the functions ::at_exit and ::at_quick exit.
 **/
p99_inline
109
int at_thrd_exit(void (*p00_void_func)(void)) {
110
  return !P99_CALLBACK_PUSH(&P00_AT_THRD_EXIT, p00_void_func);
111 112
}

113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309
/* Add rudimentary support for the timespec data structure */

/* All this fuss is needed because TIME_UTC isn't allowed to be 0.
   And this is because timespec_get returns failure as 0, and success
   by returning the time base, oh well. */

/* A fallback version if all else fails */

p99_inline
int p00_timespec_get(struct timespec *p00_ts, int p00_base) {
  struct timeval t;
  if (gettimeofday(&t, P99_0(struct timezone*))) {
    errno = 0;
    return 0;
  } else {
    p00_ts->tv_sec = t.tv_sec;
    /* ensure that the usec value is first converted to a nsec
       value of the correct width ... */
    p00_ts->tv_nsec = t.tv_usec;
    /* ... and do the multiplication within that width. */
    p00_ts->tv_nsec *= 1000;
    return p00_base;
  }
}

#ifndef TIME_UTC

/**
 ** @brief The ::timespec_get function sets the interval pointed to by
 ** @a p00_ts to hold the current calendar time based on the specified
 ** time base.
 **
 ** If @a p00_base is ::TIME_UTC, the @c tv_sec member is set to the
 ** number of seconds since an implementation defined epoch, truncated
 ** to a whole value and the @c tv_nsec member is set to the integral
 ** number of nanoseconds, rounded to the resolution of the system
 ** clock.
 **
 ** @return If the ::timespec_get function is successful it returns
 ** the nonzero value @a p00_base; otherwise, it returns zero.
 **
 ** @ingroup C11_library
 **/
p99_inline
int timespec_get(struct timespec *p00_ts, int p00_base);

enum {
  p00_time_base,
  p00_time_utc,
  p00_time_monotonic,
# ifdef CLOCK_PROCESS_CPUTIME_ID
  p00_time_process_cputime_id,
# endif
# ifdef CLOCK_THREAD_CPUTIME_ID
  p00_time_thread_cputime_id,
# endif
  p00_time_base_max
};

/**
 ** @brief expands to an integer constant greater than 0 that
 ** designates the UTC time base since an implementation defined epoch
 **
 ** This is the only time base that is guaranteed to be available by C11.
 **
 ** @see TIME_MONOTONIC
 **/
# define TIME_UTC p00_time_utc

# if defined(CLOCK_REALTIME) || defined(P00_DOXYGEN)

# if defined(CLOCK_MONOTONIC) || defined(P00_DOXYGEN)
/**
 ** @brief expands to an integer constant greater than 0 that
 ** designates a real time clock who's base is usually the boot time
 ** of the processor
 **
 ** If this is available, this denotes a clock that should be a bit
 ** more efficient than ::TIME_UTC, since it usually doesn't have to
 ** perform a call into the OS kernel but may query the processor
 ** directly.
 **
 ** Use this if available and if you are only interested in times
 ** relative to your program execution, e.g for benchmarks.
 **/
#  define TIME_MONOTONIC p00_time_monotonic

# endif
# ifdef CLOCK_PROCESS_CPUTIME_ID
#  define TIME_PROCESS_CPUTIME_ID p00_time_process_cputime_id
# endif
# ifdef CLOCK_THREAD_CPUTIME_ID
#  define TIME_THREAD_CPUTIME_ID p00_time_thread_cputime_id,
# endif

P99_CONST_FUNCTION
p99_inline
clockid_t p00_getclockid(int base) {
  return (base >= p00_time_base_max)
         ? CLOCK_REALTIME
  : (clockid_t const[]) {
    [p00_time_base] = CLOCK_REALTIME,
                      [p00_time_utc] = CLOCK_REALTIME,
# ifdef CLOCK_MONOTONIC
                                       [p00_time_monotonic] = CLOCK_MONOTONIC,
# endif
# ifdef CLOCK_PROCESS_CPUTIME_ID
                                           [p00_time_process_cputime_id] = CLOCK_PROCESS_CPUTIME_ID,
# endif
# ifdef CLOCK_THREAD_CPUTIME_ID
                                               [p00_time_thread_cputime_id] = CLOCK_THREAD_CPUTIME_ID,
# endif
  }[base];
}

/**
 ** @brief The ::timespec_get function sets the interval pointed to by
 ** @a p00_ts to hold the current calendar time based on the specified
 ** time base.
 **
 ** If @a p00_base is ::TIME_UTC, the @c tv_sec member is set to the
 ** number of seconds since an implementation defined epoch, truncated
 ** to a whole value and the @c tv_nsec member is set to the integral
 ** number of nanoseconds, rounded to the resolution of the system
 ** clock.
 **
 ** @return If the ::timespec_get function is successful it returns
 ** the nonzero value @a p00_base; otherwise, it returns zero.
 **/
p99_inline
int timespec_get(struct timespec *p00_ts, int p00_base) {
  clockid_t p00_clkid = p00_getclockid(p00_base);
  if (clock_gettime(p00_clkid, p00_ts)) {
    errno = 0;
    return 0;
  } else
    return p00_base;
}

# elif defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__)
# include <mach/mach_time.h>

# define TIME_MONOTONIC p00_time_monotonic

P99_WEAK(p00_timebase)
double p00_timebase;

P99_WEAK(p00_timeoff_)
struct timespec p00_timeoff_;

P99_WEAK(p00_timeoff)
struct timespec const*const p00_timeoff = &p00_timeoff_;

P99_WEAK(p00_timeonce)
once_flag p00_timeonce = ONCE_FLAG_INIT;

p99_inline
void p00_timeonce_init(void) {
  /* Calibrate the monotonic time */
  mach_timebase_info_data_t p00_tb = P99_INIT;
  mach_timebase_info(&p00_tb);
  p00_timebase = p00_tb.numer;
  p00_timebase /= p00_tb.denom;
  /* Compute the offset of the monotonic time compared to UTC */
  /* Nanosec since system start, or something similar. */
  uint64_t p00_nsec = mach_absolute_time() * p00_timebase;
  p00_timespec_get(&p00_timeoff_, TIME_UTC);
  uint64_t const p00_giga = UINT64_C(1000000000);
  uint64_t p00_epoch = p00_timeoff_.tv_sec * p00_giga + p00_timeoff_.tv_nsec;
  p00_epoch -= p00_nsec;
  p00_timeoff_.tv_sec = p00_epoch / p00_giga;
  p00_timeoff_.tv_nsec = p00_epoch % p00_giga;
}

p99_inline
int timespec_get(struct timespec *p00_ts, int p00_base) {
  call_once(&p00_timeonce, p00_timeonce_init);
  uint64_t p00_nsec = mach_absolute_time() * p00_timebase;
  register uint64_t const p00_giga = UINT64_C(1000000000);
  p00_ts->tv_sec = p00_nsec / p00_giga;
  p00_ts->tv_nsec = p00_nsec % p00_giga;
  if (p00_base != TIME_MONOTONIC) {
    p00_ts->tv_sec += p00_timeoff->tv_sec;
    p00_ts->tv_nsec += p00_timeoff->tv_nsec;
    while (p00_ts->tv_nsec >= p00_giga) {
      p00_ts->tv_nsec -= p00_giga;
      ++p00_ts->tv_sec;
    }
  }
  return p00_base;
}
# else
# warning only low resolution gettimeofday found
# define timespec_get p00_timespec_get
# endif
#endif

310 311 312 313 314
/**
 ** @}
 **/

#endif