Threads
Každé vlákno je reprezentováno dynamicky alokovanou strukturou thread_t:
typedef struct _thread {
unsigned *sp;
unsigned stack[STACKSIZE / 4];
unsigned interrupts_previous_status;
unsigned interrupts_lock_count;
enum _state state;
unsigned sleep_start_time;
unsigned sleep_delay;
struct _thread *left, *right;
struct _thread *next;
unsigned resource_count;
} *thread_t;
*sp ukazatel na vrchol zásobníku; kvůli implementaci přepínání kontextu
musí být první položkou struktury
stack zásobník, jehož velikost je určena preprocesorovou konstantou v
souboru global.h
interrupts_previous_status
sem se ukládá stav přerušení při prvním volání rekurzivního
zakázání přerušení, který je obnoven po stejném počtu volání
rekurzivního povolení přerušení
interrupts_lock_count
určuje bilanci mezi počtem volání rekurzivního zakázání a povolení
přerušení
state označuje stav vlákna:
running - vlákno je připraveno k naplánování
sleeping - vlákno spí na určitou dobu (bude probuzeno po vypršení
timeoutu)
suspended - vlákno spí, dokud ho jiné vlákno neprobudí
zombie - vlákno je ve stavu, kdy buď skončilo samo, nebo jej jiné
vlákno zabilo, ale ještě na něj nebylo zavoláno "join"
sleep_start_time, sleep_delay
tyto 2 hodnoty mají význam pouze pro sleeping vlákna, podle nich
plánovač pozná, kdy je má vzbudit
*left, *right
ukazatele na vedlejší vlákna, pokud jsou sdružována v nějaké
pomocné datové struktuře (viz níže)
*next ukazatel na další vlákno ve frontách, které spravují synchronizační
primitiva
resource_count
počet "resources", kolik dané vlákno drží (např. pokud vlákno
úspěšně zavolá mutex_lock(), drží další "resource")
Running vlákna jsou udržována v obousměrném cyklickém spojovém seznamu.
Obousměrný je proto, aby se z něj vlákna rychle odebírala, cyklický proto,
že pri rotovaní seznamu (při plánování) stačí jenom změnit ukazatel na
"začátek" seznamu.
Sleeping vlákna reprezentuje obousměrný tříděný spojový seznam. Obousměrný
je opět kvůli rychlosti odebírání vláken. Vlákna jsou v seznamu tříděna
podle rostoucího zamýšleného času vzbuzení.
Suspended vlákna nejsou v tomto modulu držena v žádné pomocné datové
struktuře, ale u jednotlivých synchronizačních primitiv.
Zombie vlákna nejsou v žádné další datové struktuře, ukazatele na ně mají
příslušná rodičovská vlákna.
Při změně stavu vlákna dochází současně k aktualizaci těchto seznamů. Při
uspání aktuálního (právě běžícího) vlákna se následně volá plánovač.
Všechna existující vlákna jsou navíc v jednosměrném spojovém seznamu
platných vláken. Pokud se k nějakému vláknu přistupuje (ve funkcích
uvedených níže), nejprve se musí prohledat tento seznam, jestli je thread
platný ukazatel na existující vlákno.
Kvůli zajištění konzistence těchto pomocných datových struktur a zaručení
správných výsledků při dotazovnání na ně jsou veškeré operace s nimi
prováděny při zakázaných přerušeních.
Interface
thread_t thread_create(void (*start_func)(void *), void *data);
Funkce vytvoří nové vlákno, s vlastním zásobníkem a dalšími
pomocnými proměnnými, vrací ukazatel na strukturu reprezentující
toto vlákno. Vlastní běh vlákna začíná voláním funkce START_FUNC s
jediným parametrem DATA a končí při návratu z tého funkce nebo
zabitím jiným vláknem.
thread_t thread_get_current();
Vrátí ukazatel na aktuální vlákno.
unsigned thread_get_id(thread_t thread);
Vrací ID vlákna THREAD.
int thread_join(thread_t thread);
Funkce se zablokuje, dokud vlákno THREAD neskončí; tehdy vrací 0.
Pokud je THREAD neplatný ukazatel nebo se z něj v průběhu čekání
stane neplatný ukazatel, vrací funkce okamžitě hodnotu EINVAL. K
blokování (uspání do stavu suspended) a buzení vlákna se používá
podmínková proměnná (viz thread_kill).
int thread_join_timeout(thread_t thread, unsigned msec);
Provádí totéž co předchozí funkce s tím rozdílem, že pokud vyprší
timeout zadaný jako MSEC milisekund, vrátí ETIMEDOUT.
void thread_sleep(unsigned sec);
Uspí (do stavu sleeping) aktuální vlákno na SEC sekund.
void thread_usleep(unsigned usec);
Uspí (do stavu sleeping) aktuální vlákno na USEC mikrosekund. Pokud
je USEC < 1000, čeká aktivně, pokud je to naopak příliš veliké
číslo (další výpočet s ním by přetekl), zaokrouhlí se na sekundy a
zavolá předcházející funkce.
void thread_yield();
Zavolá plánovač - aktuální vlákno se vzdá procesoru.
Privátní fuknce (používány zejména synchronizačními primitivy):
void thread_suspend();
Uspí (do stavu suspended) aktuální vlákno.
void thread_wakeup(thread_t thread);
Vzbudí spící (jak sleeping, tak suspended) vlákno THREAD.
void thread_kill(thread_t thread);
Pošle signál (podmínkovou proměnnou společnou pro všechna vlákna,
protože do struktury thread_t přidat proměnnou typu mutex_t ani
cond_t nejde kvůli cyklické závislosti), čímž dojde k vzbuzení také
těch vláken, která čekají (join) na vlákno THREAD, a vlákno THREAD
zabije.