Il modulo micro
Il modulo micro ha una funzione per richiamare altre funzioni come microthread (micro e microatomic), una classe per la creazione di canali (Channel) e un decorator (microfunc) per definire una funzione che sarà sempre eseguita in microthreads.
Una funzione può essere dichiarata in uno di questi modi:
@microfunc() def funzione_da_eseguire(param1, param2): print param1, param2 @microfunc(is_micro=True) # possibili args sono is_micro=False e is_atomic=False def funzione_da_eseguire(param1, param2): print param1, param2 @microfunc(is_atomic=True) def funzione_da_eseguire(param1, param2): print param1, param2
Se si usa la modalità default (la prima) al momento della definizione della funzione viene avviato in un microthread un dispatcher per quella funzione che sta in ascolto su un channel (vedi la classe Channel con prefer_sender = True e micro_send = False).
Al momento della chiamata della funzione il dispatcher riceve tramite il channel la chiamata e lo scheduler viene informato che il dispatcher è in attesa di eseguire del codice. Il dispatcher esegue le chiamate una alla volta, senza avviare altri microthreads.
In realtà bisogna aggiungere che al posto di un channel è usato un oggetto Channel e il passaggio dei dati (cioè gli argomenti della chiamata) avviene con i metodi sendq e recvq (vedi la classe Channel). Questo significa che se il dispatcher non è pronto a ricevere al momento della chiamata della funzione, il chiamante accoda i dati in una lista e questo evita che si blocchi il chiamante.
Questo meccanismo fa si che ci sia un solo microthread per l'esecuzione di una certa funzione, e che in esso le varie chiamate siano eseguite in sequenza. Ma allo stesso tempo si ottiene che il chiamante di quella funzione non risulti mai bloccato dalla chiamata.
TODO: Elenco delle microfunc che usano questa modalità
is_micro
Se si usa la modalità is_micro si ottiene una funzione che, quando chiamata, viene eseguita in un nuovo microthread.
In questo caso ogni chiamata produce un microthread e quindi, naturalmente, il chiamante non risulta mai bloccato dalla chiamata. Diversamente dalla prima modalità si ha che le varie chiamate possono venire eseguite in parallelo.
TODO: Elenco delle microfunc che usano questa modalità e delle chiamate dirette alla funzione micro(...)
is_atomic
La modalità is_atomic può essere utile quando si avvia lo schedulatore di Python Stackless in modo preemptive. Con questo scheduling lo schedulatore riprende il controllo senza che sia il codice del microthread corrente a richiederlo.
Se un microthread accede a risorse condivise dagli altri microthread, ad esempio variabili membri di oggetti condivisi, si possono avere i cosidetti pezzi di codice critici in cui non si deve permettere allo schedulatore di interromperci.
Se si usa la modalità is_atomic si ottiene una funzione che, quando chiamata, viene eseguita in un microthread che non sarà mai interrotto fino alla sua terminazione.
In Netsukuku si usa la modalità cooperative. In questa modalità lo schedulatore non riprende il controllo da un microthread in modo autonomo. Quindi, il codice eseguito in ogni microthread è responsabile di assicurarsi che lo schedulatore riprenda il controllo in un tempo accettabile affinché gli altri microthread abbiano l'opportunità di venire eseguiti.
Di conseguenza, se si hanno pezzi di codice critici è sufficiente fare attenzione in questi frangenti a non eseguire funzioni che restituiscano il controllo allo schedulatore.
Per questo la modalità is_atomic non è stata mai usata nel codice.