* cluts daily reports 8/13 - pthread_eintr expanded
@ 2011-08-13 19:10 Luka Marčetić
2011-08-13 19:20 ` Solar Designer
2011-08-13 23:38 ` Rich Felker
0 siblings, 2 replies; 4+ messages in thread
From: Luka Marčetić @ 2011-08-13 19:10 UTC (permalink / raw)
To: musl
[-- Attachment #1: Type: text/plain, Size: 414 bytes --]
Done:
*a lot more work done on pthread_eintr (if you need me to explain
any of the unfinished code, tell me)
Priorities:
*pthread_eintr needs work (if someone's proficient with valgrind, I
would appreciate help interpreting the output)
*...
`to-report` may be uninformative (.git gets in the way), but updated
pthread_eintr is attached.
P.S. I was offline for a while, and may be in the future.
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: pthread_eintr.c --]
[-- Type: text/x-csrc; name="pthread_eintr.c", Size: 13876 bytes --]
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/wait.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <time.h>
#include <sched.h>
#include <stdint.h>
#include <setjmp.h>
#include "common/common.h"
#include <pthread.h>
/*
* Copyright (c) 2011 Luka Marčetić<paxcoder@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted.
*
* There's ABSOLUTELY NO WARRANTY, express or implied.
*/
/**
** \file
** Tests that EINTR isn't returned by interrupted pthread_* functions for which
** this is a specified behavior
** tests: pthread_create,
** depends: open,pipe,close, mmap, sigaction, kill,waitpid,nanosleep, fprintf,
** pause, pthread_cleanup_push, pthread_cleanup_pop, setjmp,longjmp
** sched_getscheduler,sched_get_priority_min,pthread_setschedprio,
** pthread_key_create, pthread_getconcurrency
**/
#define NANOSLEEP_MAX 100000 //a multiple of 100 (that's the loop step)
volatile char blocked; //when unset, signals child that it may exit
jmp_buf env; //context storage for sigset and longjmp
int pfd[2]; //pipe file descriptors
static void handle(int sig);
static void block(void);
static void* thread(void *fun);
#define WRAP_START \
int err = 0; \
if (!setjmp(env)) { \
blocked = 1; \
close(pfd[1]); /*ready*/
#define WRAP_END \
blocked = 0; /*reaching this = failure, see handle() for more info*/ \
} \
return err;
static int wrap_pthread_create(pthread_t *restrict thread,
const pthread_attr_t *restrict attr,
void *(*start_routine)(void*),
void *restrict arg);
static int wrap_pthread_cancel(pthread_t thread);
static int wrap_pthread_once(pthread_once_t *once_control,
void (*init_routine)(void));
static int wrap_pthread_setspecific(pthread_key_t key, const void *value);
static int wrap_pthread_key_delete(pthread_key_t key);
static int wrap_pthread_join(pthread_t thread, void **value_ptr);
static int wrap_pthread_atfork(void (*prepare)(void),
void (*parent)(void),
void (*child)(void));
static int wrap_pthread_sigmask(int how,
const sigset_t *restrict set,
sigset_t *restrict oset);
static int wrap_pthread_equal(pthread_t t1, pthread_t t2);
static int wrap_pthread_setschedprio(pthread_t thread, int prio);
static int wrap_pthread_setconcurrency(int new_level);
int main()
{
int8_t x;
char *s, c;
pid_t pid;
pthread_t tid;
pthread_key_t tkey;
struct timespec ns = {.tv_nsec=0};
unsigned int i, nr_fun, failed;
int err, ret[2]={0,0}, stat;
const char *function[] = {
"pthread_create",
"pthread_cancel",
"pthread_once",
"pthread_setspecific",
"pthread_key_delete",
"pthread_join",
"pthread_atfork",
"pthread_sigmask",
"pthread_equal",
"pthread_setschedprio",
"pthread_setconcurrency",
"pthread_detach",
"pthread_sigmask",
"pthread_setspecific",
"pthread_key_create",
"pthread_rwlock_unlock",
"pthread_kill",
};
struct {
unsigned int
kill:1,
wait:1;
} error, no_error = {.wait=0};
struct sigaction act = {.sa_handler=handle, .sa_flags=SA_NODEFER};
failed = 0;
for (i=0; i < (nr_fun=sizeof(function)/sizeof(*function)); ++i) {
pipe(pfd);
memset(&error, 0, sizeof(error));
if (!(pid = fork())) {
close(pfd[0]);
blocked = 0; //wrappers set it to 1 after saving env
sigaction(SIGTERM, &act, NULL);
sigaction(SIGABRT, &act, NULL);
pthread_create(&tid, NULL, thread, block);
//printf("%i calling.\n", i); //----
switch (i) {
case 0:
err = wrap_pthread_create(&tid, NULL, thread, NULL);
break;
case 1:
err = wrap_pthread_cancel(tid);
break;
case 2:
err = wrap_pthread_once(
(pthread_once_t []){PTHREAD_ONCE_INIT},
block
);
break;
case 3:
if (!pthread_key_create(&tkey, NULL)) {
err = wrap_pthread_setspecific(tkey, NULL);
pthread_key_delete(tkey);
} else
err = -3;
break;
case 4:
//key creation(inicialization) done in the wrapper:
err = wrap_pthread_key_delete(tkey);
break;
case 5:
err = wrap_pthread_join(tid, NULL);
break;
case 6:
err = wrap_pthread_atfork(NULL, NULL, NULL);
break;
case 7:
err = wrap_pthread_sigmask(SIG_UNBLOCK, NULL, NULL);
break;
//pthread_equal() returned 1(Operation not permitted)
case 8:
err = wrap_pthread_equal(tid, tid);
break;
//pthread_equal() returned 1(Operation not permitted)
#ifndef MUSL
case 9:
err = wrap_pthread_setschedprio(
tid,
sched_get_priority_min(sched_getscheduler(0))
);
break;
#endif
case 10:
err = wrap_pthread_setconcurrency(pthread_getconcurrency());
break;
default:
blocked = 0;
close(pfd[1]);
err = -2;
break;
}
if (setjmp(env)) fprintf(stdout,"BUG: SIGTERM received after function return\n");//----
printf("%d returning.\n", i); //----
return err;
}
if (!pid) {
fprintf(stdout, "BUG: child i=%d escaped!\n", i);
exit(0); //exit/contain child
}else if (pid == -1) {
close(pfd[1]);
close(pfd[0]);
fprintf(stderr,
"A call to fork() nr %d/%d yielded -1 (errno=%s)!\n",
i+1, nr_fun, (s = e_name(errno))
);
free(s);
failed += 1;
}else{
close(pfd[1]);
//printf("reading %i.\n", i); //----
read(pfd[0], &c, 1); //wait for the child to become ready
close(pfd[0]);
//Send SIGABRT to child in delays of <NANOSLEEP_MAX/100,0> microsec:
ns.tv_nsec = NANOSLEEP_MAX;
while(
(ret[0] = kill(pid, SIGABRT)) == 0
&& (ret[1] = waitpid(pid, &stat, WNOHANG)) == 0
&& (ns.tv_nsec -= 100)
)
nanosleep(&ns, NULL);
//let child's function out of its loop/blocked state
kill(pid, SIGTERM);
if (!ns.tv_nsec) {
printf("waiting %i.\n", i);//----
waitpid(pid, &stat, 0);
}
//Process errors:
s = e_name(errno);
error.kill = (ret[0] != 0);
error.wait = (ret[1] != 0 && ret[1] != pid);
if (memcmp(&error, &no_error, sizeof(error))) {
++failed;
if (error.kill)
fprintf(stderr,
"A call to kill() returned %d, errno=%s\n",
ret[0], s
);
if (error.wait)
fprintf(stderr,
"A call to waitpid() returned %d, errno=%s\n",
ret[1], s
);
}else if (WIFEXITED(stat)) {
if ((x = WEXITSTATUS(stat))) {
++failed;
if (x == -2)
fprintf(stderr,
"%s() or one of its dependencies missing!\n",
function[i]
);
else if (x == -3)
fprintf(stderr,
"%s() did not run - its dependency failed\n",
function[i]
);
else if (x) {
free(s);
s = e_name(x);
fprintf(stderr, "%s() returned %s\n", function[i], s);
}
}
//else printf("%s passed (returned %d)\n", function[i], x); //----
}else{
fprintf(stderr, "%s()'s host process crashed!\n", function[i]);
if (WIFSIGNALED(stat))
fprintf(stderr,
"\tTerminating signal: %d\n",
(int)WTERMSIG(stat)
);
}
free(s);
}
}
return failed;
}
/**
** Signal handler. Upon receiving the first SIGTERM it unsets blocked, longjmps
** Otherwise - on another signal or if blocked is unset - it simply returns
**/
static void handle(int sig)
{
if (sig == SIGTERM) {
if(blocked) {
blocked = 0;
longjmp(env, 1);
}
else {printf("BUG: Misplaced SIGTERM!\n");} //-----
}
}
///waits for blocked to be unset by a signal[!] via the signal handler, returns
static void block(void)
{
while(blocked)
sched_yield(); //can't use pause() - handle() doesn't return on SIGTERM
sleep(3);printf("exit block()\n");//---
}
/**
** Establishes the function passed to it as a thread cancellation handler and
** calls it. Thus, if the executing thread is canceled, the said function will
** still have to be executed before the thread is terminated.
** \param fun a void* cast of (void (*)()) or NULL (only return)
**/
static void* thread(void *fun)
{
if (fun != NULL) {
pthread_cleanup_push(fun, NULL);
((void (*)())fun)();
pthread_cleanup_pop(0);
}
return NULL;
}
///<Wrappers start here: They take the same arguments as the functions they
///<call but install the SIGTERM handler and optionally loop until it's received
static int wrap_pthread_create(pthread_t *restrict thread,
const pthread_attr_t *restrict attr,
void *(*start_routine)(void*),
void *restrict arg)
{
WRAP_START
while (!err)
err = pthread_create(thread, attr, start_routine, arg);
WRAP_END
}
static int wrap_pthread_cancel(pthread_t thread)
{
WRAP_START
err = pthread_cancel(thread);
WRAP_END
}
static int wrap_pthread_once(pthread_once_t *once_control,
void (*init_routine)(void))
{
WRAP_START
err = pthread_once(once_control, init_routine);
WRAP_END
}
static int wrap_pthread_setspecific(pthread_key_t key, const void *value)
{
WRAP_START
while(!err)
err = pthread_setspecific(key, value);
WRAP_END
}
static int wrap_pthread_key_delete(pthread_key_t key)
{
WRAP_START
while(!err) {
/* this is clumsy, having to key_create lessens key_delete's chances
of being interrupted - test not definite */
pthread_key_create(&key, NULL);
err = pthread_key_delete(key);
}
WRAP_END
}
static int wrap_pthread_join(pthread_t thread, void **value_ptr)
{
WRAP_START
err = pthread_join(thread, value_ptr);
WRAP_END
}
static int wrap_pthread_atfork(void (*prepare)(void),
void (*parent)(void),
void (*child)(void))
{
WRAP_START
while(!err)
err = pthread_atfork(prepare, parent, child);
WRAP_END
}
static int wrap_pthread_sigmask(int how,
const sigset_t *restrict set,
sigset_t *restrict oset)
{
WRAP_START
while(!err)
err = pthread_sigmask(how, set, oset);
WRAP_END
}
static int wrap_pthread_equal(pthread_t t1, pthread_t t2)
{
WRAP_START
do{
err = pthread_equal(t1, t2);
}while(err != 0 && err != EINTR);
WRAP_END
}
static int wrap_pthread_setschedprio(pthread_t thread, int prio)
{
WRAP_START
while(!err)
err = pthread_setschedprio(thread, prio);
WRAP_END
}
static int wrap_pthread_setconcurrency(int new_level)
{
WRAP_START
while (!err)
err = pthread_setconcurrency(new_level);
WRAP_END
}
/*
{
WRAP_START
WRAP_END
}
{
WRAP_START
WRAP_END
}
{
WRAP_START
WRAP_END
}
{
WRAP_START
WRAP_END
}
{
WRAP_START
WRAP_END
}
{
WRAP_START
WRAP_END
}
{
WRAP_START
WRAP_END
}
*/
[-- Attachment #3: to-report --]
[-- Type: text/plain, Size: 207 bytes --]
tests/common/String.c | 4
tests/common/e_name.c | 3
tests/pthread_eintr.c | 402
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: cluts daily reports 8/13 - pthread_eintr expanded
2011-08-13 19:10 cluts daily reports 8/13 - pthread_eintr expanded Luka Marčetić
@ 2011-08-13 19:20 ` Solar Designer
2011-08-13 19:42 ` Luka Marčetić
2011-08-13 23:38 ` Rich Felker
1 sibling, 1 reply; 4+ messages in thread
From: Solar Designer @ 2011-08-13 19:20 UTC (permalink / raw)
To: musl
On Sat, Aug 13, 2011 at 09:10:05PM +0200, Luka Mar??eti?? wrote:
> `to-report` may be uninformative (.git gets in the way)
Try:
diff -urNx .git cluts.old cluts | diffstat
or:
diff -urNX nopatch cluts.old cluts | diffstat
where nopatch will contain things like:
.git
*~
*.o
core
Whenever you get something unneeded in diffstat output, you simply edit
nopatch and re-run the command. Don't edit its output.
Alexander
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: cluts daily reports 8/13 - pthread_eintr expanded
2011-08-13 19:20 ` Solar Designer
@ 2011-08-13 19:42 ` Luka Marčetić
0 siblings, 0 replies; 4+ messages in thread
From: Luka Marčetić @ 2011-08-13 19:42 UTC (permalink / raw)
To: musl
On 08/13/2011 09:20 PM, Solar Designer wrote:
> On Sat, Aug 13, 2011 at 09:10:05PM +0200, Luka Mar??eti?? wrote:
>> `to-report` may be uninformative (.git gets in the way)
> Try:
>
> diff -urNx .git cluts.old cluts | diffstat
>
> or:
>
> diff -urNX nopatch cluts.old cluts | diffstat
>
> where nopatch will contain things like:
>
> .git
> *~
> *.o
> core
>
> Whenever you get something unneeded in diffstat output, you simply edit
> nopatch and re-run the command. Don't edit its output.
>
> Alexander
Very nice. thanks, will do.
Luka.
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: cluts daily reports 8/13 - pthread_eintr expanded
2011-08-13 19:10 cluts daily reports 8/13 - pthread_eintr expanded Luka Marčetić
2011-08-13 19:20 ` Solar Designer
@ 2011-08-13 23:38 ` Rich Felker
1 sibling, 0 replies; 4+ messages in thread
From: Rich Felker @ 2011-08-13 23:38 UTC (permalink / raw)
To: musl
On Sat, Aug 13, 2011 at 09:10:05PM +0200, Luka Marčetić wrote:
> Done:
> *a lot more work done on pthread_eintr (if you need me to
> explain any of the unfinished code, tell me)
> Priorities:
> *pthread_eintr needs work (if someone's proficient with
> valgrind, I would appreciate help interpreting the output)
pthread_equal test is wrong. This function returns a boolean value,
not an error code. I have no idea why the text "The pthread_equal()
function shall not return an error code of [EINTR]" appears in POSIX
for this function. Presumably it was a copy-and-paste error.
Rich
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2011-08-13 23:38 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-08-13 19:10 cluts daily reports 8/13 - pthread_eintr expanded Luka Marčetić
2011-08-13 19:20 ` Solar Designer
2011-08-13 19:42 ` Luka Marčetić
2011-08-13 23:38 ` Rich Felker
Code repositories for project(s) associated with this public inbox
https://git.vuxu.org/mirror/musl/
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).