/* * Copyright (C) Internet Systems Consortium, Inc. ("ISC") * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * See the COPYRIGHT file distributed with this work for additional * information regarding copyright ownership. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #ifdef SYS_select #include #endif #ifdef SYS_poll #include #endif #ifdef SYS_kevent #include #endif #ifdef SYS_epoll_wait #include #endif #ifdef SYS_gettimeofday #define VIRTUAL_TIME #ifdef VIRTUAL_TIME static struct timeval epoch = { 0, 0 }; static int _init_called = 0; void _init(void) { (void)syscall(SYS_gettimeofday, &epoch, NULL); _init_called = 1; } static void absolute_inflate(struct timeval *vt, struct timeval *rt) { double d; rt->tv_sec = vt->tv_sec; rt->tv_usec = vt->tv_usec; if ((epoch.tv_sec > vt->tv_sec) || ((epoch.tv_sec == vt->tv_sec) && (epoch.tv_usec > vt->tv_usec))) return; rt->tv_sec -= epoch.tv_sec; rt->tv_usec -= epoch.tv_usec; while (rt->tv_usec < 0) { rt->tv_sec -= 1; rt->tv_usec += 1000000; } if (rt->tv_sec == 0) goto done; d = (double) (rt->tv_sec - 1); d += (double) rt->tv_usec / 1000000.; d = exp(d); rt->tv_sec = (time_t) d; d -= (double) rt->tv_sec; rt->tv_usec = (suseconds_t) (d * 1000000.); done: rt->tv_sec += epoch.tv_sec; rt->tv_usec += epoch.tv_usec; while (rt->tv_usec >= 1000000) { rt->tv_sec += 1; rt->tv_usec -= 1000000; } return; } static void absolute_deflate(struct timeval *rt, struct timeval *vt) { double d; vt->tv_sec = rt->tv_sec; vt->tv_usec = rt->tv_usec; if ((epoch.tv_sec > rt->tv_sec) || ((epoch.tv_sec == rt->tv_sec) && (epoch.tv_usec > rt->tv_usec))) return; vt->tv_sec -= epoch.tv_sec; vt->tv_usec -= epoch.tv_usec; while (vt->tv_usec < 0) { vt->tv_sec -= 1; vt->tv_usec += 1000000; } if (vt->tv_sec == 0) goto done; d = (double) vt->tv_sec; d += (double) vt->tv_usec / 1000000.; d = log(d); vt->tv_sec = (time_t) d; d -= (double) vt->tv_sec; vt->tv_sec += 1; vt->tv_usec = (suseconds_t) (d * 1000000.); done: vt->tv_sec += epoch.tv_sec; vt->tv_usec += epoch.tv_usec; while (vt->tv_usec >= 1000000) { vt->tv_sec += 1; vt->tv_usec -= 1000000; } return; } static void interval_inflate(struct timeval *vt, struct timeval *rt) { struct timeval now, tv; (void) gettimeofday(&now, NULL); absolute_deflate(&now, &tv); tv.tv_sec += vt->tv_sec; tv.tv_usec += vt->tv_usec; while (tv.tv_usec >= 1000000) { tv.tv_sec += 1; tv.tv_usec -= 1000000; } absolute_inflate(&tv, rt); rt->tv_sec -= now.tv_sec; rt->tv_usec -= now.tv_usec; if (rt->tv_usec < 0) { rt->tv_sec -= 1; rt->tv_usec += 1000000; } return; } static void interval_deflate(struct timeval *rt, struct timeval *vt) { struct timeval now, tv; vt->tv_sec = rt->tv_sec; vt->tv_usec = rt->tv_usec; if ((vt->tv_sec == 0) && (vt->tv_usec <= 10000)) return; (void) gettimeofday(&now, NULL); tv.tv_sec = now.tv_sec + rt->tv_sec; tv.tv_usec = now.tv_usec + rt->tv_usec; while (tv.tv_usec >= 1000000) { tv.tv_sec += 1; tv.tv_usec -= 1000000; } absolute_deflate(&now, &now); absolute_deflate(&tv, vt); vt->tv_sec -= now.tv_sec; vt->tv_usec -= now.tv_usec; while (vt->tv_usec < 0) { vt->tv_sec -= 1; vt->tv_usec += 1000000; } if ((vt->tv_sec == 0) && (vt->tv_usec < 10000)) vt->tv_usec = 10000; return; } #endif int gettimeofday(struct timeval *tv, struct timezone *tz) { #ifdef VIRTUAL_TIME struct timeval now; int ret; if (!_init_called) _init(); if (epoch.tv_sec == 0) return syscall(SYS_gettimeofday, tv, tz); ret = syscall(SYS_gettimeofday, &now, tz); if (ret == 0) absolute_inflate(&now, tv); return ret; #else return syscall(SYS_gettimeofday, tv, tz); #endif } #ifdef SYS_select int select(int nfds, fd_set *rfds, fd_set *wfds, fd_set *xfds, struct timeval *timeout) { #ifdef VIRTUAL_TIME struct timeval tv; if (!_init_called) _init(); if (epoch.tv_sec == 0 || timeout == NULL || (timeout->tv_sec == 0 && timeout->tv_usec == 0)) return syscall(SYS_select, nfds, rfds, wfds, xfds, timeout); interval_deflate(timeout, &tv); return syscall(SYS_select, nfds, rfds, wfds, xfds, &tv); #else return syscall(SYS_select, nfds, rfds, wfds, xfds, timeout); #endif } #endif #ifdef SYS_poll int poll(struct pollfd fds[], nfds_t nfds, int timeout) { #ifdef VIRTUAL_TIME struct timeval in, out; if (!_init_called) _init(); if (timeout <= 0 || epoch.tv_sec == 0) return syscall(SYS_poll, fds, nfds, timeout); in.tv_sec = timeout / 1000; in.tv_usec = (timeout % 1000) * 1000; interval_deflate(&in, &out); timeout = out.tv_sec * 1000 + out.tv_usec / 1000; return syscall(SYS_poll, fds, nfds, timeout); #else return syscall(SYS_poll, fds, nfds, timeout); #endif } #endif #ifdef SYS_kevent int kevent(int kq, struct kevent *changelist, int nchanges, struct kevent *eventlist, int nevents, const struct timespec *timeout) { #ifdef VIRTUAL_TIME struct timeval in, out; struct timespec ts; if (!_init_called) _init(); if (epoch.tv_sec == 0 || timeout == NULL || (timeout->tv_sec == 0 && timeout->tv_nsec == 0)) return syscall(SYS_kevent, kq, changelist, nchanges, eventlist, nevents, timeout); in.tv_sec = timeout->tv_sec; in.tv_usec = timeout->tv_nsec / 1000; interval_deflate(&in, &out); ts.tv_sec = out.tv_sec; ts.tv_nsec = out.tv_usec * 1000; return syscall(SYS_kevent, kq, changelist, nchanges, eventlist, nevents, &ts); #else return syscall(SYS_kevent, kq, changelist, nchanges, eventlist, nevents, timeout); #endif } #endif #ifdef SYS_epoll_wait int epoll_wait(int fd, struct epoll_event *events, int maxevents, int timeout) { #ifdef VIRTUAL_TIME struct timeval in, out; if (!_init_called) _init(); if (timeout == 0 || timeout == -1 || epoch.tv_sec == 0) return syscall(SYS_epoll_wait, fd, events, maxevents, timeout); in.tv_sec = timeout / 1000; in.tv_usec = (timeout % 1000) * 1000; interval_deflate(&in, &out); timeout = out.tv_sec * 1000 + out.tv_usec / 1000; return syscall(SYS_poll, fd, events, maxevents, timeout); #else return syscall(SYS_poll, fd, events, maxevents, timeout); #endif } #endif #endif