[LTP] [PATCH 2/2] pthread_create-14-1: avoid threads sharing stack

Jan Stancek jstancek@redhat.com
Wed Dec 6 14:08:48 CET 2017



----- Original Message -----
> 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.

That's true.

> 
> > +/* 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?

By mistake. I forgot to drop it when I dropped semaphore that synchronized
sending/handling of signal.

> 
> > +		/*
> > +		 * 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)?

Probably not by much. I'll compare both ways. If it's minimal difference
I'll leave it as infinite loop.

> 
> > +	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.

Agreed.

> 
> > +		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.

Thanks for having a look.

Regards,
Jan

> 
> --
> Cyril Hrubis
> chrubis@suse.cz
> 


More information about the ltp mailing list