From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 18215 invoked by alias); 16 Apr 2004 02:02:48 -0000 Mailing-List: contact libc-hacker-help@sources.redhat.com; run by ezmlm Precedence: bulk List-Subscribe: List-Archive: List-Post: List-Help: , Sender: libc-hacker-owner@sources.redhat.com Received: (qmail 18187 invoked from network); 16 Apr 2004 02:02:47 -0000 Received: from unknown (HELO sunsite.ms.mff.cuni.cz) (195.113.15.26) by sources.redhat.com with SMTP; 16 Apr 2004 02:02:47 -0000 Received: from sunsite.ms.mff.cuni.cz (sunsite.mff.cuni.cz [127.0.0.1]) by sunsite.ms.mff.cuni.cz (8.12.8/8.12.8) with ESMTP id i3FNpFIE015850; Fri, 16 Apr 2004 01:51:22 +0200 Received: (from jakub@localhost) by sunsite.ms.mff.cuni.cz (8.12.8/8.12.8/Submit) id i3FEnrN4031372; Thu, 15 Apr 2004 16:49:53 +0200 Date: Fri, 16 Apr 2004 02:02:00 -0000 From: Jakub Jelinek To: Ulrich Drepper Cc: Glibc hackers Subject: [PATCH] Fix POSIX timers Message-ID: <20040415144952.GE514@sunsite.ms.mff.cuni.cz> Reply-To: Jakub Jelinek Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline User-Agent: Mutt/1.4i X-SW-Source: 2004-04/txt/msg00032.txt.bz2 Hi! SIGEV_THREAD times don't work at all if kernel provides POSIX timers. The problem is that sigwaitinfo magically removes SIGCANCEL from the sigset_t passed as first argument, but SIGTIMER == SIGCANCEL. SIGTIMER is blocked in timer_helper_thread and so could be never delivered. Fix below. I have also included a new testcase for timers, but have there 3 FIXMEs where I'm not sure what exactly POSIX mandates. http://www.opengroup.org/onlinepubs/007904975/functions/timer_getoverrun.html has: The timer_settime() function shall fail if: [EINVAL] A value structure specified a nanosecond value less than zero or greater than or equal to 1000 million, and the it_value member of that structure did not specify zero seconds and nanoseconds. This would suggest to me that it should not fail if it_value.tv_sec == 0 && it_value.tv_nsec == 0 && (it_interval.tv_nsec < 0 || it_interval.tv_nsec >= 1000000000) but both the kernel and sysdeps/pthread/timer_settime.c fails with EINVAL. If current behaviour is wrong, this can be solved both in userland and/or the kernel, what is the preference? For timer_gettime, POSIX says: The it_interval member of value shall contain the reload value last set by timer_settime(). But kernel doesn't return exactly the it_interval value set by last timer_settime(), but some value between that and orig it_inverval + timer resolution. Again, is this a bug or permissible behaviour. The testcase below prints a warning about both these things, but doesn't actually fail if they are encountered, until this is resolved. Last thing, I wonder if it wouldn't be better to use the same mechanism for SIGEV_THREAD as for message queues. Then we could have just one magic helper thread for both mqs and timers. Kernel could drop current SIGEV_THREAD_ID support in posix_timers.c (return EINVAL on it) and add SIGEV_THREAD support via netlink. Unfortunately current posix_timers.c doesn't properly check sigev_notify values, but this could be worked around in userland (if SIGEV_THREAD struct sigevent sets sigev_signo to -1, current kernel timer_create will fail with EINVAL and glibc could fall back to compat stuff). 2004-04-15 Jakub Jelinek * rt/Makefile (tests): Add tst-timer4. * rt/tst-timer4.c: New test. nptl/ * sysdeps/unix/sysv/linux/timer_routines.c: Include errno.h. (timer_helper_thread): Use inline rt_sigtimedwait syscall instead of calling sigwaitinfo. --- libc/nptl/sysdeps/unix/sysv/linux/timer_routines.c.jj 2003-04-21 09:38:28.000000000 +0200 +++ libc/nptl/sysdeps/unix/sysv/linux/timer_routines.c 2004-04-15 16:49:55.679087743 +0200 @@ -1,4 +1,4 @@ -/* Copyright (C) 2003 Free Software Foundation, Inc. +/* Copyright (C) 2003, 2004 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper , 2003. @@ -17,6 +17,7 @@ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#include #include #include #include @@ -61,7 +62,19 @@ timer_helper_thread (void *arg) { siginfo_t si; - if (sigwaitinfo (&ss, &si) > 0) + /* sigwaitinfo cannot be used here, since it deletes + SIGCANCEL == SIGTIMER from the set. */ + + int oldtype = LIBC_CANCEL_ASYNC (); + + /* XXX The size argument hopefully will have to be changed to the + real size of the user-level sigset_t. */ + int result = INLINE_SYSCALL (rt_sigtimedwait, 4, &ss, &si, NULL, + _NSIG / 8); + + LIBC_CANCEL_RESET (oldtype); + + if (result > 0) { if (si.si_code == SI_TIMER) { --- libc/rt/Makefile.jj 2004-04-14 08:59:27.000000000 +0200 +++ libc/rt/Makefile 2004-04-15 14:34:31.000000000 +0200 @@ -44,7 +44,7 @@ librt-routines = $(aio-routines) \ tests := tst-shm tst-clock tst-clock_nanosleep tst-timer tst-timer2 \ tst-aio tst-aio64 tst-aio2 tst-aio3 tst-aio4 tst-aio5 tst-aio6 \ tst-aio7 tst-mqueue1 tst-mqueue2 tst-mqueue3 tst-mqueue4 \ - tst-mqueue5 tst-mqueue6 tst-mqueue7 tst-timer3 + tst-mqueue5 tst-mqueue6 tst-mqueue7 tst-timer3 tst-timer4 extra-libs := librt extra-libs-others := $(extra-libs) --- libc/rt/tst-timer4.c.jj 2004-04-15 11:26:33.000000000 +0200 +++ libc/rt/tst-timer4.c 2004-04-15 18:34:45.225919063 +0200 @@ -0,0 +1,624 @@ +/* Tests for POSIX timer implementation. + Copyright (C) 2004 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Jakub Jelinek , 2004 + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include +#include +#include +#include +#include +#if _POSIX_THREADS +# include + +pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; +pthread_cond_t cond = PTHREAD_COND_INITIALIZER; + +timer_t timer_none, timer_sig1, timer_sig2, timer_thr1, timer_thr2; + +int thr1_cnt, thr1_err; +union sigval thr1_sigval; +struct timespec thr1_ts; + +static void +thr1 (union sigval sigval) +{ + pthread_mutex_lock (&lock); + thr1_err = clock_gettime (CLOCK_REALTIME, &thr1_ts); + if (thr1_cnt >= 5) + { + struct itimerspec it = { }; + thr1_err |= timer_settime (timer_thr1, 0, &it, NULL); + } + thr1_sigval = sigval; + ++thr1_cnt; + pthread_cond_signal (&cond); + pthread_mutex_unlock (&lock); +} + +int thr2_cnt, thr2_err; +union sigval thr2_sigval; +size_t thr2_guardsize; +struct timespec thr2_ts; + +static void +thr2 (union sigval sigval) +{ + pthread_attr_t nattr; + int err = 0; + size_t guardsize = -1; + int ret = pthread_getattr_np (pthread_self (), &nattr); + if (ret) + { + errno = ret; + printf ("pthread_getattr_np failed: %m\n"); + err = 1; + } + else + { + ret = pthread_attr_getguardsize (&nattr, &guardsize); + if (ret) + { + errno = ret; + printf ("pthread_attr_getguardsize failed: %m\n"); + err = 1; + } + if (pthread_attr_destroy (&nattr) != 0) + { + puts ("pthread_attr_destroy failed"); + err = 1; + } + } + pthread_mutex_lock (&lock); + thr2_err = clock_gettime (CLOCK_REALTIME, &thr2_ts) | err; + if (thr2_cnt >= 5) + { + struct itimerspec it = { }; + thr2_err |= timer_settime (timer_thr2, 0, &it, NULL); + } + thr2_sigval = sigval; + ++thr2_cnt; + thr2_guardsize = guardsize; + pthread_cond_signal (&cond); + pthread_mutex_unlock (&lock); +} + +volatile int sig1_cnt, sig1_err; +volatile union sigval sig1_sigval; +struct timespec sig1_ts; + +static void +sig1_handler (int sig, siginfo_t *info, void *ctx) +{ + int err = 0; + if (sig != SIGRTMIN) err |= 1 << 0; + if (info->si_signo != SIGRTMIN) err |= 1 << 1; + if (info->si_code != SI_TIMER) err |= 1 << 2; + if (clock_gettime (CLOCK_REALTIME, &sig1_ts) != 0) + err |= 1 << 3; + if (sig1_cnt >= 5) + { + struct itimerspec it = { }; + if (timer_settime (timer_sig1, 0, &it, NULL)) + err |= 1 << 4; + } + sig1_err |= err; + sig1_sigval = info->si_value; + ++sig1_cnt; +} + +volatile int sig2_cnt, sig2_err; +volatile union sigval sig2_sigval; +struct timespec sig2_ts; + +static void +sig2_handler (int sig, siginfo_t *info, void *ctx) +{ + int err = 0; + if (sig != SIGRTMIN + 1) err |= 1 << 0; + if (info->si_signo != SIGRTMIN + 1) err |= 1 << 1; + if (info->si_code != SI_TIMER) err |= 1 << 2; + if (clock_gettime (CLOCK_REALTIME, &sig2_ts) != 0) + err |= 1 << 3; + if (sig2_cnt >= 5) + { + struct itimerspec it = { }; + if (timer_settime (timer_sig2, 0, &it, NULL)) + err |= 1 << 4; + } + sig2_err |= err; + sig2_sigval = info->si_value; + ++sig2_cnt; +} + +/* Check if end is later or equal to start + nsec. */ +static int +check_ts (const char *name, const struct timespec *start, + const struct timespec *end, long msec) +{ + struct timespec ts = *start; + + ts.tv_sec += msec / 1000000; + ts.tv_nsec += (msec % 1000000) * 1000; + if (ts.tv_nsec >= 1000000000) + { + ++ts.tv_sec; + ts.tv_nsec -= 1000000000; + } + if (end->tv_sec < ts.tv_sec + || (end->tv_sec == ts.tv_sec && end->tv_nsec < ts.tv_nsec)) + { + printf ("timer %s invoked too soon: %ld.%09ld instead of expected %ld.%09ld\n", + name, (long) end->tv_sec, end->tv_nsec, + (long) ts.tv_sec, ts.tv_nsec); + return 1; + } + else + return 0; +} + +#define TIMEOUT 15 +#define TEST_FUNCTION do_test () +static int +do_test (void) +{ + int result = 0; + + struct timespec ts; + if (clock_gettime (CLOCK_REALTIME, &ts) != 0) + { + printf ("clock_gettime failed: %m\n"); + result = 1; + } + else + printf ("clock_gettime returned timespec = { %ld, %ld }\n", + (long) ts.tv_sec, ts.tv_nsec); + + if (clock_getres (CLOCK_REALTIME, &ts) != 0) + { + printf ("clock_getres failed: %m\n"); + result = 1; + } + else + printf ("clock_getres returned timespec = { %ld, %ld }\n", + (long) ts.tv_sec, ts.tv_nsec); + + struct sigevent ev; + memset (&ev, 0x11, sizeof (ev)); + ev.sigev_notify = SIGEV_NONE; + if (timer_create (CLOCK_REALTIME, &ev, &timer_none) != 0) + { + printf ("timer_create for timer_none failed: %m\n"); + result = 1; + } + + struct sigaction sa = { .sa_sigaction = sig1_handler, + .sa_flags = SA_SIGINFO }; + sigemptyset (&sa.sa_mask); + sigaction (SIGRTMIN, &sa, NULL); + sa.sa_sigaction = sig2_handler; + sigaction (SIGRTMIN + 1, &sa, NULL); + + memset (&ev, 0x22, sizeof (ev)); + ev.sigev_notify = SIGEV_SIGNAL; + ev.sigev_signo = SIGRTMIN; + ev.sigev_value.sival_ptr = &ev; + if (timer_create (CLOCK_REALTIME, &ev, &timer_sig1) != 0) + { + printf ("timer_create for timer_sig1 failed: %m\n"); + result = 1; + } + + memset (&ev, 0x33, sizeof (ev)); + ev.sigev_notify = SIGEV_SIGNAL; + ev.sigev_signo = SIGRTMIN + 1; + ev.sigev_value.sival_int = 163; + if (timer_create (CLOCK_REALTIME, &ev, &timer_sig2) != 0) + { + printf ("timer_create for timer_sig2 failed: %m\n"); + result = 1; + } + + memset (&ev, 0x44, sizeof (ev)); + ev.sigev_notify = SIGEV_THREAD; + ev.sigev_notify_function = thr1; + ev.sigev_notify_attributes = NULL; + ev.sigev_value.sival_ptr = &ev; + if (timer_create (CLOCK_REALTIME, &ev, &timer_thr1) != 0) + { + printf ("timer_create for timer_thr1 failed: %m\n"); + result = 1; + } + + pthread_attr_t nattr; + if (pthread_attr_init (&nattr) + || pthread_attr_setguardsize (&nattr, 0)) + { + puts ("pthread_attr_t setup failed"); + result = 1; + } + + memset (&ev, 0x55, sizeof (ev)); + ev.sigev_notify = SIGEV_THREAD; + ev.sigev_notify_function = thr2; + ev.sigev_notify_attributes = &nattr; + ev.sigev_value.sival_int = 111; + if (timer_create (CLOCK_REALTIME, &ev, &timer_thr2) != 0) + { + printf ("timer_create for timer_thr2 failed: %m\n"); + result = 1; + } + + int ret = timer_getoverrun (timer_thr1); + if (ret != 0) + { + if (ret == -1) + printf ("timer_getoverrun failed: %m\n"); + else + printf ("timer_getoverrun returned %d != 0\n", ret); + result = 1; + } + + struct itimerspec it; + it.it_value.tv_sec = 0; + it.it_value.tv_nsec = -26; + it.it_interval.tv_sec = 0; + it.it_interval.tv_nsec = 0; + if (timer_settime (timer_sig1, 0, &it, NULL) == 0) + { + puts ("timer_settime with negative tv_nsec unexpectedly succeeded"); + result = 1; + } + else if (errno != EINVAL) + { + printf ("timer_settime with negative tv_nsec did not fail with " + "EINVAL: %m\n"); + result = 1; + } + + it.it_value.tv_nsec = 100000; + it.it_interval.tv_nsec = 1000000000; + if (timer_settime (timer_sig2, 0, &it, NULL) == 0) + { + puts ("timer_settime with tv_nsec 1000000000 unexpectedly succeeded"); + result = 1; + } + else if (errno != EINVAL) + { + printf ("timer_settime with tv_nsec 1000000000 did not fail with " + "EINVAL: %m\n"); + result = 1; + } + + it.it_value.tv_nsec = 0; + it.it_interval.tv_nsec = -26; + if (timer_settime (timer_thr1, 0, &it, NULL) != 0) + { + printf ("timer_settime with it_value 0 it_interval invalid failed: %m\n"); + /* FIXME: is this mandated by POSIX? + result = 1; */ + } + + it.it_interval.tv_nsec = 3000000000; + if (timer_settime (timer_thr2, 0, &it, NULL) != 0) + { + printf ("timer_settime with it_value 0 it_interval invalid failed: %m\n"); + /* FIXME: is this mandated by POSIX? + result = 1; */ + } + + struct timespec startts; + if (clock_gettime (CLOCK_REALTIME, &startts) != 0) + { + printf ("clock_gettime failed: %m\n"); + result = 1; + } + + it.it_value.tv_nsec = 100000000; + it.it_interval.tv_nsec = 0; + if (timer_settime (timer_none, 0, &it, NULL) != 0) + { + printf ("timer_settime timer_none failed: %m\n"); + result = 1; + } + + it.it_value.tv_nsec = 200000000; + if (timer_settime (timer_thr1, 0, &it, NULL) != 0) + { + printf ("timer_settime timer_thr1 failed: %m\n"); + result = 1; + } + + it.it_value.tv_nsec = 300000000; + if (timer_settime (timer_thr2, 0, &it, NULL) != 0) + { + printf ("timer_settime timer_thr2 failed: %m\n"); + result = 1; + } + + it.it_value.tv_nsec = 400000000; + if (timer_settime (timer_sig1, 0, &it, NULL) != 0) + { + printf ("timer_settime timer_sig1 failed: %m\n"); + result = 1; + } + + it.it_value.tv_nsec = 500000000; + if (TEMP_FAILURE_RETRY (timer_settime (timer_sig2, 0, &it, NULL)) != 0) + { + printf ("timer_settime timer_sig2 failed: %m\n"); + result = 1; + } + + pthread_mutex_lock (&lock); + while (thr1_cnt == 0 || thr2_cnt == 0) + pthread_cond_wait (&cond, &lock); + pthread_mutex_unlock (&lock); + + while (sig1_cnt == 0 || sig2_cnt == 0) + { + ts.tv_sec = 0; + ts.tv_nsec = 100000000; + nanosleep (&ts, NULL); + } + + pthread_mutex_lock (&lock); + + if (thr1_cnt != 1) + { + printf ("thr1 not called exactly once, but %d times\n", thr1_cnt); + result = 1; + } + else if (thr1_err) + { + puts ("an error occurred in thr1"); + result = 1; + } + else if (thr1_sigval.sival_ptr != &ev) + { + printf ("thr1_sigval.sival_ptr %p != %p\n", thr1_sigval.sival_ptr, &ev); + result = 1; + } + else if (check_ts ("thr1", &startts, &thr1_ts, 200000)) + result = 1; + + if (thr2_cnt != 1) + { + printf ("thr2 not called exactly once, but %d times\n", thr2_cnt); + result = 1; + } + else if (thr2_err) + { + puts ("an error occurred in thr2"); + result = 1; + } + else if (thr2_sigval.sival_int != 111) + { + printf ("thr2_sigval.sival_ptr %d != 111\n", thr2_sigval.sival_int); + result = 1; + } + else if (check_ts ("thr2", &startts, &thr2_ts, 300000)) + result = 1; + else if (thr2_guardsize != 0) + { + printf ("thr2 guardsize %zd != 0\n", thr2_guardsize); + result = 1; + } + + pthread_mutex_unlock (&lock); + + if (sig1_cnt != 1) + { + printf ("sig1 not called exactly once, but %d times\n", sig1_cnt); + result = 1; + } + else if (sig1_err) + { + printf ("errors occurred in sig1 handler %x\n", sig1_err); + result = 1; + } + else if (sig1_sigval.sival_ptr != &ev) + { + printf ("sig1_sigval.sival_ptr %p != %p\n", sig1_sigval.sival_ptr, &ev); + result = 1; + } + else if (check_ts ("sig1", &startts, &sig1_ts, 400000)) + result = 1; + + if (sig2_cnt != 1) + { + printf ("sig2 not called exactly once, but %d times\n", sig2_cnt); + result = 1; + } + else if (sig2_err) + { + printf ("errors occurred in sig2 handler %x\n", sig2_err); + result = 1; + } + else if (sig2_sigval.sival_int != 163) + { + printf ("sig2_sigval.sival_ptr %d != 163\n", sig2_sigval.sival_int); + result = 1; + } + else if (check_ts ("sig2", &startts, &sig2_ts, 500000)) + result = 1; + + if (timer_gettime (timer_none, &it) != 0) + { + printf ("timer_gettime timer_none failed: %m\n"); + result = 1; + } + else if (it.it_value.tv_sec || it.it_value.tv_nsec + || it.it_interval.tv_sec || it.it_interval.tv_nsec) + { + printf ("timer_gettime timer_none returned { %ld.%09ld, %ld.%09ld }\n", + (long) it.it_value.tv_sec, it.it_value.tv_nsec, + (long) it.it_interval.tv_sec, it.it_interval.tv_nsec); + result = 1; + } + + if (clock_gettime (CLOCK_REALTIME, &startts) != 0) + { + printf ("clock_gettime failed: %m\n"); + result = 1; + } + + it.it_value.tv_sec = 1; + it.it_value.tv_nsec = 0; + it.it_interval.tv_sec = 0; + it.it_interval.tv_nsec = 100000000; + if (timer_settime (timer_none, 0, &it, NULL) != 0) + { + printf ("timer_settime timer_none failed: %m\n"); + result = 1; + } + + it.it_value.tv_nsec = 100000000; + it.it_interval.tv_nsec = 200000000; + if (timer_settime (timer_thr1, 0, &it, NULL) != 0) + { + printf ("timer_settime timer_thr1 failed: %m\n"); + result = 1; + } + + it.it_value.tv_nsec = 200000000; + it.it_interval.tv_nsec = 300000000; + if (timer_settime (timer_thr2, 0, &it, NULL) != 0) + { + printf ("timer_settime timer_thr2 failed: %m\n"); + result = 1; + } + + it.it_value.tv_nsec = 300000000; + it.it_interval.tv_nsec = 400000000; + if (timer_settime (timer_sig1, 0, &it, NULL) != 0) + { + printf ("timer_settime timer_sig1 failed: %m\n"); + result = 1; + } + + it.it_value.tv_nsec = 400000000; + it.it_interval.tv_nsec = 500000000; + if (TEMP_FAILURE_RETRY (timer_settime (timer_sig2, 0, &it, NULL)) != 0) + { + printf ("timer_settime timer_sig2 failed: %m\n"); + result = 1; + } + + pthread_mutex_lock (&lock); + while (thr1_cnt < 6 || thr2_cnt < 6) + pthread_cond_wait (&cond, &lock); + pthread_mutex_unlock (&lock); + + while (sig1_cnt < 6 || sig2_cnt < 6) + { + ts.tv_sec = 0; + ts.tv_nsec = 100000000; + nanosleep (&ts, NULL); + } + + pthread_mutex_lock (&lock); + + if (thr1_err) + { + puts ("an error occurred in thr1"); + result = 1; + } + else if (check_ts ("thr1", &startts, &thr1_ts, 1100000 + 4 * 200000)) + result = 1; + + if (thr2_err) + { + puts ("an error occurred in thr2"); + result = 1; + } + else if (check_ts ("thr2", &startts, &thr2_ts, 1200000 + 4 * 300000)) + result = 1; + else if (thr2_guardsize != 0) + { + printf ("thr2 guardsize %zd != 0\n", thr2_guardsize); + result = 1; + } + + pthread_mutex_unlock (&lock); + + if (sig1_err) + { + printf ("errors occurred in sig1 handler %x\n", sig1_err); + result = 1; + } + else if (check_ts ("sig1", &startts, &sig1_ts, 1300000 + 4 * 400000)) + result = 1; + + if (sig2_err) + { + printf ("errors occurred in sig2 handler %x\n", sig2_err); + result = 1; + } + else if (check_ts ("sig2", &startts, &sig2_ts, 1400000 + 4 * 500000)) + result = 1; + + if (timer_gettime (timer_none, &it) != 0) + { + printf ("timer_gettime timer_none failed: %m\n"); + result = 1; + } + else if (it.it_interval.tv_sec || it.it_interval.tv_nsec != 100000000) + { + printf ("second timer_gettime timer_none returned it_interval %ld.%09ld\n", + (long) it.it_interval.tv_sec, it.it_interval.tv_nsec); + /* FIXME: For now disabled. + result = 1; */ + } + + if (timer_delete (timer_none) != 0) + { + printf ("timer_delete for timer_none failed: %m\n"); + result = 1; + } + + if (timer_delete (timer_sig1) != 0) + { + printf ("timer_delete for timer_sig1 failed: %m\n"); + result = 1; + } + + if (timer_delete (timer_sig2) != 0) + { + printf ("timer_delete for timer_sig2 failed: %m\n"); + result = 1; + } + + if (timer_delete (timer_thr1) != 0) + { + printf ("timer_delete for timer_thr1 failed: %m\n"); + result = 1; + } + + if (timer_delete (timer_thr2) != 0) + { + printf ("timer_delete for timer_thr2 failed: %m\n"); + result = 1; + } + return result; +} +#else +# define TEST_FUNCTION 0 +#endif + +#include "../test-skeleton.c" Jakub