[LTP] [PATCH 2/2] pthread_create-14-1: avoid threads sharing stack
Cyril Hrubis
chrubis@suse.cz
Wed Dec 6 13:58:47 CET 2017
Hi!
> +/*
> + * Copyright (c) 2004, Bull S.A.. All rights reserved.
> + * Copyright (c) 2017, Linux Test Project
> + * Created by: Sebastien Decugis
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of version 2 of the GNU General Public License as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it would be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, write the Free Software Foundation, Inc.,
> + *
> + * This sample test aims to check the following assertion:
> + * The function does not return EINTR
> + *
> + * The steps are:
> + * -> continuously send SIGUSR1 to a thread which runs pthread_create()
> + * -> check that EINTR is never returned
> + */
> +
> +/* We are testing conformance to IEEE Std 1003.1, 2003 Edition */
> +#define _POSIX_C_SOURCE 200112L
> +
> +/* Some routines are part of the XSI Extensions */
> +#ifndef WITHOUT_XOPEN
> +#define _XOPEN_SOURCE 600
> +#endif
> +
> +#include <stdio.h>
> +#include <errno.h>
> +#include <pthread.h>
> +#include <semaphore.h>
> +#include <signal.h>
> +#include <sys/wait.h>
> +#include <sys/time.h>
> +#include <stdlib.h>
> +#include <unistd.h>
> +
> +#include "../testfrmw/testfrmw.h"
> +#include "../testfrmw/testfrmw.c"
> +#include "../testfrmw/threads_scenarii.c"
> +#include "safe_helpers.h"
> +
> +#define RUN_TIME_USEC (2*1000*1000)
> +#define SIGNALS_WITHOUT_DELAY 100
> +
> +/* total number of signals sent */
> +static unsigned long count_sig;
> +/* sleep [us] in between signals */
> +static unsigned long sleep_time;
^
Shouldn't this be volatile too?
We reset it from the test() thread while it's used in a loop in the
sendsig() thread.
> +/* signal thread active flag */
> +static volatile int sendsig_active;
> +/* number of pthread_create scenarios tested */
> +static unsigned long count_ope;
> +
> +static unsigned long current_time_usec(void)
> +{
> + struct timeval now;
> +
> + SAFE_FUNC(gettimeofday(&now, NULL));
> + return now.tv_sec * 1000000 + now.tv_usec;
> +}
> +
> +/* the following function keeps sending signal to the process */
> +static void *sendsig(void *arg)
> +{
> + static sigset_t usersigs;
> +
> + (void)arg;
> + pid_t process = getpid();
> +
> + /* block the signal SIGUSR1 for this THREAD */
> + SAFE_FUNC(sigemptyset(&usersigs));
> + SAFE_FUNC(sigaddset(&usersigs, SIGUSR1));
> + SAFE_PFUNC(pthread_sigmask(SIG_BLOCK, &usersigs, NULL));
> +
> + while (sendsig_active) {
> + if (!sendsig_active)
> + break;
Why do we have the condition for sendsig_active twice here?
> + /*
> + * Keep increasing sleeptime to make sure we progress
> + * allow SIGNALS_WITHOUT_DELAY signals without any pause,
> + * then start increasing sleep_time to make sure all threads
> + * can progress.
> + */
> + sleep_time++;
> + if (sleep_time / SIGNALS_WITHOUT_DELAY > 0)
> + usleep(sleep_time / SIGNALS_WITHOUT_DELAY);
> +
> + count_sig++;
> + SAFE_FUNC(kill(process, SIGUSR1));
> + }
> + return NULL;
> +}
> +
> +static void sighdl1(int sig)
> +{
> + (void)sig;
> +}
> +
> +static void *threaded(void *arg)
> +{
> + int ret;
> +
> + /* Signal we're done (especially in case of a detached thread) */
> + do {
> + ret = sem_post(&scenarii[sc].sem);
> + } while ((ret == -1) && (errno == EINTR));
> +
> + if (ret == -1)
> + UNRESOLVED(errno, "Failed to wait for the semaphore");
> +
> + return arg;
> +}
> +
> +/* create threads and check that EINTR is never returned */
> +static void test(void)
> +{
> + int ret = 0;
> + pthread_t child;
> + pthread_t th_sig1;
> + struct sigaction sa;
> +
> + sigemptyset(&sa.sa_mask);
> + sa.sa_flags = 0;
> + sa.sa_handler = sighdl1;
> + SAFE_FUNC(sigaction(SIGUSR1, &sa, NULL));
> +
> + sendsig_active = 1;
> + SAFE_PFUNC(pthread_create(&th_sig1, NULL, sendsig, NULL));
> +
> + for (sc = 0; sc < NSCENAR; sc++) {
> + /* reset sleep time for signal thread */
> + sleep_time = 0;
> +
> + ret = pthread_create(&child, &scenarii[sc].ta, threaded, NULL);
> + if (ret == EINTR)
> + FAILED("pthread_create returned EINTR");
> +
> + switch (scenarii[sc].result) {
> + case 0: /* Operation was expected to succeed */
> + if (ret != 0)
> + UNRESOLVED(ret, "Failed to create this thread");
> + break;
> + case 1: /* Operation was expected to fail */
> + if (ret == 0) {
> + UNRESOLVED(-1, "An error was expected but"
> + " the thread creation succeeded");
> + }
> + break;
> + case 2: /* We did not know the expected result */
> + default:
> + break;
> + }
> +
> + if (ret != 0)
> + continue;
> +
> + /* The new thread is running */
> + /* Just wait for the thread to terminate */
> + do {
> + ret = sem_wait(&scenarii[sc].sem);
> + } while ((ret == -1) && (errno == EINTR));
> + if (ret == -1)
> + UNRESOLVED(errno, "Failed to wait for the semaphore");
> + if (scenarii[sc].detached == 0)
> + SAFE_PFUNC(pthread_join(child, NULL));
> + }
> + sendsig_active = 0;
We may possibly simplify the whole thread that sends the signals by
letting it run in an infinite loop, it will be reaped by the exit() int
the main_loop() anyway.
Or does disabling it here make the test faster (do we get more
iterations)?
> + SAFE_PFUNC(pthread_join(th_sig1, NULL));
> +}
> +
> +static void main_loop(void)
> +{
> + int child_count = 0;
> + int ret;
> + int status;
> + int stat_pipe[2];
> + pid_t child;
> + unsigned long usec_start, usec;
> + unsigned long child_count_sig;
> +
> + usec_start = current_time_usec();
> + do {
> + fflush(stdout);
> + SAFE_FUNC(pipe(stat_pipe));
> + child = fork();
We should handle the fork() failures here as well, even if it's quite
unlikely to happen.
> + if (child == 0) {
> + count_sig = 0;
> + close(stat_pipe[0]);
> + test();
> + SAFE_FUNC(write(stat_pipe[1], &count_sig,
> + sizeof(count_sig)));
> + close(stat_pipe[1]);
> + exit(0);
> + }
> + close(stat_pipe[1]);
> + SAFE_FUNC(read(stat_pipe[0], &child_count_sig,
> + sizeof(count_sig)));
> + close(stat_pipe[0]);
> + count_sig += child_count_sig;
> +
> + ret = waitpid(child, &status, 0);
> + if (ret != child)
> + UNRESOLVED(errno, "Waitpid returned the wrong PID");
> + if (!WIFEXITED(status)) {
> + output("status: %d\n", status);
> + FAILED("Child exited abnormally");
> + }
> + if (WEXITSTATUS(status) != 0) {
> + output("exit status: %d\n", WEXITSTATUS(status));
> + FAILED("An error occurred in child");
> + }
> +
> + child_count++;
> + count_ope += NSCENAR;
> + usec = current_time_usec();
> + } while ((usec - usec_start) < RUN_TIME_USEC);
> +
> + output("Test spawned %d child processes.\n", child_count);
> + output("Test finished after %lu usec.\n", usec - usec_start);
> +}
> +
> +int main(void)
> +{
> + output_init();
> + scenar_init();
> + main_loop();
> + scenar_fini();
> +
> + output("Test executed successfully.\n");
> + output(" %d thread creations.\n", count_ope);
> + output(" %d signals were sent meanwhile.\n", count_sig);
> + PASSED;
> +}
Other than that this looks good to me.
--
Cyril Hrubis
chrubis@suse.cz
More information about the ltp
mailing list