mailing list of musl libc
 help / color / mirror / code / Atom feed
* 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).