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.