#ifdef __cplusplus
extern "C" {
#endif
#include "ulib/clock.h"
#include "ulib/chacha.h"
#include "ulib/gettime.h"
#ifdef __cplusplus
}
#endif
#ifdef USE_WIN32_NATIVE
/* #define getpid() _getpid() */
#define ftruncate(a,b) _chsize(a,b)
/* typedef U32 mode_t; */
#endif
#undef open
#define state_fd UCXT.clock_state_fd
#define state_f UCXT.clock_state_f
#define adjustment UCXT.clock_adj
#define last UCXT.clock_last
#define clock_seq UCXT.clock_seq
#define myU2time UCXT.myU2time
#define prev_clock_reg UCXT.clock_prev_reg
#define defer_100ns UCXT.clock_defer_100ns
#define pathlen UCXT.clock_pathlen
/* called at boot */
void uu_clock_init(pUCXT) {
state_fd = -3;
state_f = NULL;
pathlen.path = NULL;
pathlen.len = 0;
adjustment = 0;
last.tv_sec = 0;
last.tv_usec = 0;
prev_clock_reg = 0;
defer_100ns = 0;
/* clock_seq uninit */
}
void uu_clock_getpath(pUCXT, struct_pathlen_t *sp) {
Copy(&pathlen, sp, 1, struct_pathlen_t);
}
void uu_clock_setpath(pUCXT, struct_pathlen_t *sp) {
if (pathlen.path)
Safefree(pathlen.path);
Copy(sp, &pathlen, 1, struct_pathlen_t);
if (state_fd >= 0)
fclose(state_f);
state_fd = -3;
}
/* returns 100ns intervals since unix epoch.
* since gettimeofday() is in 1usec intervals,
* last digit is simulated via adjustment.
*/
IV uu_clock(pUCXT, U64 *ret_clock_reg, U16 *ret_clock_seq) {
struct timeval tv;
mode_t save_umask;
int len;
UV ptod[2];
U64 clock_reg;
#ifdef HAVE_LSTAT
struct stat statbuf;
#endif
/* state_fd:
* -4 cannot create
* -3 untried
* -2 symlink
* -1 can create
* >=0 open
*/
if (state_fd == -3) {
#ifdef HAVE_LSTAT
if (lstat(pathlen.path, &statbuf) < 0) { /* this covers EINTR too.. ugh */
if (errno == ENOENT)
state_fd = -1;
else
state_fd = -4;
}
else if ((statbuf.st_mode & S_IFMT) == S_IFLNK) {
state_fd = -2;
}
else {
#endif
state_fd = open(pathlen.path, O_RDWR);
if (state_fd < 0 && errno == ENOENT)
state_fd = -1; /* can create */
else if (state_fd >= 0) {
#ifdef HAVE_LSTAT
state_f = NULL;
if ((lstat(pathlen.path, &statbuf) == 0) && ((statbuf.st_mode & S_IFMT) != S_IFLNK))
#endif
state_f = fdopen(state_fd, "r+");
if (!state_f) {
close(state_fd);
state_fd = -2;
}
}
#ifdef HAVE_LSTAT
}
#endif
}
if (state_fd >= 0) {
unsigned int cl;
unsigned long tv1, tv2;
int a;
rewind(state_f);
if (fscanf(state_f, "clock: %04x tv: %lu %lu adj: %d\n", &cl, &tv1, &tv2, &a) == 4) {
clock_seq = cl & 0x3fff;
last.tv_sec = tv1;
last.tv_usec = tv2;
adjustment = a;
}
}
/* gettimeofday(&tv, 0); */
(*myU2time)(aTHX_ (UV*)&ptod);
tv.tv_sec = (long)ptod[0];
tv.tv_usec = (long)ptod[1];
if ((last.tv_sec == 0) && (last.tv_usec == 0)) {
cc_rand16(aUCXT, &clock_seq);
clock_seq &= 0x3fff;
last.tv_sec = tv.tv_sec - 1;
last.tv_usec = tv.tv_usec;
}
if ((tv.tv_sec < last.tv_sec) || ((tv.tv_sec == last.tv_sec) && (tv.tv_usec < last.tv_usec))) {
clock_seq = (clock_seq+1) & 0x3fff;
adjustment = 0;
last = tv;
}
else if ((tv.tv_sec == last.tv_sec) && (tv.tv_usec == last.tv_usec)) {
if (adjustment >= MAX_ADJUSTMENT) {
clock_seq = (clock_seq+1) & 0x3fff;
adjustment = 0;
}
else {
adjustment++;
}
}
else {
adjustment = 0;
last = tv;
}
clock_reg = tv.tv_usec*10 + adjustment;
clock_reg += ((U64)tv.tv_sec)*10000000;
/* *clock_reg += (((U64)0x01b21dd2) << 32) + 0x13814000; */
if ((clock_reg - prev_clock_reg) >= defer_100ns) {
if (state_fd == -1) { /* can create */
#ifdef HAVE_LSTAT
if ((lstat(pathlen.path, &statbuf) == 0) && ((statbuf.st_mode & S_IFMT) == S_IFLNK))
state_fd = -2;
else {
#endif
save_umask = umask(0);
state_fd = open(pathlen.path, O_RDWR|O_CREAT, 0660);
if (state_fd < 0)
state_fd = -4;
(void) umask(save_umask);
if (state_fd >= 0) {
#ifdef HAVE_LSTAT
state_f = NULL;
if ((lstat(pathlen.path, &statbuf) == 0) && ((statbuf.st_mode & S_IFMT) != S_IFLNK))
#endif
state_f = fdopen(state_fd, "r+");
if (!state_f) {
close(state_fd);
state_fd = -2;
}
}
#ifdef HAVE_LSTAT
}
#endif
}
if (state_fd > 0) {
rewind(state_f);
len = fprintf(state_f,
"clock: %04x tv: %016lu %08lu adj: %08d\n",
clock_seq, (unsigned long)last.tv_sec,
(unsigned long)last.tv_usec, adjustment);
fflush(state_f);
if (ftruncate(state_fd, len) < 0) {
fprintf(state_f, " \n");
fflush(state_f);
}
rewind(state_f);
}
}
prev_clock_reg = clock_reg;
/* *clock_high = clock_reg >> 32; */
/* *clock_low = (U32)clock_reg; */
*ret_clock_reg = clock_reg;
*ret_clock_seq = clock_seq;
return 0;
}
/* ex:set ts=2 sw=2 itab=spaces: */