Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 19 Dec 2000 20:40:24 -0800 (PST)
From:      andy@silverbrook.com.au
To:        freebsd-gnats-submit@FreeBSD.org
Subject:   misc/23679: libc_r thread scheduling sleep time mis-calculations
Message-ID:  <200012200440.eBK4eOq41601@freefall.freebsd.org>
Resent-Message-ID: <200012200450.eBK4o1a42686@freefall.freebsd.org>

next in thread | raw e-mail | index | archive | help

>Number:         23679
>Category:       misc
>Synopsis:       libc_r thread scheduling sleep time mis-calculations
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Tue Dec 19 20:50:00 PST 2000
>Closed-Date:
>Last-Modified:
>Originator:     Andy Newman
>Release:        RELENG_4
>Organization:
Silverbrook Research
>Environment:
FreeBSD bebop.silverbrook.com.au 4.2-STABLE FreeBSD 4.2-STABLE #0: Wed Nov 22 08:52:41 EST 2000     toor@bebop.silverbrook.com.au:/usr/obj/usr/src/sys/bebop  i386

>Description:
In a particular circumstance libc_r mis-calculates sleep times. The
attached demonstration program shows the problem.  A number of child
processes are forked from a parent and then attempt to acquire a
(SysV IPC) semaphore, do some work (sleep) and release the semaphore.
Each child process attempts to sleep for a constant time (1 second).
Building against libc produces the expected results.  Building against
libc_r produces differences in sleep times.  The first child process
sleeps for the correct time, the second for twice as long, the third
for twice as long as that etc.... (powers of 2).

Tracing the system calls being made shows that incorrect timeout values
are being passed to poll(2) presumably via libc_r's thread scheduler.
Prior to the poll(2) calls there are two gettimeofday(2) calls which is
consistent with an execution path from libc_r's nanosleep() routine
through to the thread scheduler.

The shar achive has a test, tst.c, and a makefile.  Two executables
are built, tst, linked against libc, and tstp, linked against libc_r.
The program is invoked with a single arg, the semaphore's "key". The
program creates a sempahore and forks 8 child processes which all
try to acquire it, do some work and release it.
>How-To-Repeat:
# This is a shell archive.  Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file".  Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
#	tst.c
#	Makefile
#
echo x - tst.c
sed 's/^X//' >tst.c << 'END-of-tst.c'
X/*
X * Demonstrate problems with process sleep times.
X */
X
X#include <stdio.h>
X#include <stdarg.h>
X#include <sys/types.h>
X#include <sys/time.h>
X#include <sys/ipc.h>
X#include <sys/sem.h>
X#include <sys/wait.h>
X#include <unistd.h>
X#include <err.h>
X#include <time.h>
X
Xint	mypid;
X
Xvoid	child(int);
Xvoid	release(int, const char *);
Xvoid	log(const char *, ...);
X
Xint
Xmain(int argc, char **argv)
X{
X    int			semid;
X    int			i;
X    key_t		key;
X    unsigned int	n_proc = 8;
X    unsigned long	ulvar;
X
X    mypid = getpid();
X
X    if (argc < 2)
X	goto usage;
X    if (sscanf(argv[1], "%lu", &ulvar) != 1)
X	goto usage;
X    key = (key_t)ulvar;
X    if (argc > 2 && sscanf(argv[2], "%u", &n_proc) != 1)
X	goto usage;
X
X    if ((semid = semget(key, 1, IPC_CREAT|0600)) == -1)
X	err(1, "semget(create)");
X    if (semctl(semid, 0, SETVAL, 0) < 0)
X	err(1, "semctl(SETVAL)");
X    log("parent created semaphore, id = 0x%08x", semid);
X
X    for (i = 0; i < n_proc; ++i)
X	child(semid);
X
X    log("parent releasing semaphore");
X    release(semid, "parent");
X
X    while (i-- > 0)
X    {
X	int status;
X	pid_t pid;
X	pid = wait(&status);
X	log("parent reaped child pid=%d, status=%0o", (int)pid, status);
X    }
X
X    semctl(semid, 0, IPC_RMID, 0);
X
X    return 0;
X
Xusage:
X    fprintf(stderr, "usage: %s semaphore-key [num_children]\n", argv[0]);
X    return 1;
X}
X
Xvoid
Xlog(const char *fmt, ...)
X{
X    va_list		va;
X    struct timeval	tv;
X    char		buf[1024];
X
X    gettimeofday(&tv, NULL);
X    sprintf(buf, "%lu.%-6lu: ", tv.tv_sec, tv.tv_usec);
X    va_start(va, fmt);
X    vsprintf(buf+strlen(buf), fmt, va);
X    va_end(va);
X    fprintf(stderr, "%s\n", buf);
X    fflush(stderr);
X}
X
Xvoid
Xacquire(int semid)
X{
X    struct sembuf	semops;
X
X    semops.sem_num = 0;
X    semops.sem_op  = -1;
X    semops.sem_flg = 0;
X
X    log("child %d acquiring semaphore", mypid);
X    if (semop(semid, &semops, 1) < 0)
X	err(1, "acquire");
X    log("child %d acquired semaphore", mypid);
X}
X
Xvoid
Xrelease(int semid, const char *who)
X{
X    struct sembuf	semops;
X
X    semops.sem_num = 0;
X    semops.sem_op  = 1;
X    semops.sem_flg = 0;
X    log("%s %d releasing semaphore", who, mypid);
X    if (semop(semid, &semops, 1) < 0)
X	err(1, "release");
X    log("%s %d released semaphore", who, mypid);
X}
X
Xvoid
Xwork(void)
X{
X    time_t	t1, t2;
X
X    log("child %d working for 1 second", mypid);
X    t1 = time(NULL);
X    sleep(1);
X    t2 = time(NULL);
X    log("child %d done working: %d elapsed", mypid, t2 - t1);
X}
X
Xvoid
Xchild(int semid)
X{
X    switch (fork())
X    {
X    case -1:
X	err(1, "fork");
X
X    case 0:
X	mypid = getpid();
X	acquire(semid);
X	work();
X	release(semid, "child");
X	log("child %d done", mypid);
X	_exit(0);
X    }
X}
END-of-tst.c
echo x - Makefile
sed 's/^X//' >Makefile << 'END-of-Makefile'
Xall: tst tstp
X
Xtst : tst.c
X	cc -g -Wall -o tst tst.c
X
Xtstp: tst.c
X	cc -g -Wall -o tstp -pthread tst.c
X
Xclean:
X	rm -f tst tstp ktrace.out
END-of-Makefile
exit


>Fix:


>Release-Note:
>Audit-Trail:
>Unformatted:


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-bugs" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200012200440.eBK4eOq41601>