[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[nptl] Re: Patch for SCHED_RR test with LinuxThreads
Hello,
> > [
> > though I don't know if I am really sorry: nobody else found those
> > issues
> > :-D
> > ]
>
> Do you mean that we're the firsts, or that this is probably not a real
> issue?
No... Excuse my selfish inference, but I was thinking that nobody
found the issue in _MY_ code.
> > I shall publish the new version by Monday. I want to give me a few
> > days in order to review the program.
>
> No problem. I can run the tests with a minimal effort on a 2x and a 8x
> IA32 xeon, and with SuSe 9.0; SLES 9; FC2. (and standard kernel or
> vanilla; standard glibc or recent one). Is this enough to get some
> conclusions?
Oh, well... fine!
In attachement, pls. find the version 2 of the test program.
Since it changed quite a lot, I posted the entire version instead
of the corresponding patch.
I am a bit busy right now, but I hope I shall come back this week
and post the "new results".
Regards,
Loic.
--
--
// Sender address goes to /dev/null (!!)
// Use my 32/64 bits, ANSI C89, compliant email-address instead:
unsigned y[]=
{0,34432,26811,16721,41866,63119,61007,48155,26147,10986};
void x(z){putchar(z);}; unsigned t;
main(i){if(i<10){t=(y[i]*47560)%65521;x(t>>8);x(t&255);main(++i);}}
+++ GMX DSL Premiumtarife 3 Monate gratis* + WLAN-Router 0,- EUR* +++
Clevere DSL-Nutzer wechseln jetzt zu GMX: http://www.gmx.net/de/go/dsl
/*
*----------------------------------------------------------------------------
* Copyright (c) 2004, Loic Domaigne. All rights reserved.
* Created by: Loic Domaigne.
*
* 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., 59
* Temple Place - Suite 330, Boston MA 02111-1307, USA.
*
*---------------------------------------------------------------------------
*
* #------------------------#
* # To compile #
* #------------------------#
* $ gcc -W -Wall -pthread -lrt test_schedrr.c -o test_schedrr
*
*
* #------------------------#
* # To call the program: #
* #------------------------#
* $ schedrr nthreads prio
*
* where nthreads is the number of SCHED_RR threads you want to start, and
* prio their priority. On some OS, you might need to run this process
* as root.
*
* Example:
* # ./test_schedrr 32 20 ## start 32 SCHED_RR threads at prio 20
*
*
* #------------------------#
* # Program Tunings: #
* #------------------------#
* You might tune
* o the number of max threads supported by the application (default: 1024)
* by setting NR_MAX_THREADS to another value to the compilation
*
* o the timeout used for the test (default: 30sec) by setting
* TEST_TIMEOUT
*
* Example:
* $ gcc -W -Wall -pthread -DTEST_TIMEOUT=100 -DNR_MAX_THREADS=4096 schedrr.c
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <pthread.h>
#include <sched.h>
#include <signal.h>
#include <unistd.h>
#include <time.h>
#ifndef _U_
#define _U_ __attribute__((unused))
#endif
#define FOREVER for(;;)
#define FIRE_TIMEOUT(tsec) \
do { \
pthread_mutex_lock (&mutex); \
alarm_state=ALARM_FIRED_UP; \
pthread_cond_signal (&cv); \
while ( alarm_state!=ALARM_RUNNING ) { \
pthread_cond_wait (&cv, &mutex); \
} \
pthread_mutex_unlock (&mutex); \
} while (0);
#ifndef NR_MAX_THREADS
#define NR_MAX_THREADS 1024
#endif
#ifndef TEST_TIMEOUT
#define TEST_TIMEOUT 30
#endif
#define ONE_BILLION 1000000000
typedef struct {
int created; /* ture if thread has been created */
int started; /* true if thread has started */
struct timespec tpstart; /* time at which thread has been started */
int stop; /* true if thread must stop */
unsigned long counter; /* kind of cpu usage */
int cancelled; /* true if thread has been cancelled */
} thread_info_t;
sigset_t alarm_sigmask;
int nthreads;
int prio;
thread_info_t info[NR_MAX_THREADS];
struct timespec tpstop;
/* ----------------------------
* Workaround for LinuxThreads.
* ----------------------------
*
* alarm() must be called in the alarm_thread to work on LinuxThreads.
* That's the classical LT limitation that signals are delivered to
* particular thread instead to the process.
*
* The "go-mechanism" has been implemented in a very classical
* fashion with a pair of cv/mutex.
*
*/
enum {
ALARM_OFF,
ALARM_FIRED_UP,
ALARM_RUNNING
} alarm_state = ALARM_OFF;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cv = PTHREAD_COND_INITIALIZER;
/*****************************************************************************
* spawn_thread- starts a thread with a given scheduling policy/priority
*****************************************************************************
*
* This function is a helper function around pthread_create() to ease the
* creation of thread with other scheduling parameters than the default
* one.
*
* @tid@ : thread's id
* @policy@ : thread's policy (SCHED_RR, SCHED_FIFO or SCHED_OTHER)
* @prio@ : thread's priority
* @start_routine@: thread's start routine
* @arg@ : thread's argument
*
*****************************************************************************/
int
spawn_thread(pthread_t* tid,
int policy,
int prio,
void* (*start_routine)(void*),
void* arg
)
{
pthread_attr_t ta; /* thread's attribute */
struct sched_param sp; /* scheduling parameter */
int status; /* returned status */
/*
* set explicit scheduling to the wanted policy/prio
*/
(void) pthread_attr_init(&ta);
status = pthread_attr_setinheritsched(&ta, PTHREAD_EXPLICIT_SCHED);
assert (status==0);
status = pthread_attr_setschedpolicy(&ta, policy);
assert (status==0);
sp.sched_priority = prio;
status = pthread_attr_setschedparam(&ta, &sp);
assert (status==0);
/*
* start corresponding thread
*/
status = pthread_create(tid, &ta, start_routine, arg);
return status;
}
/*****************************************************************************
* dump_thread_info- dump thread's info
*****************************************************************************
*
* This helper function dumps the field for the array @info@ of size @n@
*
****************************************************************************/
void
dump_thread_info(thread_info_t* info,
int n
)
{
int i;
for (i=0; i<n; i++) {
/*
* the idea behind those inc is force eventually cache coherency
* (a memory barrier would be definitively better)
*/
info[i].created++;
info[i].started++;
info[i].counter++;
info[i].tpstart.tv_sec++;
info[i].tpstart.tv_nsec++;
printf ("Thread %5d: ",i+1);
if ( info[i].created==1 ) { // 1 because of +1
printf ("NOT CREATED\n");
} else {
if (info[i].started==1 ) { // 1 because of +1
printf ("created but NOT STARTED\n");
} else {
struct timespec diff;
diff.tv_sec = tpstop.tv_sec - info[i].tpstart.tv_sec + 1 ;
diff.tv_nsec = tpstop.tv_nsec - info[i].tpstart.tv_nsec + 1 ;
if ( diff.tv_nsec < 0 ) {
diff.tv_nsec += ONE_BILLION;
diff.tv_sec--;
}
printf ("started for %ld.%09ld sec, sched. %lu times\n",
(long) diff.tv_sec,
(long) diff.tv_nsec,
info[i].counter-1
);
}
}
}
}
/*****************************************************************************
* alarm_thread- caught eventual SIGALRM
*****************************************************************************
*
* This thread wait synchronously on eventual receipt of the SIGARLM, and
* set @alarm_raised@ to 1.
*
* NOTE:
* YOU MUST INSURE THAT POLICY/PRIORITY OF TIMEOUT_THREAD IS COMPATIBLE WITH
* THE OTHER THREADS IN THE PROCESS! IOW, when using RT-scheduling, make sure
* that this thread has the highest prio.
*
*****************************************************************************/
void*
alarm_thread(void* unused _U_)
{
int sig_caught;
int status;
/*
* wait the order to fire the alarm up
*/
printf ("alarm> Waiting that alarm is fired up\n");
pthread_mutex_lock(&mutex);
while ( alarm_state!=ALARM_FIRED_UP ) {
status = pthread_cond_wait (&cv, &mutex);
assert (status==0);
}
alarm (TEST_TIMEOUT);
/*
* signal testdrv_thread that the alarm is running
*/
alarm_state = ALARM_RUNNING;
printf ("alarm> Alarm activated\n");
pthread_cond_signal (&cv);
pthread_mutex_unlock (&mutex);
/*
* wait synchronously on SIGALRM now
*/
sig_caught = 0;
do {
(void) sigwait (&alarm_sigmask, &sig_caught);
if ( sig_caught == SIGALRM) {
/*
* something went really wrong: prints out thread info and exits
*/
assert ( clock_gettime(CLOCK_REALTIME, &tpstop)==0 );
dump_thread_info(info, nthreads);
printf ("TIMED OUT\n");
_exit (1);
}
}
while ( sig_caught!=SIGALRM);
return NULL; // keep compiler happy
}
/*****************************************************************************
* cpu_thread- a pure compute bound thread.
*****************************************************************************
*
* The following thread is a trivial pure compute bound thread. It does
* basically nothing but increments a (thread local) counter before yielding
* the CPU.
*
****************************************************************************/
void*
cpu_thread (void* arg)
{
thread_info_t* info = (thread_info_t*) arg;
info->started = 1;
assert ( clock_gettime(CLOCK_REALTIME, &info->tpstart)==0 );
/*
* busy loop until stop order received
*/
while (info->stop==0) {
info->counter++;
sched_yield();
}
return NULL;
}
/*****************************************************************************
* testdrv_thread- main test driver thread
*****************************************************************************
*
* This is the thread that does the real work. It starts @nthreads@ SCHED_RR
* threads running at priority @prio@. It waits then (using the thread info)
* that all the threads have been started, before terminating the process.
*
* In order to avoid that the process hangs indefinitively, a global timeout
* of TEST_TIMEOUT seconds is set right at the beginning. Each steps where
* a hang can occur is protected by testing if the alarm has been raised
* with the @alarm_raised@ flag.
*
****************************************************************************/
void*
testdrv_thread(void* unused _U_)
{
pthread_t thrid[NR_MAX_THREADS];
int status;
int nrunning;
int i;
/*
* set-up global timeout
*/
FIRE_TIMEOUT(TEST_TIMEOUT);
/*
* start the @nthreads@ SCHED_RR threads at priority @prio@
*/
printf ("testdrv> Starting %d threads SCHED_RR with prio %d\n",
nthreads,
prio
);
for (i=0; i<nthreads; i++) {
info[i].started = 0;
info[i].stop = 0;
info[i].counter = 0;
info[i].tpstart.tv_sec = 0;
info[i].tpstart.tv_nsec = -1;
status = spawn_thread (&thrid[i], SCHED_RR, prio, cpu_thread, &info[i]);
info[i].created = 1;
if (status!=0) fprintf(stderr,"%s\n",strerror(status));
assert (status==0);
}
printf ("testdrv> The %d SCHED_RR threads running at prio %d "
"have been created\n",
nthreads,
prio
);
/*
* now wait that all threads run
*/
printf ("testdrv> Wait that all threads run... \n");
do {
nrunning = 0;
for (i=0; i<nthreads; i++) {
if (info[i].started==1) nrunning++;
}
}
while ( nrunning!=nthreads);
/*
* succeed!
*/
assert ( clock_gettime(CLOCK_REALTIME, &tpstop)==0 );
printf ("PASSED\n");
exit (0);
}
/*###########################################################################*/
/*## MAIN PROCESS ENTRY POINT ##*/
/*###########################################################################*/
int
main(int argc,
char* argv[]
)
{
pthread_t alarm_thrid;
pthread_t testdrv_thrid;
int status;
int minprio, maxprio;
/*
* process main args
*/
assert (argc!=2);
nthreads = atoi(argv[1]);
prio = atoi(argv[2]);
assert (1 <= nthreads && nthreads <= NR_MAX_THREADS );
maxprio = sched_get_priority_max (SCHED_RR);
minprio = sched_get_priority_min (SCHED_RR);
assert ( minprio <= prio && prio <= (maxprio-2) );
/*
* setup signal mask that shall be inherited by all threads
*/
(void) sigemptyset (&alarm_sigmask);
status = sigaddset (&alarm_sigmask, SIGALRM);
assert (status==0);
status = pthread_sigmask (SIG_BLOCK, &alarm_sigmask, NULL);
assert (status==0);
/*
* start timeout handler thread
*/
printf ("main> Spawn alarm thread\n");
status = spawn_thread (&alarm_thrid, SCHED_RR, prio+2, alarm_thread, NULL);
assert (status==0);
/*
* start thread conducting the experiment
*/
printf ("main> Spawn test driver thread\n");
status = spawn_thread (&testdrv_thrid, SCHED_RR, prio+1, testdrv_thread, NULL);
assert (status==0);
/*
* shall be never joined
*/
status = pthread_join (testdrv_thrid, NULL);
return 0;
}