From 22ca321fae328b0f3a2a7e88d447557d2a65a0d9 Mon Sep 17 00:00:00 2001 From: Nemo D'ACREMONT Date: Fri, 14 Mar 2025 17:34:37 +0100 Subject: [PATCH] feat: add script to fetch tests --- fetch_tests.sh | 10 +++ tst/01-main.c | 28 ++++++++ tst/02-switch.c | 62 +++++++++++++++++ tst/03-equity.c | 66 ++++++++++++++++++ tst/11-join.c | 49 +++++++++++++ tst/12-join-main.c | 48 +++++++++++++ tst/21-create-many.c | 52 ++++++++++++++ tst/22-create-many-recursive.c | 54 +++++++++++++++ tst/23-create-many-once.c | 71 +++++++++++++++++++ tst/31-switch-many.c | 73 ++++++++++++++++++++ tst/32-switch-many-join.c | 64 +++++++++++++++++ tst/33-switch-many-cascade.c | 74 ++++++++++++++++++++ tst/51-fibonacci.c | 88 ++++++++++++++++++++++++ tst/61-mutex.c | 104 ++++++++++++++++++++++++++++ tst/62-mutex.c | 122 +++++++++++++++++++++++++++++++++ tst/71-preemption.c | 110 +++++++++++++++++++++++++++++ tst/81-deadlock.c | 57 +++++++++++++++ 17 files changed, 1132 insertions(+) create mode 100755 fetch_tests.sh create mode 100644 tst/01-main.c create mode 100644 tst/02-switch.c create mode 100644 tst/03-equity.c create mode 100644 tst/11-join.c create mode 100644 tst/12-join-main.c create mode 100644 tst/21-create-many.c create mode 100644 tst/22-create-many-recursive.c create mode 100644 tst/23-create-many-once.c create mode 100644 tst/31-switch-many.c create mode 100644 tst/32-switch-many-join.c create mode 100644 tst/33-switch-many-cascade.c create mode 100644 tst/51-fibonacci.c create mode 100644 tst/61-mutex.c create mode 100644 tst/62-mutex.c create mode 100644 tst/71-preemption.c create mode 100644 tst/81-deadlock.c diff --git a/fetch_tests.sh b/fetch_tests.sh new file mode 100755 index 0000000..391cdcb --- /dev/null +++ b/fetch_tests.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +tst_dir=tst +filenames="01-main 02-switch 03-equity 11-join 12-join-main 21-create-many 22-create-many-recursive 23-create-many-once 31-switch-many 32-switch-many-join 33-switch-many-cascade 51-fibonacci 61-mutex 62-mutex 71-preemption 81-deadlock" +mkdir -p "${tst_dir}" + +for filename in ${filenames} +do + curl -o "${tst_dir}/${filename}.c" "https://goglin.gitlabpages.inria.fr/enseirb-it202/tests/${filename}.c" & +done diff --git a/tst/01-main.c b/tst/01-main.c new file mode 100644 index 0000000..bf99842 --- /dev/null +++ b/tst/01-main.c @@ -0,0 +1,28 @@ +#include +#include +#include "thread.h" + +/* test du thread_self et yield du main seul. + * + * le programme doit retourner correctement. + * valgrind doit être content. + * + * support nécessaire: + * - thread_yield() depuis et vers le main + * - thread_self() depuis le main + */ + +int main() +{ + int err, i; + + for(i=0; i<10; i++) { + printf("le main yield tout seul\n"); + err = thread_yield(); + assert(!err); + } + + printf("le main est %p\n", (void*) thread_self()); + + return 0; +} diff --git a/tst/02-switch.c b/tst/02-switch.c new file mode 100644 index 0000000..5349e9f --- /dev/null +++ b/tst/02-switch.c @@ -0,0 +1,62 @@ +#include +#include +#include "thread.h" + +/* test de switchs. + * + * les affichages doivent être dans le bon ordre (fifo) + * le programme doit retourner correctement. + * valgrind doit être content. + * + * support nécessaire: + * - thread_create() + * - thread_yield() depuis ou vers le main + * - thread_exit() + * - thread_join() avec récupération de la valeur de retour, ou sans + */ + +static void * thfunc(void *id) +{ + int err, i; + for(i=0; i<10; i++) { + printf("%s yield vers un autre thread\n", (char*) id); + err = thread_yield(); + assert(!err); + } + + printf("%s terminé\n", (char*) id); + thread_exit(NULL); + return (void*) 0xdeadbeef; /* unreachable, shut up the compiler */ +} + +int main() +{ + thread_t th1,th2,th3; + void *res; + int err, i; + + err = thread_create(&th1, thfunc, "fils1"); + assert(!err); + err = thread_create(&th2, thfunc, "fils2"); + assert(!err); + err = thread_create(&th3, thfunc, "fils3"); + assert(!err); + /* des switchs avec l'autre thread */ + for(i=0; i<20; i++) { + printf("le main yield vers un fils\n"); + err = thread_yield(); + assert(!err); + } + + err = thread_join(th3, &res); + assert(!err); + assert(res == NULL); + err = thread_join(th2, NULL); /* on ignore ce code de retour */ + assert(!err); + err = thread_join(th1, &res); + assert(!err); + assert(res == NULL); + + printf("main terminé\n"); + return 0; +} diff --git a/tst/03-equity.c b/tst/03-equity.c new file mode 100644 index 0000000..d2cf275 --- /dev/null +++ b/tst/03-equity.c @@ -0,0 +1,66 @@ +#include +#include +#include +#include "thread.h" + +/* test de l'équité de la cooperation via des thread_yield(). + * + * plus le score est proche de 1, plus + * l'ordonnancement cooperatif est equitable. + * + * support nécessaire: + * - thread_create() + * - thread_yield() depuis ou vers le main + * - retour sans thread_exit() + * - thread_join() + */ + +static unsigned v[3]; +static int fini = 0; +static double score = 0; + +static void * thfunc(void *arg) +{ + unsigned long myid = (unsigned long) arg; + int err, i; + for(i=0; i<1000 && !fini; i++) { + err = thread_yield(); + assert(!err); + v[myid]++; + if (v[myid] == 1000 && !fini) { + fini = 1; + printf("le thread %lu a terminé\n", myid); + printf("nombres de yield: %u %u %u\n", v[0], v[1], v[2]); + score = (v[0]+v[1]+v[2]) / 3000.; + printf("score: %lf\n", score); + } + } + + return NULL; +} + +int main() +{ + thread_t th1, th2; + int err; + + v[0] = v[1] = v[2] = 0; + err = thread_create(&th1, thfunc, (void*)0UL); + assert(!err); + err = thread_create(&th2, thfunc, (void*)1UL); + assert(!err); + + thfunc((void*)2UL); + + err = thread_join(th2, NULL); + assert(!err); + err = thread_join(th1, NULL); + assert(!err); + + if ( score < .8) { + return EXIT_FAILURE; + } + else { + return EXIT_SUCCESS; + } +} diff --git a/tst/11-join.c b/tst/11-join.c new file mode 100644 index 0000000..4404bf5 --- /dev/null +++ b/tst/11-join.c @@ -0,0 +1,49 @@ +#include +#include +#include "thread.h" + +/* test du join, avec ou sans thread_exit. + * + * le programme doit retourner correctement. + * valgrind doit être content. + * + * support nécessaire: + * - thread_create() + * - thread_exit() + * - retour sans thread_exit() + * - thread_join() avec récupération valeur de retour, avec et sans thread_exit() + */ + +static void * thfunc(void *dummy __attribute__((unused))) +{ + thread_exit((void*)0xdeadbeef); + return NULL; /* unreachable, shut up the compiler */ +} + +static void * thfunc2(void *dummy __attribute__((unused))) +{ + return (void*) 0xbeefdead; +} + +int main() +{ + thread_t th, th2; + int err; + void *res = NULL; + + err = thread_create(&th, thfunc, NULL); + assert(!err); + err = thread_create(&th2, thfunc2, NULL); + assert(!err); + + err = thread_join(th, &res); + assert(!err); + assert(res == (void*) 0xdeadbeef); + + err = thread_join(th2, &res); + assert(!err); + assert(res == (void*) 0xbeefdead); + + printf("join OK\n"); + return 0; +} diff --git a/tst/12-join-main.c b/tst/12-join-main.c new file mode 100644 index 0000000..5df9a1c --- /dev/null +++ b/tst/12-join-main.c @@ -0,0 +1,48 @@ +#include +#include +#include "thread.h" + +/* test du join du main par un fils. + * + * le programme doit terminer correctement (faire le printf et s'arreter entièrement). + * valgrind doit être content. + * + * support nécessaire: + * - thread_create() + * - thread_self() dans le main + * - thread_exit() dans le main + * - retour sans thread_exit() + * - thread_join() du main par un autre thread + */ + +#ifdef USE_PTHREAD +thread_t thmain = 0; +#else +thread_t thmain = NULL; /* si votre thread_t est un pointeur */ +#endif + +static void * thfunc(void *dummy __attribute__((unused))) +{ + void *res; + int err; + + err = thread_join(thmain, &res); + assert(!err); + assert(res == (void*) 0xdeadbeef); + printf("main terminé OK\n"); + return NULL; +} + +int main() +{ + thread_t th; + int err; + + thmain = thread_self(); + + err = thread_create(&th, thfunc, NULL); + assert(!err); + + thread_exit((void*) 0xdeadbeef); + return 0; /* unreachable, shut up the compiler */ +} diff --git a/tst/21-create-many.c b/tst/21-create-many.c new file mode 100644 index 0000000..ed941dc --- /dev/null +++ b/tst/21-create-many.c @@ -0,0 +1,52 @@ +#include +#include +#include +#include +#include "thread.h" + +/* test de plein de create-destroy consécutifs. + * + * valgrind doit etre content. + * la durée du programme doit etre proportionnelle au nombre de threads donnés en argument. + * jusqu'à combien de threads cela fonctionne-t-il ? + * + * support nécessaire: + * - thread_create() + * - thread_exit() + * - thread_join() avec récupération de la valeur de retour + */ + +static void * thfunc(void *dummy __attribute__((unused))) +{ + thread_exit(NULL); + return (void*) 0xdeadbeef; /* unreachable, shut up the compiler */ +} + +int main(int argc, char *argv[]) +{ + thread_t th; + struct timeval tv1, tv2; + unsigned long us; + int err, i, nb; + void *res; + + if (argc < 2) { + printf("argument manquant: nombre de threads\n"); + return -1; + } + + nb = atoi(argv[1]); + + gettimeofday(&tv1, NULL); + for(i=0; i +#include +#include +#include +#include "thread.h" + +/* test de plein de create-destroy récursif. + * + * valgrind doit etre content. + * la durée du programme doit etre proportionnelle au nombre de threads donnés en argument. + * jusqu'à combien de threads cela fonctionne-t-il ? + * + * support nécessaire: + * - thread_create() + * - retour sans thread_exit() + * - thread_join() avec récupération de la valeur de retour + */ + +static void * thfunc(void *_nb) +{ + unsigned long nb = (unsigned long) _nb; + if ((unsigned long) nb > 0) { + thread_t th; + int err; + void *res; + err = thread_create(&th, thfunc, ((char*)_nb)-1); + assert(!err); + err = thread_join(th, &res); + assert(!err); + assert(res == ((char*)_nb-1)); + } + return _nb; +} + +int main(int argc, char *argv[]) +{ + unsigned long nb; + struct timeval tv1, tv2; + unsigned long us; + + if (argc < 2) { + printf("argument manquant: nombre de threads\n"); + return -1; + } + + nb = atoi(argv[1]); + + gettimeofday(&tv1, NULL); + thfunc((void*) nb); + gettimeofday(&tv2, NULL); + us = (tv2.tv_sec-tv1.tv_sec)*1000000+(tv2.tv_usec-tv1.tv_usec); + printf("%ld threads créés et détruits récursivement en %lu us\n", nb, us); + return 0; +} diff --git a/tst/23-create-many-once.c b/tst/23-create-many-once.c new file mode 100644 index 0000000..99fe91e --- /dev/null +++ b/tst/23-create-many-once.c @@ -0,0 +1,71 @@ +#include +#include +#include +#include +#include "thread.h" + +/* test de plein de create, puis plein de join quand ils ont tous fini + * + * valgrind doit etre content. + * la durée du programme doit etre proportionnelle au nombre de threads donnés en argument. + * jusqu'à combien de threads cela fonctionne-t-il ? + * + * support nécessaire: + * - thread_create() + * - thread_exit() + * - thread_join() sans récupération de la valeur de retour + */ + +static void * thfunc(void *dummy __attribute__((unused))) +{ + thread_exit(NULL); + return (void*) 0xdeadbeef; /* unreachable, shut up the compiler */ +} + +int main(int argc, char *argv[]) +{ + thread_t *th; + int err, i, nb; + struct timeval tv1, tv2; + unsigned long us; + + if (argc < 2) { + printf("argument manquant: nombre de threads\n"); + return -1; + } + + nb = atoi(argv[1]); + + th = malloc(nb*sizeof(*th)); + if (!th) { + perror("malloc"); + return -1; + } + + gettimeofday(&tv1, NULL); + + /* on cree tous les threads */ + for(i=0; i +#include +#include +#include +#include +#include "thread.h" + +/* test de plein de switch par plein de threads + * + * la durée du programme doit etre proportionnelle au nombre de threads et de yields donnés en argument + * + * support nécessaire: + * - thread_create() + * - thread_yield() depuis ou vers le main + * - retour sans thread_exit() + * - thread_join() avec récupération de la valeur de retour + */ + +static void * thfunc(void *_nbyield) +{ + int nbyield = (intptr_t) _nbyield; + int i; + + for(i=0; i +#include +#include +#include +#include +#include "thread.h" + +/* test de plein de switch pendant que N-1 threads sont bloqués dans join + * + * La durée du programme doit etre proportionnelle au nombre de threads et de yields donnés en argument + * + * support nécessaire: + * - thread_create() + * - thread_yield() depuis ou vers le main + * - retour sans thread_exit() + * - thread_join() avec récupération de la valeur de retour + */ + +static int nbyield; + +static void * thfunc(void *_nbth) +{ + int nbth = (intptr_t) _nbth; + if ((unsigned long) nbth > 0) { + thread_t th; + int err; + void *res; + err = thread_create(&th, thfunc, ((char*)_nbth)-1); + assert(!err); + err = thread_join(th, &res); + assert(!err); + assert(res == ((char*)_nbth)-1); + } else { + int i; + struct timeval tv1, tv2; + unsigned long us; + gettimeofday(&tv1, NULL); + for(i=0; i +#include +#include +#include +#include +#include "thread.h" + +/* test une chaine de thread avec un nombre decroissants de switch quand on descend dans la chaine. + * + * La durée du programme doit etre proportionnelle au nombre de total de yields = ( nbyield * nbthread * (nbthread+1) / 2 ) donnés en argument + * + * support nécessaire: + * - thread_create() + * - thread_yield() depuis ou vers le main + * - retour sans thread_exit() + * - thread_join() avec récupération de la valeur de retour + */ + +static int nbyield; +static int nbthread; + +static void * thfunc(void *_nbth) +{ + int nbth = (intptr_t) _nbth; + int i; + + if ((unsigned long) nbth > 0) { + thread_t th; + int err; + void *res; + err = thread_create(&th, thfunc, ((char*)_nbth)-1); + assert(!err); + + for(i=0; i<(nbyield*nbth); i++) { + thread_yield(); + } + + err = thread_join(th, &res); + assert(!err); + assert(res == ((char*)_nbth)-1); + } + else { + for(i=0; i<(nbyield*nbthread); i++) { + thread_yield(); + } + } + return _nbth; +} + +int main(int argc, char *argv[]) +{ + struct timeval tv1, tv2; + unsigned long us; + unsigned long nbth; + + if (argc < 3) { + printf("arguments manquants: nombre de threads, puis nombre de yield\n"); + return -1; + } + + nbthread = atoi(argv[1]); + nbth = nbthread; + nbyield = atoi(argv[2]); + + gettimeofday(&tv1, NULL); + thfunc((void*) nbth); + gettimeofday(&tv2, NULL); + + us = (tv2.tv_sec-tv1.tv_sec)*1000000+(tv2.tv_usec-tv1.tv_usec); + printf("%d yield avec plein de threads dans join: %ld us\n", nbyield, us); + + printf("%ld threads créés et détruits\n", nbth); + return 0; +} diff --git a/tst/51-fibonacci.c b/tst/51-fibonacci.c new file mode 100644 index 0000000..66d10bd --- /dev/null +++ b/tst/51-fibonacci.c @@ -0,0 +1,88 @@ +#include +#include +#include +#include +#include +#include "thread.h" + +/* fibonacci. + * + * la durée doit être proportionnel à la valeur du résultat. + * valgrind doit être content. + * jusqu'à quelle valeur cela fonctionne-t-il ? + * + * support nécessaire: + * - thread_create() + * - thread_join() avec récupération de la valeur de retour + * - retour sans thread_exit() + */ + +static void * fibo(void *_value) +{ + thread_t th, th2; + int err; + void *res = NULL, *res2 = NULL; + unsigned long value = (unsigned long) _value; + + /* on passe un peu la main aux autres pour eviter de faire uniquement la partie gauche de l'arbre */ + thread_yield(); + + if (value < 3) + return (void*) 1; + + err = thread_create(&th, fibo, (void*)(value-1)); + assert(!err); + err = thread_create(&th2, fibo, (void*)(value-2)); + assert(!err); + + err = thread_join(th, &res); + assert(!err); + err = thread_join(th2, &res2); + assert(!err); + + return (void*)((unsigned long) res + (unsigned long) res2); +} + +unsigned long fibo_checker( unsigned long n ) +{ + unsigned long a = 1; + unsigned long b = 1; + unsigned long c, i; + + if ( n <= 2 ) { + return 1; + } + + for( i=2; i +#include +#include +#include +#include +#include +#include "thread.h" + +/* test de faire une somme avec plein de thread sur un compteur partagé + * + * valgrind doit etre content. + * Le résultat doit etre égal au nombre de threads * 1000. + * La durée du programme doit etre proportionnelle au nombre de threads donnés en argument. + * + * support nécessaire: + * - thread_create() + * - retour sans thread_exit() + * - thread_join() sans récupération de la valeur de retour + * - thread_mutex_init() + * - thread_mutex_destroy() + * - thread_mutex_lock() + * - thread_mutex_unloc() + */ + +int counter = 0; +thread_mutex_t lock; + +static void * thfunc(void *dummy __attribute__((unused))) +{ + unsigned long i = 0; + int tmp; + + for(i=0; i<1000;i++) { + /* Verrouille la section critique accédant a counter */ + thread_mutex_lock(&lock); + tmp = counter; + thread_yield(); + tmp++; + thread_yield(); + counter = tmp; + thread_mutex_unlock(&lock); + } + + return NULL; +} + +int main(int argc, char *argv[]) +{ + thread_t *th; + int err, i, nb; + struct timeval tv1, tv2; + unsigned long us; + + if (argc < 2) { + printf("argument manquant: nombre de threads\n"); + return -1; + } + + nb = atoi(argv[1]); + + if (thread_mutex_init(&lock) != 0) { + fprintf(stderr, "thread_mutex_init failed\n"); + return -1; + } + + th = malloc(nb*sizeof(*th)); + if (!th) { + perror("malloc"); + return -1; + } + + gettimeofday(&tv1, NULL); + /* on cree tous les threads */ + for(i=0; i +#include +#include +#include +#include +#include +#include +#include "thread.h" + +/* test de faire une somme avec plein de thread sur un compteur partagé + * + * valgrind doit etre content. + * Les résultats doivent etre égals au nombre de threads * 1000. + * La durée du programme doit etre proportionnelle au nombre de threads donnés en argument. + * + * support nécessaire: + * - thread_create() + * - retour sans thread_exit() + * - thread_join() sans récupération de la valeur de retour + * - thread_mutex_init() + * - thread_mutex_destroy() + * - thread_mutex_lock() + * - thread_mutex_unloc() + */ + +#define NB_MUTEX 10 + +int counter[NB_MUTEX] = { 0 }; +thread_mutex_t lock[NB_MUTEX]; + +static void * thfunc(void *_nb) +{ + unsigned long nb = (unsigned long) _nb; + unsigned long i = 0; + int tmp; + + int m = nb % NB_MUTEX; + + for(i=0; i<1000;i++) { + /* Verrouille la section critique accédant a counter */ + thread_mutex_lock(&lock[m]); + tmp = counter[m]; + thread_yield(); + tmp++; + thread_yield(); + counter[m] = tmp; + thread_mutex_unlock(&lock[m]); + } + + return NULL; +} + +int main(int argc, char *argv[]) +{ + thread_t *th; + int i, nbthrd; + int err, nb; + struct timeval tv1, tv2; + unsigned long us; + + if (argc < 2) { + printf("argument manquant: nombre de threads\n"); + return -1; + } + + nb = atoi(argv[1]); + nbthrd = nb * NB_MUTEX; + + for(i=0; i +#include +#include +#include +#include +#include +#include +#include "thread.h" + +/* test de validation de la pré-emption (et par effet de bord de certaines implémentation de priorités) + * + * valgrind doit etre content. + * La durée du programme doit etre proportionnelle au nombre de threads donnés en argument. + * L'affichage doit être une série d'id alternée dans un ordre plus ou + * moin aléatoire, et non une suite de 0, puis de 1, puis de 2, ... + * + * support nécessaire: + * - thread_create() + * - retour sans thread_exit() + * - thread_join() sans récupération de la valeur de retour + */ +#define NB_ITER 100 +#define ITER_LENGTH 1000000 + +static int fini = 0; +static double score = 0; +static long *values = NULL; + +static void * thfunc( void *arg ) +{ + unsigned long i, j = 0; + int me = (intptr_t)arg; + + for(i=0; i +#include +#include +#include +#include "thread.h" + +/* test de detection d'un deadlock lors d'un cycle de thread qui joignent tous le suivant. + * main(th0) joine th1 qui joine th2 qui joine main. + * il faut qu'un join renvoie EDEADLK quand il detecte le deadlock, et les autres renvoient 0 normalement. + */ + + +static thread_t th0, th1, th2; +int totalerr = 0; + +static void * thfunc2(void *dummy __attribute__((unused))) +{ + void *res; + int err = thread_join(th0, &res); + printf("join th2->th0 = %d\n", err); + totalerr += err; + thread_exit(NULL); +} + +static void * thfunc1(void *dummy __attribute__((unused))) +{ + void *res; + int err = thread_create(&th2, thfunc2, NULL); + assert(!err); + err = thread_join(th2, &res); + printf("join th1->th2 = %d\n", err); + totalerr += err; + thread_exit(NULL); +} + +int main() +{ + void *res; + int err; + + th0 = thread_self(); + + err = thread_create(&th1, thfunc1, NULL); + assert(!err); + err = thread_join(th1, &res); + printf("join th0->th1 = %d\n", err); + totalerr += err; + printf("somme des valeurs de retour = %d\n", totalerr); + assert(totalerr == EDEADLK); + + if ( totalerr == EDEADLK ) { + return EXIT_SUCCESS; + } + else { + return EXIT_FAILURE; + } +}