1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
|
#define _GNU_SOURCE
#include <errno.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/syscall.h>
#include <sched.h>
#include <signal.h>
static int loops = 15; // each thread+main will do this amount of loop
static int sleepms = 1000; // in each loop, will sleep "sleepms" milliseconds
static int burn = 0; // after each sleep, will burn cpu in a tight 'burn' loop
static void setup_sigusr_handler(void); // sigusr1 and 2 sigaction setup.
static pid_t gettid()
{
#ifdef __NR_gettid
return syscall(__NR_gettid);
#else
return getpid();
#endif
}
// will be invoked from gdb.
static void whoami(char *msg) __attribute__((unused));
static void whoami(char *msg)
{
fprintf(stderr, "pid %ld Thread %ld %s\n", (long) getpid(), (long) gettid(),
msg);
fflush(stderr);
}
static void do_burn ()
{
int i;
int loopnr = 0;
// one single line for the below, to ensure interrupt on this line.
for (i = 0; i < burn; i++) loopnr++;
}
static int thread_ready = 0;
static pthread_cond_t ready = PTHREAD_COND_INITIALIZER;
static pthread_mutex_t ready_mutex = PTHREAD_MUTEX_INITIALIZER;
static void signal_ready (void)
{
int rc;
rc = pthread_mutex_lock(&ready_mutex);
if (rc != 0)
fprintf(stderr, "signal_ready lock error %d_n", rc);
thread_ready = 1;
rc = pthread_cond_signal(&ready);
if (rc != 0)
fprintf(stderr, "signal_ready signal error %d_n", rc);
rc = pthread_mutex_unlock(&ready_mutex);
if (rc != 0)
fprintf(stderr, "signal_ready unlock error %d_n", rc);
}
struct spec {
char *name;
int sleep;
int burn;
int t;
};
static struct timeval t[4];
static int nr_sleeper_or_burner = 0;
static volatile int report_finished = 1;
// set to 0 to have no finish msg (as order is non-deterministic)
static void *sleeper_or_burner(void *v)
{
int i = 0;
struct spec* s = (struct spec*)v;
int ret;
fprintf(stderr, "%s ready to sleep and/or burn\n", s->name);
fflush (stderr);
signal_ready();
nr_sleeper_or_burner++;
for (i = 0; i < loops; i++) {
if (sleepms > 0 && s->sleep) {
t[s->t].tv_sec = sleepms / 1000;
t[s->t].tv_usec = (sleepms % 1000) * 1000;
ret = select (0, NULL, NULL, NULL, &t[s->t]);
/* We only expect a timeout result or EINTR from the above. */
if (ret != 0 && errno != EINTR)
perror("unexpected result from select");
}
if (burn > 0 && s->burn)
do_burn();
}
if (report_finished) {
fprintf(stderr, "%s finished to sleep and/or burn\n", s->name);
fflush (stderr);
}
return NULL;
}
// wait till a thread signals it is ready
static void wait_ready(void)
{
int rc;
rc = pthread_mutex_lock(&ready_mutex);
if (rc != 0)
fprintf(stderr, "wait_ready lock error %d_n", rc);
while (! thread_ready && rc == 0) {
rc = pthread_cond_wait(&ready, &ready_mutex);
if (rc != 0)
fprintf(stderr, "wait_ready wait error %d_n", rc);
}
thread_ready = 0;
rc = pthread_mutex_unlock(&ready_mutex);
if (rc != 0)
fprintf(stderr, "wait_ready unlock error %d_n", rc);
}
// We will lock ourselves on one single cpu.
// This bypasses the unfairness of the Valgrind scheduler
// when a multi-cpu machine has enough cpu to run all the
// threads wanting to burn cpu.
static void setaffinity(void)
{
#ifdef VGO_linux
cpu_set_t single_cpu;
CPU_ZERO(&single_cpu);
CPU_SET(1, &single_cpu);
(void) sched_setaffinity(0, sizeof(single_cpu), &single_cpu);
#endif
// GDBTD: equivalent for Darwin ?
}
int main (int argc, char *argv[])
{
char *threads_spec;
pthread_t ebbr, egll, zzzz;
struct spec b, l, p, m;
char *some_mem __attribute__((unused)) = malloc(100);
if (argc > 5 && atoi(argv[5])) setaffinity();
setup_sigusr_handler();
if (argc > 1)
loops = atoi(argv[1]);
if (argc > 2)
sleepms = atoi(argv[2]);
if (argc > 3)
burn = atoll(argv[3]);
if (argc > 4)
threads_spec = argv[4];
else
threads_spec = "BSBSBSBS";
fprintf(stderr, "loops/sleep_ms/burn/threads_spec/affinity: %d %d %d %s %d\n",
loops, sleepms, burn, threads_spec, argc > 5 && atoi(argv[5]));
fflush(stderr);
b.name = "Brussels";
b.burn = *threads_spec++ == 'B';
b.sleep = *threads_spec++ == 'S';
b.t = -1;
if (b.burn || b.sleep) {
b.t = 1;
pthread_create(&ebbr, NULL, sleeper_or_burner, &b);
wait_ready();
}
l.name = "London";
l.burn = *threads_spec++ == 'B';
l.sleep = *threads_spec++ == 'S';
l.t = -1;
if (l.burn || l.sleep) {
l.t = 2;
pthread_create(&egll, NULL, sleeper_or_burner, &l);
wait_ready();
}
p.name = "Petaouchnok";
p.burn = *threads_spec++ == 'B';
p.sleep = *threads_spec++ == 'S';
p.t = -1;
if (p.burn || p.sleep) {
p.t = 3;
pthread_create(&zzzz, NULL, sleeper_or_burner, &p);
wait_ready();
}
m.name = "main";
m.burn = *threads_spec++ == 'B';
m.sleep = *threads_spec++ == 'S';
m.t = 0;
sleeper_or_burner(&m);
if (b.t != -1) pthread_join(ebbr, NULL);
if (l.t != -1) pthread_join(egll, NULL);
if (p.t != -1) pthread_join(zzzz, NULL);
return 0;
}
static int sigusr1_received = 0;
static void sigusr1_handler(int signr)
{
sigusr1_received++;
}
static void setup_sigusr_handler(void)
{
struct sigaction sa;
sa.sa_handler = sigusr1_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
if (sigaction (SIGUSR1, &sa, NULL) != 0)
perror("sigaction SIGUSR1");
sa.sa_handler = SIG_IGN;
if (sigaction (SIGUSR2, &sa, NULL) != 0)
perror("sigaction SIGUSR2");
}
|