Domanda

Ho svolto alcune ricerche sulle implementazioni STM (memoria transazionale software), in particolare sugli algoritmi che utilizzano blocchi e non dipendono dalla presenza di un garbage collector per mantenere la compatibilità con linguaggi non gestiti come C/C++.Ho letto il capitolo STM in Herlihy e Shavit "L'arte della programmazione multiprocessore", oltre a leggere un paio di articoli di Shavit che descrivono il suo "Blocco transazionale" E "Blocco transazionale II" Implementazioni STM.Il loro approccio di base consiste nell'utilizzare una tabella hash che memorizza i valori di un orologio di versione globale e un blocco per determinare se una posizione di memoria è stata toccata dalla scrittura di un altro thread.A quanto ho capito l'algoritmo, quando viene eseguita una transazione di scrittura, il clock della versione viene letto e archiviato nella memoria locale del thread e vengono creati anche un set di lettura e un set di scrittura nella memoria locale del thread.Quindi vengono eseguiti i seguenti passaggi:

  1. I valori di tutti gli indirizzi letti vengono memorizzati nel read-set.Ciò significa che la transazione verifica che tutte le posizioni lette non siano bloccate e che siano uguali o inferiori al valore dell'orologio della versione archiviato localmente.
  2. I valori di tutti gli indirizzi scritti vengono memorizzati nel set di scrittura, insieme ai valori che devono essere scritti in quelle posizioni.
  3. Una volta completata l'intera transazione di scrittura (e ciò può includere la lettura e la scrittura in un numero di posizioni), la transazione tenta di bloccare ciascun indirizzo su cui deve essere scritto utilizzando il blocco nella tabella hash che viene sottoposta a hashing rispetto all'indirizzo ' valore.
  4. Quando tutti gli indirizzi impostati in scrittura sono bloccati, l'orologio della versione globale viene incrementato atomicamente e il nuovo valore incrementato viene archiviato localmente.
  5. La transazione di scrittura controlla nuovamente per assicurarsi che i valori nel set di lettura non siano stati aggiornati con un nuovo numero di versione o non siano bloccati da un altro thread.
  6. La transazione di scrittura aggiorna il timbro versione per quella posizione di memoria con il nuovo valore memorizzato dal passaggio n. 4 e salva in memoria i valori nel set di scrittura
  7. I blocchi sulle posizioni di memoria vengono rilasciati

Se uno qualsiasi dei passaggi di controllo precedenti fallisce (ovvero i passaggi n. 1, n. 3 e n. 5), la transazione di scrittura viene interrotta.

Il processo per una transazione di lettura è molto più semplice.Secondo i documenti di Shavit, noi semplicemente

  1. Legge e archivia localmente il valore globale dell'orologio della versione
  2. Controlla che le posizioni di memoria non abbiano un valore di orologio maggiore del valore di orologio della versione globale attualmente memorizzato e assicurati anche che le posizioni di memoria non siano attualmente bloccate
  3. Eseguire le operazioni di lettura
  4. Ripetere il passaggio n. 2 per la convalida

Se il passaggio n. 2 o n. 4 fallisce, la transazione di lettura viene interrotta.

La domanda che non riesco a risolvere nella mia mente, però, è cosa succede quando si tenta di leggere una posizione di memoria all'interno di un oggetto che si trova nell'heap e un altro thread chiama delete su un puntatore a quell'oggetto?Negli articoli di Shavit, entrano nei dettagli per spiegare come non possano esserci scritture in una posizione di memoria che è stata riciclata o liberata, ma sembra che all'interno di una transazione di lettura, non ci sia nulla che impedisca un possibile scenario temporale che ti consentirebbe per leggere da una posizione di memoria all'interno di un oggetto che è stato liberato da un altro thread.Ad esempio, considera il seguente codice:

Thread A esegue quanto segue all'interno di una transazione di lettura atomica: linked_list_node* next_node = node->next;

Thread B esegue quanto segue: delete node;

Da next_node è una variabile thread-locale, non è un oggetto transazionale.L'operazione di dereferenziazione necessaria per assegnargli il valore di node->next sebbene in realtà richieda due letture separate.Tra queste letture, delete potrebbe essere chiamato in causa node, in modo che la lettura dal membro next sta effettivamente leggendo da un segmento di memoria che è già stato liberato.Poiché le letture sono ottimistiche, la liberazione della memoria è indicata da node In Thread B non verrà rilevato in Thread A.Ciò non causerà un possibile arresto anomalo o un errore di segmentazione?In tal caso, come si potrebbe evitarlo senza bloccare anche le posizioni di memoria per una lettura (qualcosa che sia il libro di testo che i documenti indicano non è necessario)?

Nessuna soluzione corretta

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top