Differences between revisions 1 and 2
Revision 1 as of 2015-06-25 09:15:37
Size: 11895
Editor: lukisi
Comment:
Revision 2 as of 2015-06-25 10:39:40
Size: 12086
Editor: lukisi
Comment:
Deletions are marked like this. Additions are marked like this.
Line 7: Line 7:

 Nota: Questa tipologia di thread è chiamata anche [[https://en.wikipedia.org/wiki/Green_threads|green thread]]. Il termine tasklet è preso dalla terminologia usata in Stackless Python.

Tasklet System

Tasklet

Quasi tutti i moduli che compongono il demone ntkd fanno uso di tasklet per svolgere i loro compiti. Si tratta di thread cooperativi, cioè che permettono allo schedulatore di passare l'esecuzione ad un altro thread soltanto su specifica autorizzazione da parte del thread corrente.

  • Nota: Questa tipologia di thread è chiamata anche green thread. Il termine tasklet è preso dalla terminologia usata in Stackless Python.

L'autorizzazione a schedulare altre tasklet viene data da parte della tasklet corrente con una delle seguenti modalità:

  • La tasklet corrente dichiara esplicitamente di voler passare il controllo.
  • La tasklet corrente chiama una delle seguenti funzioni bloccanti:
    • Attesa di un certo tempo.
    • Lettura del contenuto di un file. TODO

    • Scrittura di un file. TODO

    • Attesa di una comunicazione dalla rete.
    • Invio di una comunicazione nella rete.
    • Esecuzione di un comando e attesa dell'esito.

Inoltre la tasklet corrente può creare altre tasklet, attraverso la funzione spawn . Questo consiste nel dire allo schedulatore che una certa chiamata ad una funzione dovrà essere eseguita in una nuova tasklet. La nuova tasklet non verrà comunque iniziata dallo schedulatore fino a quando la tasklet corrente non lo consentirà attraverso una delle modalità dette in precedenza.

La funzione spawn restituisce un handle per la tasklet creata. Tramite questo handle è possibile verificare se la tasklet è ancora in esecuzione. Si può inoltre richiedere la terminazione della tasklet.

Alla funzione spawn si può indicare se la nuova tasklet dovrà essere joinable . In questo caso tramite il suo handle sarà possibile chiamare il metodo join : la tasklet corrente si blocca in attesa che la tasklet individuata dall'handle termini e poi riceve il valore di ritorno dell'esecuzione della tasklet; si tratta di un void * che di norma è il valore di ritorno della funzione con cui la tasklet ha avuto inizio.

La tasklet corrente può richiedere di terminare se stessa, con un eventuale valore di ritorno, indipendentemente dalla profondità di chiamate nel suo stack che ha raggiunto.

Due tasklet possono comunicare tra loro attraverso un canale . Se due tasklet condividono un canale questo fornisce loro un meccanismo per comunicare tra loro senza bloccarsi.

E' stata realizzata una libreria che contiene alcune interfacce di programmazione verso un generico sistema di tasklet. Si chiama ntkd-tasklet-system.

Questa libreria sarà una dipendenza nel pacchetto software di ogni singolo modulo. Ma tali pacchetti non saranno tenuti ad avere una dipendenza sulla specifica libreria che implementa il sistema di tasklet.

La libreria ntkd-tasklet-system sarà una dipendenza anche nel pacchetto software che raccoglie i vari moduli e produce l'eseguibile del demone ntkd. Il compito di tale pacchetto sarà quello di fornire ai vari moduli una implementazione delle interfacce del sistema di tasklet, quindi avrà una dipendenza anche sulla specifica libreria che usa a tale scopo.

Si cerca così di rendere i vari moduli del demone ntkd indipendenti dalla implementazione delle tasklet. La prima implementazione che useremo nel demone sarà la libreria Tasklet v1.0.1 ma dovrebbe essere possibile usare altre implementazioni.

Interfacce

L'interfaccia INtkdTasklet prevede questi metodi:

  • void schedule()
  • Passa il controllo ad altre tasklet.
  • void ms_wait(int msec)
  • Blocca la tasklet corrente per il tempo indicato, ma consente alle altre di proseguire.
  • [NoReturn] void exit_tasklet(void * ret)

  • Termina la tasklet corrente.
  • INtkdTaskletHandle spawn(INtkdTaskletSpawnable sp, bool joinable=false)
  • Crea una nuova tasklet, come descritto sotto.
  • NtkdTaskletCommandResult exec_command(string cmdline)

  • Avvia un nuovo processo e attende il suo esito. E' bloccante per la tasklet corrente, ma consente alle altre di proseguire.
  • INtkdServerStreamSocket get_server_stream_socket(uint16 port) throws Error
  • Crea un socket e lo mette in ascolto su una porta TCP. La chiamata non è bloccante, ma le successive operazioni lo saranno; si veda sotto i metodi previsti dall'interfaccia INtkdServerStreamSocket. Ottenere il socket tramite questo meccanismo consentirà in seguito di effettuare le altre operazioni bloccando solo la tasklet corrente e consentendo alle altre di proseguire.
  • INtkdConnectedStreamSocket get_client_stream_socket(string dest_addr, uint16 dest_port, string? my_addr=null) throws Error
  • Crea un socket e lo connette ad un server TCP. E' bloccante per la tasklet corrente, ma consente alle altre di proseguire. Anche le successive operazioni sul socket saranno bloccanti; si veda sotto i metodi previsti dall'interfaccia INtkdConnectedStreamSocket. Il meccanismo fornito consentirà di bloccare solo la tasklet corrente.
  • INtkdServerDatagramSocket get_server_datagram_socket(uint16 port, string dev) throws Error
  • Crea un socket UDP, lo associa ad una specifica interfaccia di rete, lo abilita alla ricezione di messaggi broadcast sulla porta specificata. La chiamata non è bloccante, ma le successive operazioni lo saranno; si veda sotto i metodi previsti dall'interfaccia INtkdServerDatagramSocket. Ottenere il socket tramite questo meccanismo consentirà in seguito di effettuare le altre operazioni bloccando solo la tasklet corrente e consentendo alle altre di proseguire.
  • INtkdClientDatagramSocket get_client_datagram_socket(uint16 port, string dev) throws Error
  • Crea un socket UDP, lo associa ad una specifica interfaccia di rete, lo abilita all'invio di messaggi broadcast sulla porta specificata. La chiamata non è bloccante, ma le successive operazioni lo saranno; si veda sotto i metodi previsti dall'interfaccia INtkdClientDatagramSocket. Ottenere il socket tramite questo meccanismo consentirà in seguito di effettuare le altre operazioni bloccando solo la tasklet corrente e consentendo alle altre di proseguire.
  • INtkdChannel get_channel()
  • Crea un canale.

L'interfaccia INtkdTaskletSpawnable prevede questi metodi:

  • void * func()

L'interfaccia INtkdTaskletHandle prevede questi metodi:

  • bool is_running()
  • void kill()
  • bool is_joinable()
  • void * join()

La classe NtkdTaskletCommandResult ha questi membri:

  • string stdout
  • string stderr
  • int exit_status

L'interfaccia INtkdServerStreamSocket si usa lato server. Essa prevede questi metodi:

  • INtkdConnectedStreamSocket accept() throws Error
  • Blocca la tasklet corrente in attesa di una connessione, ma consente alle altre tasklet di proseguire.
  • void close() throws Error

L'interfaccia INtkdConnectedStreamSocket si usa su entrambi gli end point della connessione. Essa prevede questi metodi:

  • unowned string _peer_address_getter()
  • In realtà si chiama con la property peer_address. Si usa sul server dopo aver ricevuto una connessione. Riporta l'indirizzo del client.
  • unowned string _my_address_getter()
  • In realtà si chiama con la property my_address. Si usa sul server dopo aver ricevuto una connessione. Riporta l'indirizzo a cui il server è stato contattato.
  • size_t recv(uint8* b, size_t maxlen) throws Error
  • Blocca la tasklet in attesa di dati dall'altro end point della connessione, ma consente alle altre tasklet di proseguire.
  • Il valore restituito dalla funzione sarà sempre positivo. In caso di errore la funzione lancia un apposito Error. In particolare in caso di 0 bytes (connessione chiusa) viene lanciato l'errore IOError.Closed. Questo vale per tutte le funzioni di lettura e scrittura sui socket.
  • void send(uint8* b, size_t len) throws Error
  • Blocca la tasklet in attesa di completare la trasmissione all'altro end point della connessione, ma consente alle altre tasklet di proseguire.
  • void close() throws Error

L'interfaccia INtkdServerDatagramSocket prevede questi metodi:

  • size_t recvfrom(uint8* b, size_t maxlen, out rmt_ip, out rmt_port) throws Error
  • Blocca la tasklet in attesa di leggere un pacchetto UDP broadcast tramite l'interfaccia di rete, ma consente alle altre tasklet di proseguire.
  • void close() throws Error

L'interfaccia INtkdClientDatagramSocket prevede questi metodi:

  • size_t sendto(uint8* b, size_t len) throws Error
  • Blocca la tasklet in attesa di inviare un pacchetto UDP broadcast sull'interfaccia di rete, ma consente alle altre tasklet di proseguire.
  • void close() throws Error

L'interfaccia INtkdChannel prevede questi metodi:

  • void send(Value v)
  • Accoda un messaggio per la trasmissione su questo canale e blocca la tasklet corrente finché non sia stato letto.
  • void send_async(Value v)
  • Accoda un messaggio per la trasmissione su questo canale ma non blocca la tasklet e nemmeno rilascia il controllo allo schedulatore.
  • int get_balance()
  • Ci dice se ci sono messaggi in attesa di venire letti (valore positivo) o se ci sono altre tasklet in attesa di messaggi da leggere (valore negativo).
  • Value recv()
  • Legge il prossimo messaggio. Blocca la tasklet in attesa di un messaggio se non ce ne sono. Garantisce che venga rispettata la priorità, nel caso in cui altre tasklet cerchino di leggere dallo stesso canale.
  • Value recv_with_timeout(int timeout_msec) throws ZcdChannelError

  • Legge il prossimo messaggio. Blocca la tasklet in attesa di un messaggio se non ce ne sono, ma prevede un tempo massimo di attesa oltre il quale riprende il controllo.

Quando si desidera avviare una tasklet occorre creare appositamente una istanza di una classe che implementa l'interfaccia INtkdTaskletSpawnable. Tale classe ha come membri tutti i dati che andrebbero passati alla funzione che inizia il task. Inoltre ha il metodo func che è proprio quello che costituisce il corpo della tasklet che si vuole avviare. La classe che si istanzia può essere interna ad un'altra classe per fare in modo che abbia accesso ai suoi membri privati.

Poi si valorizzano i membri della istanza di INtkdTaskletSpawnable, come sarebbero passati gli argomenti alla funzione. Quindi si chiama il metodo spawn di INtkdTasklet, al quale si può indicare se si desidera che la nuova tasklet sia joinable.

L'istanza di INtkdTaskletHandle restituita dal metodo spawn potrà essere usata per fare il join della tasklet (se è joinable) oppure in caso di necessità per abortirla.

Quando il metodo spawn restituisce il controllo al chiamante, esso è tenuto a rimuovere immediatamente il suo riferimento alla istanza di INtkdTaskletSpawnable.

Il metodo spawn si occupa di memorizzare un riferimento all'istanza di INtkdTaskletSpawnable per evitare che venga distrutta e di mantenerlo in vita fino alla fine della tasklet che andiamo a creare. Poi il metodo crea la nuova tasklet, che eseguirà un'apposita funzione che chiamiamo real_func , con l'implementazione che si intende adottare, sia essa Tasklet (basata su GNU Pth) o similari. Di norma la funzione real_func ha in C la signature "void *(*)(void *)". Come argomento viene passata l'istanza di INtkdTaskletSpawnable dopo averne fatto il cast a (void *).

Nella nuova tasklet, la funzione real_func riceve il dato come (void *) e ne fa il cast a (INtkdTaskletSpawnable *). Poi richiama il suo metodo func e memorizza il suo valore di ritorno. Infine rimuove il riferimento all'istanza di INtkdTaskletSpawnable e poi termina restituendo il valore di ritorno di func .

Inizializzazione

I moduli del demone ntkd che fanno uso delle tasklet vengono "inizializzati" con alcune chiamate, fra cui il metodo init_tasklet_system . In esso il modulo principale fornisce le implementazioni delle interfacce usate per le comunicazioni allo schedulatore.

Praticamente deve fornire una istanza di INtkdTasklet.

Netsukuku/ita/docs/Librerie/TaskletSystem (last edited 2017-01-17 13:34:39 by lukisi)