Reputation: 1
I need to make a LIFO scheduler in C using pthread.
The LIFO scheduler contains a single stack of tasks whose maximum size is given by the
parameter passed to sched_init. Initially, the stack contains only the initial task. When a
thread has nothing to do, it unstacks a task from the stack and performs it; if there are no tasks ready
the thread goes to sleep waiting for one (for example, the sched_spawn function can
try to wake up a thread). When a new task is created, it is stacked on the stack.
The scheduler terminates when the stack is empty and all threads are asleep.
You'll notice that the stack is a shared structure, so you'll need to protect it with synchronization pri- mitives.
mitives. You'll also need to use synchronization primitives to wake up any
and terminate them when the scheduler become idle.
That's my file.h:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
struct scheduler;
typedef void (*taskfunc)(void*, struct scheduler *);
// Structure pour une tâche
typedef struct {
taskfunc task;
void *closure;
} Task;
typedef struct scheduler
{
Task *tasks;
int qlen;
int nthreads;
int size;
pthread_t *threads;
pthread_mutex_t mutex; // Mutex pour la synchronisation
pthread_cond_t condition; // Condition pour attendre les tâches
} scheduler;
static inline int
sched_default_threads()
{
return sysconf(_SC_NPROCESSORS_ONLN);
}
int sched_init(int nthreads, int qlen, taskfunc f, void *closure);
int sched_spawn(taskfunc f, void *closure, struct scheduler *s);
#include "sched.h"
/*************************************************/
/* */
/* sucre syntaxique */
/* */
/*************************************************/
#define AND &&
#define OR ||
#define ISNOT !=
#define NOT !
#define then
typedef enum { FALSE, TRUE} bool;
/*************************************************/
/* */
/* predeclarations */
/* */
/*************************************************/
/* initialise un struct scheduler vide */
void initVide(struct scheduler *L);
/* renvoie 1 si le struct scheduler en parametre est vide, 0 sinon */
bool estVide(struct scheduler *L);
/* renvoie le premier element du struct scheduler en parametre */
taskfunc premier(struct scheduler *L);
/* renvoie un nouveau struct scheduler correspondant a celui en parametre, avec l'element task ajoute en haut de la pile */
struct scheduler ajoute(taskfunc task, void *closure, struct scheduler *L);
/* modifie le struct scheduler en parametre: task est ajoute comme premier element */
void empile(taskfunc task, void *closure, struct scheduler* L);
/* modifie le struct scheduler en parametre: le premier element est retire */
void depile(struct scheduler* L);
/* affichage simple du struct scheduler */
void affiche(struct scheduler *L);
/* longueur en recursif et en iteratif */
int longueur (struct scheduler *L);
/*************************************************/
/* */
/* briques de base */
/* */
/*************************************************/
void initVide(struct scheduler *L) {
L->tasks = NULL;
L->qlen = 0;
L->nthreads = 0;
L->size = 0;
L->threads = NULL;
pthread_mutex_init(&(L->mutex), NULL);
pthread_cond_init(&(L->condition), NULL);
}
bool estVide(struct scheduler *L) {
return L->size == 0;
}
taskfunc premier(struct scheduler *L) {
if (estVide(L)) {
fprintf(stderr, "Erreur : le struct scheduler est vide.\n");
exit(EXIT_FAILURE);
}
return L->tasks[0].task;
}
struct scheduler ajoute(taskfunc task, void *closure, struct scheduler *L) {
Task newTask = {task, closure};
if (L->size >= L->qlen) {
fprintf(stderr, "Erreur : le scheduler est plein.\n");
exit(EXIT_FAILURE);
}
L->tasks[L->size++] = newTask;
return *L;
}
void empile(taskfunc task, void *closure, struct scheduler *L)
{
ajoute(task, closure, L) ;
}
void depile(struct scheduler *L) {
if (estVide(L)) {
fprintf(stderr, "Erreur : le struct scheduler est vide.\n");
exit(EXIT_FAILURE);
}
free(L->tasks[L->size - 1].closure); // Libère la mémoire de la tâche retirée
L->size--; // Réduit la taille du struct scheduler
}
/*************************************************/
/* */
/* Affiche, avec les briques de base */
/* */
/*************************************************/
void affiche(struct scheduler *L) {
for (int i = 0; i < L->size; i++) {
printf("%p ", L->tasks[i].task);
}
printf("\n");
}
/*************************************************/
/* */
/* Longueur, sans les briques de base */
/* */
/*************************************************/
int longueur (struct scheduler *L)
{
return L -> size;
}
/*************************************************/
/* */
/* Libere la memoire */
/* */
/*************************************************/
void VideScheduler(struct scheduler *L)
{
if(NOT(estVide(L)))
{
depile(L);
VideScheduler(L);
}
}
// Fonction de tâche simple qui affiche un message
void *task_function(void *arg) {
char *message = (char *)arg;
printf("Thread %lu executing task: %s\n", pthread_self(), message);
sleep(1); // Simulation d'une tâche longue
return NULL;
}
int sched_init(int nthreads, int qlen, taskfunc f, void *closure) {
// Faut initialiser notre ordonnanceur et créez les threads si nécessaire
// Il faut également initialiser la pile de tâches avec la tâche initiale
struct scheduler *scheduler = (struct scheduler *)malloc(sizeof(struct scheduler));
if (scheduler == NULL) {
perror("Erreur lors de l'allocation du struct scheduler");
return -1;
}
scheduler->nthreads = (nthreads == 0) ? sched_default_threads() : nthreads;
scheduler->qlen = qlen;
scheduler->tasks = (Task *)malloc(qlen * sizeof(Task));
if (scheduler->tasks == NULL) {
perror("Erreur lors de l'allocation de la mémoire pour les tâches");
free(scheduler);
return -1;
}
// Ajout de la tâche initiale au tableau de tâches
scheduler->tasks[0].task = f;
scheduler->tasks[0].closure = closure;
scheduler->size++;
// Vérification si la taille du tableau est suffisante pour le nombre de threads à créer
if (scheduler->nthreads > qlen) {
printf("Ajustement du nombre de threads au maximum de la capacité du tableau de tâches.\n");
scheduler->nthreads = qlen;
}
// Création des threads
scheduler->threads = (pthread_t *)malloc(scheduler->nthreads * sizeof(pthread_t));
if (scheduler->threads == NULL) {
perror("Erreur lors de l'allocation de la mémoire pour les threads");
free(scheduler->tasks);
free(scheduler);
return -1;
}
for (int i = 0; i < scheduler->nthreads; i++) {
pthread_create(&(scheduler->threads[i]), NULL,(void *(*)(void *))f, closure);
}
// Ajout de la tâche initiale
empile(f, closure, scheduler);
// Attente de la fin des threads
for (int i = 1; i < scheduler->nthreads; i++) {
pthread_join(scheduler->threads[i], NULL);
}
return 0;
}
int sched_spawn(taskfunc f, void *closure, struct scheduler *s) {
return -1;
}
int main(){
char *message = "Task1";
// Initialisation du scheduler avec 2 threads et une longueur de file de 5
int result = sched_init(0, 4, task_function, (void*) message); // Initialisation du scheduler
if (result != 0) {
fprintf(stderr, "Erreur lors de l'initialisation du scheduler\n");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
I've tried to do this as a first test but I think I'm off-topic. Could you please tell me if I'm in agreement with what I've explained in the statement? I'd like to point out that I have an appendix file that implements the quicksort and serves as an example where I need to use my LIFO scheduler to execute the quicksort and see how long it took to do the quicksort.
Upvotes: 0
Views: 41