mirror of
https://github.com/wolfcw/libfaketime.git
synced 2026-05-17 08:36:28 +03:00
Implement a fix for pthread_cond_timedwait with faketime
pthread_cond_timedwait takes an absolute time as an argument, which the function directly passes on to the kernel via the futex syscall. In an application this absolute time argument is calculated via the fake times provided by libfaketime. Since the kernel has no knowledge of the fake time, pthread_cond_timedwait must be redefined such that it converts the fake time back to real time before passing it on.
This commit is contained in:
30
src/Makefile
30
src/Makefile
@@ -9,12 +9,9 @@
|
||||
# NO_ATFILE
|
||||
# - Disables support for the fstatat() group of functions
|
||||
#
|
||||
# PTHREAD
|
||||
# - Define this to enable multithreading support.
|
||||
#
|
||||
# PTHREAD_SINGLETHREADED_TIME
|
||||
# - Define this if you want to single-thread time() ... there ARE
|
||||
# possibile caching side-effects in a multithreaded environment
|
||||
# possible caching side-effects in a multithreaded environment
|
||||
# without this, but the performance impact may require you to
|
||||
# try it unsynchronized.
|
||||
#
|
||||
@@ -24,17 +21,20 @@
|
||||
# that make use of low-level system calls, such as Java Virtual
|
||||
# Machines.
|
||||
#
|
||||
# FAKE_SLEEP
|
||||
# - Also intercept sleep(), nanosleep(), usleep(), alarm(), [p]poll()
|
||||
# FAKE_SLEEP
|
||||
# - Also intercept sleep(), nanosleep(), usleep(), alarm(), [p]poll()
|
||||
#
|
||||
# FAKE_TIMERS
|
||||
# - Also intercept timer_settime() and timer_gettime()
|
||||
# FAKE_TIMERS
|
||||
# - Also intercept timer_settime() and timer_gettime()
|
||||
#
|
||||
# MULTI_ARCH
|
||||
# - If MULTI_ARCH is set, the faketime wrapper program will put a literal
|
||||
# $LIB into the LD_PRELOAD environment variable it creates, which makes
|
||||
# ld automatically choose the correct library version to use for the
|
||||
# target binary. Use for Linux platforms with Multi-Arch support only!
|
||||
# FAKE_PTHREAD
|
||||
# - Intercept pthread_cond_timedwait
|
||||
#
|
||||
# MULTI_ARCH
|
||||
# - If MULTI_ARCH is set, the faketime wrapper program will put a literal
|
||||
# $LIB into the LD_PRELOAD environment variable it creates, which makes
|
||||
# ld automatically choose the correct library version to use for the
|
||||
# target binary. Use for Linux platforms with Multi-Arch support only!
|
||||
#
|
||||
# * Compilation addition: second libMT target added for building the pthread-
|
||||
# enabled library as a separate library
|
||||
@@ -62,7 +62,7 @@ PREFIX ?= /usr/local
|
||||
LIBDIRNAME ?= /lib/faketime
|
||||
PLATFORM ?=$(shell uname)
|
||||
|
||||
CFLAGS += -std=gnu99 -Wall -Wextra -Werror -Wno-nonnull-compare -DFAKE_STAT -DFAKE_SLEEP -DFAKE_TIMERS -DFAKE_INTERNAL_CALLS -fPIC -DPREFIX='"'$(PREFIX)'"' -DLIBDIRNAME='"'$(LIBDIRNAME)'"'
|
||||
CFLAGS += -std=gnu99 -Wall -Wextra -Werror -Wno-nonnull-compare -DFAKE_PTHREAD -DFAKE_STAT -DFAKE_SLEEP -DFAKE_TIMERS -DFAKE_INTERNAL_CALLS -fPIC -DPREFIX='"'$(PREFIX)'"' -DLIBDIRNAME='"'$(LIBDIRNAME)'"'
|
||||
ifeq ($(PLATFORM),SunOS)
|
||||
CFLAGS += -D__EXTENSIONS__ -D_XOPEN_SOURCE=600
|
||||
endif
|
||||
@@ -86,7 +86,7 @@ LIBS = libfaketime.so.${SONAME} libfaketimeMT.so.${SONAME}
|
||||
|
||||
all: ${LIBS} ${BINS}
|
||||
|
||||
libfaketimeMT.o: EXTRA_FLAGS := -DPTHREAD -DPTHREAD_SINGLETHREADED_TIME
|
||||
libfaketimeMT.o: EXTRA_FLAGS := -DPTHREAD_SINGLETHREADED_TIME
|
||||
|
||||
${LIBS_OBJ}: libfaketime.c
|
||||
${CC} -o $@ -c ${CFLAGS} ${EXTRA_FLAGS} $<
|
||||
|
||||
@@ -41,11 +41,14 @@
|
||||
#include <netinet/in.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "uthash.h"
|
||||
|
||||
#include "time_ops.h"
|
||||
#include "faketime_common.h"
|
||||
|
||||
|
||||
/* pthread-handling contributed by David North, TDI in version 0.7 */
|
||||
#ifdef PTHREAD
|
||||
#if defined PTHREAD_SINGLETHREADED_TIME || defined FAKE_PTHREAD
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
@@ -140,6 +143,13 @@ static int (*real___ftime) (struct timeb *);
|
||||
static int (*real___gettimeofday) (struct timeval *, void *);
|
||||
static int (*real___clock_gettime) (clockid_t clk_id, struct timespec *tp);
|
||||
#endif
|
||||
#ifdef FAKE_PTHREAD
|
||||
static int (*real_pthread_cond_timedwait_225) (pthread_cond_t *, pthread_mutex_t*, struct timespec *);
|
||||
static int (*real_pthread_cond_timedwait_232) (pthread_cond_t *, pthread_mutex_t*, struct timespec *);
|
||||
static int (*real_pthread_cond_init_232) (pthread_cond_t *restrict, const pthread_condattr_t *restrict);
|
||||
static int (*real_pthread_cond_destroy_232) (pthread_cond_t *);
|
||||
#endif
|
||||
|
||||
#ifndef __APPLEOSX__
|
||||
#ifdef FAKE_TIMERS
|
||||
static int (*real_timer_settime_22) (int timerid, int flags, const struct itimerspec *new_value,
|
||||
@@ -1681,6 +1691,26 @@ static void ftpl_init(void)
|
||||
real___gettimeofday = dlsym(RTLD_NEXT, "__gettimeofday");
|
||||
real___clock_gettime = dlsym(RTLD_NEXT, "__clock_gettime");
|
||||
#endif
|
||||
#ifdef FAKE_PTHREAD
|
||||
real_pthread_cond_timedwait_225 = dlvsym(RTLD_NEXT, "pthread_cond_timedwait", "GLIBC_2.2.5");
|
||||
|
||||
real_pthread_cond_timedwait_232 = dlvsym(RTLD_NEXT, "pthread_cond_timedwait", "GLIBC_2.3.2");
|
||||
real_pthread_cond_init_232 = dlvsym(RTLD_NEXT, "pthread_cond_init", "GLIBC_2.3.2");
|
||||
real_pthread_cond_destroy_232 = dlvsym(RTLD_NEXT, "pthread_cond_destroy", "GLIBC_2.3.2");
|
||||
|
||||
if (NULL == real_pthread_cond_timedwait_232)
|
||||
{
|
||||
real_pthread_cond_timedwait_232 = dlsym(RTLD_NEXT, "pthread_cond_timedwait");
|
||||
}
|
||||
if (NULL == real_pthread_cond_init_232)
|
||||
{
|
||||
real_pthread_cond_init_232 = dlsym(RTLD_NEXT, "pthread_cond_init");
|
||||
}
|
||||
if (NULL == real_pthread_cond_destroy_232)
|
||||
{
|
||||
real_pthread_cond_destroy_232 = dlsym(RTLD_NEXT, "pthread_cond_destroy");
|
||||
}
|
||||
#endif
|
||||
#ifdef __APPLEOSX__
|
||||
real_clock_get_time = dlsym(RTLD_NEXT, "clock_get_time");
|
||||
real_clock_gettime = apple_clock_gettime;
|
||||
@@ -2409,6 +2439,150 @@ int __ftime(struct timeb *tb)
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* =======================================================================
|
||||
* Faked pthread_cond_timedwait === FAKE(pthread)
|
||||
* =======================================================================
|
||||
*/
|
||||
|
||||
/* pthread_cond_timedwait
|
||||
|
||||
The specified absolute time in pthread_cond_timedwait is directly
|
||||
passed to the kernel via the futex syscall. The kernel, however,
|
||||
does not know about the fake time. In 99.9% of cases, the time
|
||||
until this function should wait is calculated by an application
|
||||
relatively to the current time, which has been faked in the
|
||||
application. Hence, we should convert the waiting time back to real
|
||||
time.
|
||||
|
||||
pthread_cond_timedwait in GLIBC_2_2_5 only supports
|
||||
CLOCK_REALTIME. Since the init and destroy functions are not
|
||||
redefined for GLIBC_2_2_5, a corresponding cond will never be
|
||||
added to monotonic_conds and hence the correct branch will
|
||||
always be taken.
|
||||
*/
|
||||
|
||||
|
||||
#ifdef FAKE_PTHREAD
|
||||
|
||||
typedef enum {FT_COMPAT_GLIBC_2_2_5, FT_COMPAT_GLIBC_2_3_2} ft_lib_compat_pthread;
|
||||
|
||||
struct pthread_cond_monotonic {
|
||||
pthread_cond_t *ptr;
|
||||
UT_hash_handle hh;
|
||||
};
|
||||
|
||||
static struct pthread_cond_monotonic *monotonic_conds = NULL;
|
||||
|
||||
int pthread_cond_init_232(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr)
|
||||
{
|
||||
clockid_t clock_id;
|
||||
int result;
|
||||
|
||||
result = real_pthread_cond_init_232(cond, attr);
|
||||
|
||||
if (result != 0 || attr == NULL)
|
||||
return result;
|
||||
|
||||
pthread_condattr_getclock(attr, &clock_id);
|
||||
|
||||
if (clock_id == CLOCK_MONOTONIC) {
|
||||
struct pthread_cond_monotonic *e = (struct pthread_cond_monotonic*)malloc(sizeof(struct pthread_cond_monotonic));
|
||||
e->ptr = cond;
|
||||
HASH_ADD_PTR(monotonic_conds, ptr, e);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int pthread_cond_destroy_232(pthread_cond_t *cond)
|
||||
{
|
||||
struct pthread_cond_monotonic* e;
|
||||
HASH_FIND_PTR(monotonic_conds, &cond, e);
|
||||
if (e) {
|
||||
HASH_DEL(monotonic_conds, e);
|
||||
free(e);
|
||||
}
|
||||
|
||||
return real_pthread_cond_destroy_232(cond);
|
||||
}
|
||||
|
||||
int pthread_cond_timedwait_common(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime, ft_lib_compat_pthread compat)
|
||||
{
|
||||
struct timespec tp, tdiff_actual, realtime, faketime;
|
||||
struct timespec *tf = NULL;
|
||||
struct pthread_cond_monotonic* e;
|
||||
clockid_t clk_id;
|
||||
int result;
|
||||
|
||||
if (abstime != NULL)
|
||||
{
|
||||
HASH_FIND_PTR(monotonic_conds, &cond, e);
|
||||
if (e != NULL)
|
||||
clk_id = CLOCK_MONOTONIC;
|
||||
else
|
||||
clk_id = CLOCK_REALTIME;
|
||||
|
||||
DONT_FAKE_TIME(result = (*real_clock_gettime)(clk_id, &realtime));
|
||||
if (result == -1)
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
faketime = realtime;
|
||||
(void)fake_clock_gettime(clk_id, &faketime);
|
||||
|
||||
timespecsub(abstime, &faketime, &tp);
|
||||
if (user_rate_set)
|
||||
{
|
||||
timespecmul(&tp, 1.0 / user_rate, &tdiff_actual);
|
||||
}
|
||||
else
|
||||
{
|
||||
tdiff_actual = tp;
|
||||
}
|
||||
|
||||
/* For CLOCK_MONOTONIC, pthread_cond_timedwait uses clock_gettime
|
||||
internally to calculate the appropriate duration for the
|
||||
waiting time. This already uses the faked functions, hence, the
|
||||
fake time needs to be passed to pthread_cond_timedwait for
|
||||
CLOCK_MONOTONIC. */
|
||||
if(clk_id == CLOCK_MONOTONIC)
|
||||
timespecadd(&faketime, &tdiff_actual, &tp);
|
||||
else
|
||||
timespecadd(&realtime, &tdiff_actual, &tp);
|
||||
|
||||
tf = &tp;
|
||||
}
|
||||
|
||||
switch (compat) {
|
||||
case FT_COMPAT_GLIBC_2_3_2:
|
||||
result = real_pthread_cond_timedwait_232(cond, mutex, tf);
|
||||
break;
|
||||
case FT_COMPAT_GLIBC_2_2_5:
|
||||
result = real_pthread_cond_timedwait_225(cond, mutex, tf);
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int pthread_cond_timedwait_225(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime)
|
||||
{
|
||||
return pthread_cond_timedwait_common(cond, mutex, abstime, FT_COMPAT_GLIBC_2_2_5);
|
||||
}
|
||||
|
||||
int pthread_cond_timedwait_232(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime)
|
||||
{
|
||||
return pthread_cond_timedwait_common(cond, mutex, abstime, FT_COMPAT_GLIBC_2_3_2);
|
||||
}
|
||||
|
||||
__asm__(".symver pthread_cond_timedwait_225, pthread_cond_timedwait@GLIBC_2.2.5");
|
||||
__asm__(".symver pthread_cond_timedwait_232, pthread_cond_timedwait@@GLIBC_2.3.2");
|
||||
__asm__(".symver pthread_cond_init_232, pthread_cond_init@@GLIBC_2.3.2");
|
||||
__asm__(".symver pthread_cond_destroy_232, pthread_cond_destroy@@GLIBC_2.3.2");
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Editor modelines
|
||||
*
|
||||
|
||||
@@ -4,7 +4,18 @@ GLIBC_2.2 {
|
||||
timer_gettime; timer_settime;
|
||||
local: timer_settime_*; timer_gettime_*;
|
||||
};
|
||||
|
||||
GLIBC_2.3.3 {
|
||||
# Changed timer_t.
|
||||
timer_gettime; timer_settime;
|
||||
} GLIBC_2.2;
|
||||
|
||||
GLIBC_2.2.5 {
|
||||
global: pthread_cond_timedwait;
|
||||
local: pthread_cond_timedwait_*; pthread_cond_init_*; pthread_cond_destroy*;
|
||||
};
|
||||
|
||||
GLIBC_2.3.2 {
|
||||
pthread_cond_timedwait; pthread_cond_init; pthread_cond_destroy;
|
||||
} GLIBC_2.2.5;
|
||||
|
||||
|
||||
1208
src/uthash.h
Normal file
1208
src/uthash.h
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
CC = gcc
|
||||
|
||||
CFLAGS = -std=gnu99 -Wall -DFAKE_STAT -Werror -Wextra
|
||||
LDFLAGS = -lrt
|
||||
LDFLAGS = -lrt -lpthread
|
||||
|
||||
SRC = timetest.c
|
||||
OBJ = ${SRC:.c=.o}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
@@ -31,6 +32,8 @@
|
||||
#endif
|
||||
|
||||
#ifndef __APPLE__
|
||||
#include <pthread.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
|
||||
#define VERBOSE 0
|
||||
@@ -49,6 +52,64 @@ handler(int sig, siginfo_t *si, void *uc)
|
||||
printf("Caught signal %d\n", sig);
|
||||
}
|
||||
}
|
||||
|
||||
void* pthread_test(void* args)
|
||||
{
|
||||
pthread_mutex_t fakeMutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
pthread_cond_t fakeCond = PTHREAD_COND_INITIALIZER;
|
||||
|
||||
pthread_cond_t monotonic_cond;
|
||||
pthread_condattr_t attr;
|
||||
|
||||
struct timespec timeToWait, now;
|
||||
int rt;
|
||||
|
||||
args = args; // silence compiler warning about unused argument
|
||||
|
||||
clock_gettime(CLOCK_REALTIME, &now);
|
||||
timeToWait.tv_sec = now.tv_sec+1;
|
||||
timeToWait.tv_nsec = now.tv_nsec;
|
||||
|
||||
printf("pthread_cond_timedwait: CLOCK_REALTIME test\n");
|
||||
printf("(Intentionally sleeping 1 second...)\n");
|
||||
fflush(stdout);
|
||||
|
||||
pthread_mutex_lock(&fakeMutex);
|
||||
rt = pthread_cond_timedwait(&fakeCond, &fakeMutex, &timeToWait);
|
||||
if (rt != ETIMEDOUT)
|
||||
{
|
||||
printf("pthread_cond_timedwait failed\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
pthread_mutex_unlock(&fakeMutex);
|
||||
|
||||
|
||||
pthread_condattr_init(&attr);
|
||||
pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
|
||||
pthread_cond_init(&monotonic_cond, &attr);
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
timeToWait.tv_sec = now.tv_sec+1;
|
||||
timeToWait.tv_nsec = now.tv_nsec;
|
||||
|
||||
printf("pthread_cond_timedwait: CLOCK_MONOTONIC test\n");
|
||||
printf("(Intentionally sleeping 1 second...)\n");
|
||||
fflush(stdout);
|
||||
|
||||
pthread_mutex_lock(&fakeMutex);
|
||||
rt = pthread_cond_timedwait(&monotonic_cond, &fakeMutex, &timeToWait);
|
||||
if (rt != ETIMEDOUT)
|
||||
{
|
||||
printf("pthread_cond_timedwait failed\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
pthread_mutex_unlock(&fakeMutex);
|
||||
|
||||
pthread_cond_destroy(&monotonic_cond);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int main (int argc, char **argv)
|
||||
@@ -69,6 +130,12 @@ int main (int argc, char **argv)
|
||||
#endif
|
||||
|
||||
#ifndef __APPLE__
|
||||
pthread_t thread;
|
||||
void *ret;
|
||||
|
||||
pthread_create(&thread, NULL, pthread_test, NULL);
|
||||
pthread_join(thread, &ret);
|
||||
|
||||
sa.sa_flags = SA_SIGINFO;
|
||||
sa.sa_sigaction = handler;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
|
||||
Reference in New Issue
Block a user