C'est quoi ?
Voici la première beta publique d'Opale, un noyau embarqué qui a donc pour vocation de permettre à des programmes d'adopter une structure multithreadée.
Comment ça marche ?
Opale est pour l'instant distribuée sous forme de lib statique. Elle propose des fonctions qui permettent d'initialiser le noyau, de lancer le noyau, de gérer les tâches et les outils de synchronisation et de communication.
Commençons par l'ordonnancement.
C'est l'algorithme qui permet de choisir entre toutes les tâches existante, celle qui va être exécutée. Opale propose deux méthodes: par priorités strictes, ou par priorités tournantes.
Priorités strictes: telle que distribuée, la bibliothèque permet d'avoir 64 tâches, chacune possédant un niveau de priorité unique (de 0, le plus haut niveau, à 63). Ces niveaux sont exculsifs, ainsi il est impossible que deux tâches aient le même niveau.
Lorsque l'ordonnanceur doit élire la tâche à exécuter, il choisira toujours la tâche de plus haute priorité prête à être exécutée.
Priorités tournantes:les priorités sont regroupées en 8 niveaux de 8 priorités. Les priorités 0 à 7 sont au niveau 0, les priorités 8 à 15 au niveau 1, etc. L'ordonnanceur va d'abord déterminer le niveau de plus haute priorité contenant des tâches prêtes, puis exécuter à tour de rôle les tâches qu'il contient. Lorsqu'un niveau est vide, on passe au suivant, etc.
Tâche inactive:
La tâche de plus faible priorité est allouée par le noyau et reçoit automatiquement la priorité la plus faible (63).
Suspension d'une tâche:
Il es possible de mettre en sommeil une tâche pour un nombre donné de ticks. Un tick est l'intervale de temps séparant deux déclenchements de l'auto-int 5, donc environ 18Hz.
Préemption:
Si un évênement quelconque rend prête à l'exécution une tâche plus prioritaire que la tâche courante , celle-ci est automatiquement mise en suspend, et la tâche plus prioritaire est exécutée.
Sémaphores et Mailbox:
Ces deux objets permettent de synchroniser et de faire communiquer les tâche. Un sémaphore est un compteur qui peut être pris ou relâché par une tâche. Prendre le compteur le décrémente, et le relâcher l'incrémente. Une tentative de prise d'un compteur qui est à 0 provoque l'endormissement de la tâche jusqu'à ce que le sémaphore soit relùaché par une autre tâche et que la tâche soit celle de plus haute priorité en attente de ce sémaphore.
Il en va de même pour les mailbox, sauf qu'un message (un pointeur typiquement) remplace le compteur. Un seul message peut être posté à la fois, mais plusieurs tâches peuvent être mises en attente de ce message. La tâche de plus haute priorité en attente recevra le message lorsu'il sera posté.
Divers:
première beta publique, complètement unsafe (à éviter sur une vrai TI a priori)
tout compris, envron 1400 octets
deux exemples inclus: l'un qui mesure l'occupation CPU (appuyez sur une touche)
l'autre qui exécute pas mal de tâches pour le fun (mais n'appuyez pas sur une touche)
il est évident que toutes les règles de codages de programmes multithreadés s'appliquent en utilisant Opale, par exemple ça serait une mauvaise idée d'allouer de la mémoire dynamiquement dans une tâche qui peut se faire préempter, ou d'appeler des fonctions d'AMS non réentrante, etc.
Testé uniquement sous VTI, et encore une fois c'est probablement très bugué a priori
à suivre
API (en anglais, et j'espère avoir été un peu clair.... c'estsurment pas complètement de l'anglais en fait):
http://mapage.noos.fr/cmolon/opale/
Pack:
http://mapage.noos.fr/cmolon/opale/Opale.zip
(j'espère qu'il y a tout ... et que j'ai pas fait trop de fautes

edit: ti screen d'une app utilisant 5 tâches, une mailbox et un sémaphore:

edit2: le code du prog en question:
#include <tigcclib.h>
#include "opale.h"
t_SEMAPHORE sem1;
t_MAILBOX mb1;
void counterTaskFunc( register void* args asm("%a0") )
{
for( ;; )
{
op_SemaphorePend( &sem1 );
(*(unsigned short*)args) ++;
}
}
void readerTaskFunc( register void* args asm("%a0") )
{
unsigned short i;
char sbuffer[30];
for( ;; )
{
op_SemaphorePost( &sem1 );
i = *(unsigned short*)args;
sprintf( sbuffer, "Task: counter: %d", i);
DrawStr (10, 10, sbuffer, A_REPLACE);
if( i == 10 )
op_MailBoxPost( &mb1, (void*)1 );
op_TaskWaitForTicks( 20 );
}
}
void testTaskFunc( register void* args asm("%a0") )
{
char i = (long)args;
unsigned short count = 0;
char sbuffer[50];
for( ;; )
{
sprintf( sbuffer, "Task: coop. counter%d - %d", i, count ++ );
DrawStr (10, 10+10*i, sbuffer, A_REPLACE);
}
}
void killerTaskFunc( register void* args asm("%a0") )
{
args = args;
op_MailBoxPend( &mb1 );
op_KernelStop();
}
// Main Function
void _main(void)
{
t_TASK readerTask,counterTask,testTask,testTask2,killerTask;
t_op_BUFFER buffer;
char readerTaskStack[ 500 ];
char counterTaskStack[ 200 ];
char testTaskStack2[ 500 ];
char testTaskStack[ 500 ];
char killerTaskStack[ 200 ];
char sbuffer[30];
unsigned short i = 0;
sprintf( sbuffer, "NUM_TASKS: %d", NUM_TASKS);
DrawStr (10, 50, sbuffer, A_REPLACE);
// autorise le groupe de niveau 0 (bit 0 de l'argument à 1) à avoir des priorités tournantes
op_KernelInit( &buffer, 1 );
op_SemaphoreInit( &sem1, 0 );
op_MailBoxInit( &mb1, NULL );
op_TaskStart( &testTask, testTaskFunc, &testTaskStack[ 500 ], 1, (void*)1 );
op_TaskStart( &testTask2, testTaskFunc, &testTaskStack2[ 500 ], 2, (void*)2 );
op_TaskStart( &readerTask, readerTaskFunc, &readerTaskStack[ 500 ], 0, &i );
op_TaskStart( &counterTask, counterTaskFunc, &counterTaskStack[ 200 ], 4, &i );
op_TaskStart( &killerTask, killerTaskFunc, &killerTaskStack[ 200 ], 5, NULL );
op_KernelStart();
}