= 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 [[../ClasseChannel|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 [[../ClasseChannel|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.