Trådsäkerhetsfråga: process<->process Shared memory(windows)

Permalänk
Hedersmedlem

Trådsäkerhetsfråga: process<->process Shared memory(windows)

Då var man där igen :P. Håller på att koda ett dataextraheringsprogram till en trevlig flygsimulator som heter BMS (http://www.youtube.com/watch?v=RwZFjKOdWOI&feature=related). BMS exporterar data genom windows shared-memory-API (se http://msdn.microsoft.com/en-us/library/aa366551(v=vs.85).asp...).

Jag har redan skrivit testkod för att läsa detta minne från min egen process, men, har kört fast på en punkt. BMS tillhandahåller inte några synkroniseringsprimitiver typ mutexes eller semaphorer. Jag har inget krav som säger att minnet jag läser konsekvent måste vara från en simulatoriteration, det gör inte om det blir lite korruption var 10.000:e frame :).

exempelkod som fungerar:

#include <iostream> #include <windows.h> #include "Flight Data.h" using namespace std; int main() { const HANDLE hMapFile = OpenFileMapping(FILE_MAP_READ, FALSE, "FalconSharedOsbMemoryArea"); if (hMapFile != NULL) { const OSBData * data = (OSBData *) MapViewOfFile(hMapFile, FILE_MAP_READ, 0, 0, sizeof(OSBData)); if (data != NULL) { while (true) { cout << endl << data->leftMFD[2].line1; Sleep(1000); } UnmapViewOfFile(data); } else { cout << "Could not map view of file"; } CloseHandle(hMapFile); } else { cout << "Could not open file mapping object"; } return 0; }

MEN:

1. Jag vill inte att kompilatorn bestämmer sig för att optimera bort mina läsningar (tänker på while(myVal) när myVal sätts till false av en annan tråd t.ex kan kompilatorn bestämma sig för att det alltid är true eftersom den lokala tråden aldrig ändrar det.)

2. Jag vill inte att datorn bestämmer sig för att efter läsning #1 av minnet , "aha, nu har vi läst det till vår cache en gång, och därför behöver vi aldrig hämta datan från RAMet igen.)

Det kan vara värt att nämna att detta körs helt i windows, jag kompilerar & länkar mitt C/C++program med MingW genom eclipse, och det är inte tänkt att köras på några andra plattformar än x86 (dock kan det vara aktuellt att även kompilera en 64bitars-version).

Jag har försökt läsa på lite om minnesbarriärer, men kan inte komma fram till något konsekvent. Vad jag förstått så kan man tex. lägga in assembly-inlines i sin kod som "__asm__ __volatile__ ("" ::: "memory");" för att skapa en barriär. Om jag har förstått det rätt så ska barriären garantera att saker inte ordnas om, men jag hittar ingenting om att barriären skulle tvinga en läsning från faktiskt ram. (BMS garanterar förresten att datan skrivs till ram och inte cachas på dess sida )

Ok, hoppas någon förstod det här, alla tips välkomna :). Hade det varit min egen kod på "båda sidor" skulle det ju vara lätt (slänga in ett mutex, vips, löst), men nu har jag ju bara en minnesadress som pekar på ett block vars innehåll ändras av någon annan process vid godtyckliga tidpunkter, och det enda jag vill garantera är att när jag kallar på "readDataFromBlocK" eller vad man nu ska kalla den funktionen som jag kommer skriva, så skall det läsas strikt direkt ur den faktiska adressen och inte ur någon *** cache :).

Det jag gör just nu är att lägga till volatile framför min data-variabel: volatile const OSBData * data, men det känns ju fel på något sätt...främst för att allt man läser om volatile verkar som det inte stämmer :).

Vad ska jag stoppa in i min while-loop i koden ovan för att tvinga en läsning från faktiskt ram, någon som vet?

Visa signatur

Every time you create an iterator: God kills a kitten.

Permalänk
Medlem

Ang. 1: Jag har för mig att du sätter anger myVal som volatile, alltså
volatile int myVal;

Pass på #2. Har inte pysslat så mycket med C(++).

Visa signatur

Jag är en optimist; det är aldrig så dåligt så att det inte kan bli sämre.

Permalänk
Hedersmedlem
Skrivet av zyberzero:

Ang. 1: Jag har för mig att du sätter anger myVal som volatile, alltså
volatile int myVal;

Pass på #2. Har inte pysslat så mycket med C(++).

Ok låt oss säga att jag kör på

volatile char data[1024];

Antag att jag har 2 trådar.

Tråd #1 nu fyller i detta minnesblock på korrekt sätt.
Tråd #2 vill i början av varje iteration kopiera allt från "data" till ett trådlokalt minnesblock. - Det är detta jag måste lösa. (återigen lite korruption spelar ingen roll, bara det faktiskt läses från rätt minne varje gång!)

Detta borde då fungera

volatile const char data[1024]; // Uppdateras av tråd #1 char t2[1024]; // tråd #2 kopierar data hit varje iteration void t2Iter() { for (size_t i = 0; i < 1024; i++) { t2[i] = data[i]; } }

- Kommer lösningen ovan göra att programmet måste göra 1024 enskilda läsninga från ramet? Detta är ju grymt ineffektivt iom att det är volatile, eller kommer kompilatorn eller arkitekturen lösa detta på något smart sätt? För man kan ju inte köra memcpy på en volatile ...

EDIT:

Den bästa fullösningen jag kan komma på just nu :

template<class T> static void cpyVolatileblock(const volatile T * in, T * out) { const size_t longs = sizeof(T) / sizeof(long); const size_t bytesRemaining = sizeof(T) - longs * sizeof(long); for (size_t i = 0; i < longs; i++) { ((long*) out)[i] = ((volatile long*) in)[i]; } for (size_t i = sizeof(T) - bytesRemaining; i < sizeof(T); i++) { ((char*) out)[i] = ((volatile char*) in)[i]; } }

Visa signatur

Every time you create an iterator: God kills a kitten.

Permalänk
Skrivet av 'Gi:

[Gurra;11369367']1. Jag vill inte att kompilatorn bestämmer sig för att optimera bort mina läsningar (tänker på while(myVal) när myVal sätts till false av en annan tråd t.ex kan kompilatorn bestämma sig för att det alltid är true eftersom den lokala tråden aldrig ändrar det.)

Som zyberzero säger, ange att pekaren som pekar på det delade dataområdet är volatile. http://www.netrino.com/node/80

Skrivet av 'Gi:

[Gurra;11369367']2. Jag vill inte att datorn bestämmer sig för att efter läsning #1 av minnet , "aha, nu har vi läst det till vår cache en gång, och därför behöver vi aldrig hämta datan från RAMet igen.)

Cacharna i din dator har cache koherens-algoritmer som gör att om en processor skriver till en minnesadress som ligger i en annan processors cache så kommer värdet i cachen att uppdateras automatiskt. http://en.wikipedia.org/wiki/Cache_coherence

Skrivet av 'Gi:

[Gurra;11369434']Kommer lösningen ovan göra att programmet måste göra 1024 enskilda läsninga från ramet? Detta är ju grymt ineffektivt iom att det är volatile, eller kommer kompilatorn eller arkitekturen lösa detta på något smart sätt?

Slå på assembler-output från din kompilator och se vad den faktiskt genererar, så slipper du undra.

Permalänk
Hedersmedlem
Skrivet av VirtualIntent:

Som zyberzero säger, ange att pekaren som pekar på det delade dataområdet är volatile. http://www.netrino.com/node/80

Cacharna i din dator har cache koherens-algoritmer som gör att om en processor skriver till en minnesadress som ligger i en annan processors cache så kommer värdet i cachen att uppdateras automatiskt. http://en.wikipedia.org/wiki/Cache_coherence

Slå på assembler-output från din kompilator och se vad den faktiskt genererar, så slipper du undra.

Tack för tipsen! Guld värt :). Med andra ord, med volatile så kommer allt fungera då som tolkar det, då cache-coherence är automatiskt för min plattform?

Visa signatur

Every time you create an iterator: God kills a kitten.

Permalänk
Skrivet av 'Gi:

[Gurra;11370602']Med andra ord, med volatile så kommer allt fungera då som tolkar det, då cache-coherence är automatiskt för min plattform?

Ja precis