[C] Inter-thread-communicatie met mutexen - hoe?

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • wacco
  • Registratie: Augustus 2002
  • Laatst online: 21-03-2023

wacco

cli, hlt.

Topicstarter
Ik heb een mooie collectie mega-obscure racedonditions weten te verzamelen in m'n kleine appje wat ik aan het schrijven ben. Schijnbaar is mij iets verkeerd aangeleerd. :/

Stukje code voor beeldvorming. Ik heb in thread nummero 1:
C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    /* in init ergens... */
    pthread_mutex_init(&curCSP->solved, NULL);
    pthread_mutex_lock(&curCSP->solved);
    pthread_mutex_init(&curCSP->writesolved, NULL);
    curCSP->solution = NULL;
    /* ...en dan later... */

      /* Wait for solution */
      pthread_mutex_lock(&curCSP->solved);
#ifdef DEBUG
      if(curCSP->solution)
        debugsudoku(curCSP->solution);
      else
        fprintf(stderr, "No solution\n");
#endif


en in thread nummero 2/3/4/etc:
C:
1
2
3
4
5
6
7
8
9
            pthread_mutex_lock(&newsudoku->csp->writesolved);
            /* Sanity check */
            if(!newsudoku->csp->solution)
              newsudoku->csp->solution = newsudoku;
            else
              free(newsudoku);
            /* Let output writer know */
            pthread_mutex_unlock(&newsudoku->csp->solved);
            pthread_mutex_unlock(&newsudoku->csp->writesolved);


csp is hier een gedeelde struct tussen meerdere sudokus. Meerdere sudokus worden tegelijk uitgeprobeerd en zodra een oplossing wordt gevonden probeert deze thread hem weg te schrijven. Maar je raad het al, een van de problemen welke zich de kop op doet is dus dat er "No solution" verschijnt. 8)7

Wat blijkt nu, de man page van pthread_mutex_unlock zegt:
pthread_mutex_unlock() will fail if:
[EINVAL] The value specified by mutex is invalid.
[EPERM] The current thread does not hold a lock on mutex.
Niemand had mij ooit verteld dat je alleen mag unlocken in dezelfde thread. En dit geeft royaal onvoorspelbaar gedrag, want het lijkt erop dat ongerelateerde locks worden unlocked in plaats van degene die ik probeerde te unlocken. :(

Dus mijn vraag is nu, hoe herschrijf ik dit? Is er een mutex waar ik dit wel mee mag doen en/of ander magic thingy?

Ik heb ergens anders ook nog een mutex waar meerdere threads op locken, en als er data beschikbaar is wordt deze unlocked, zodat er een thread wordt 'losgelaten'. Zo'n queue mechanisme gaat dus ook problemen leveren (aantal unlocks is vrijwel gegarandeerd meer per thread dan het aantal locks hier).

Spolap: Interactive webcomic


Acties:
  • 0 Henk 'm!

  • bobo1on1
  • Registratie: Juli 2001
  • Laatst online: 18-05 17:57
Gebruik een pthread condition signal.

Impedance, a measure of opposition to time-varying electric current in an electric circuit.
Not to be confused with impotence.


Acties:
  • 0 Henk 'm!

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 21:01
Inderdaad; mutexes zijn bedoeld om te locken en unlocken in een enkele thread, niet om mee te signaleren tussen threads. Daarvoor moet je een condition of een semaphore gebruiken.

Acties:
  • 0 Henk 'm!

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

Lees er eerst over. Veel. Als je niet precies weet wat je doet met parallel programmeren, dan gaat het vrijwel zeker fout.

Acties:
  • 0 Henk 'm!

  • curry684
  • Registratie: Juni 2000
  • Laatst online: 06-09 00:37

curry684

left part of the evil twins

wacco schreef op vrijdag 15 mei 2009 @ 21:03:
Niemand had mij ooit verteld dat je alleen mag unlocken in dezelfde thread. En dit geeft royaal onvoorspelbaar gedrag, want het lijkt erop dat ongerelateerde locks worden unlocked in plaats van degene die ik probeerde te unlocken. :(
Als je dat al onvoorspelbaar vindt ben ik wel benieuwd hoe een applicatie zou functioneren waar mutexes worden gereleased door andere threads dan degene die ze hebben gelockt.... :X

Fyi staat de afko 'mutex' overigens voor 'Mutual Exclusion'. Het is een mechanisme om te voorkomen dat threads tegelijk aan unsafe data komen zonder zelf iets te communiceren. Je beschermt er hoogstens 'communicatiedata' mee.

Professionele website nodig?


Acties:
  • 0 Henk 'm!

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 21:01
't Is ook een impliciete memory barrier voor zover ik weet. Dat neemt niet weg dat je ze alleen moet gebruiken volgens de specificatie, maar dat geldt natuurlijk voor alle functies die je callt in C.

Acties:
  • 0 Henk 'm!

  • wacco
  • Registratie: Augustus 2002
  • Laatst online: 21-03-2023

wacco

cli, hlt.

Topicstarter
Technisch gezien is een mutex een binaire semaphore, dus waarom je hem niet als signal kan gebruiken als maar 1 thread tegelijk iets mag benaderen is beyond me. Maar dat zal implementatie-technisch wel z'n redenen hebben. Toen ik m'n eigen user-level locks had gebrouwen kon het in ieder geval wel. :P
curry684 schreef op maandag 18 mei 2009 @ 02:58:
Als je dat al onvoorspelbaar vindt ben ik wel benieuwd hoe een applicatie zou functioneren waar mutexes worden gereleased door andere threads dan degene die ze hebben gelockt.... :X
Als binaire semaphore dus. :P

Mijn probleem een beetje is dat ik niet semaphoren wilde gebruiken omdat ik op een mac zit te klooien, en die kent geen sem_init. Nuja, hij 'kent' het wel in de zin dat de functiedeclaratie er is in de libraries, maar die doet verder niets. Je bent dus verplicht om een named semaphore te gebruiken:
C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    sprintf(sem_buffer, "/CSPsudoku%d", sem_counter++);
    curstack.queueaccess = sem_open(sem_buffer, O_CREAT, S_IRUSR | S_IWUSR, 0);
    sprintf(sem_buffer, "/CSPsudoku%d", sem_counter++);
    curstack.writeaccess = sem_open(sem_buffer, O_CREAT, S_IRUSR | S_IWUSR, 1);
    // .. queueaccess zou nu dus al gelocked moeten zijn, maar 
    // volgende wordt keihard genegeerd 
    // (andere thread, hier is curstack een pointer)
    /* Wait until we're informed to go */
    sem_wait(curstack->queueaccess);
    // .. en writeaccess op z'n beurt is locked.
    /* Get exclusive write */
    sem_wait(curstack->writeaccess);
    // ook de andere thread luistert compleet niet naar dit;
    /* Wait for solution */
    sem_wait(curCSP->solved);
    // welke geinit was als volgt;
    sprintf(sem_buffer, "/CSPsudoku%d", sem_counter++);
    curCSP->solved = sem_open(sem_buffer, O_CREAT, S_IRUSR | S_IWUSR, 0);

Nu denk je, ik draai de locks (queue/write) om (of de initialisatie 0 <-> 1) maar dat werkt dus ook niet. Ergens gaat iets duidelijk fout, maar wat? :(

Misschien notable dat ik sommige semaphoren aanmaak voor (de queue/writeaccess) en sommige nadat (alle solved, etc locks) de threads worden gespawned. queueaccess is er dus al voordat de threads beginnen. Zie geen verschil in gedrag tussen de locks (een onvoorspelbaar gedrag dus), ook niet als ik de code omgooi en ze eerst allemaal aanmaak. En sem_counter is aan het eind iets van 20000+, omdat voor elke sudoku dus 2 locks worden aangemaakt. Slechts 1 sudoku gaat ook fout dus daar ligt het ook niet aan.
Zoijar schreef op vrijdag 15 mei 2009 @ 22:31:
Lees er eerst over. Veel. Als je niet precies weet wat je doet met parallel programmeren, dan gaat het vrijwel zeker fout.
Nee dat helpt d:)b De theorie zit er hier wel ingebakken hoor, maar ik ga niet verder happen.
bobo1on1 schreef op vrijdag 15 mei 2009 @ 21:16:
Gebruik een pthread condition signal.
Daar heb ik naar zitten kijken maar dat lijkt me niet wat ik nodig heb. Ik zie niet echt hoe je die solved lock uit m'n startpost bijvoorbeeld elegant kan omschrijven met zoiets? In principe zou bovenstaande semaphore code al moeten voldoen.

Spolap: Interactive webcomic


Acties:
  • 0 Henk 'm!

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 21:01
Je kunt in principe toch een pthread condition en een pthread mutex gebruiken om zelf een echte semafoor te maken? Lijkt me niet heel veel werk. Dan kun je vervolgens een binaire semafoor gebruiken als je dat leuk vind.

Zoiets b.v.:
C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <pthread.h>

typedef struct semaphore_t {
    pthread_mutex_t mutex;
    pthread_cond_t  cond;
    int value;
} semaphore_t;

void semaphore_init(semaphore_t *sem, int value)
{
    pthread_mutex_init(&sem->mutex, NULL);
    pthread_cond_init(&sem->cond, NULL);
    sem->value = value;
}

void semaphore_signal(semaphore_t *sem)
{
    pthread_mutex_lock(&sem->mutex);
    if (sem->value++ == 0)
        pthread_cond_signal(&sem->cond);
    pthread_mutex_unlock(&sem->mutex);
}

void semaphore_wait(semaphore_t *sem)
{
    pthread_mutex_lock(&sem->mutex);
    while (sem->value == 0)
        pthread_cond_wait(&sem->cond, &sem->mutex);
    sem->value--;
    pthread_mutex_unlock(&sem->mutex);
}

void semaphore_destroy(semaphore_t *sem)
{
    pthread_mutex_destroy(&sem->mutex);
    pthread_cond_destroy(&sem->cond);
}


(Niet getest overigens.)

[ Voor 5% gewijzigd door Soultaker op 18-05-2009 18:34 ]


Acties:
  • 0 Henk 'm!

  • H!GHGuY
  • Registratie: December 2002
  • Niet online

H!GHGuY

Try and take over the world...

wacco schreef op maandag 18 mei 2009 @ 17:20:
Technisch gezien is een mutex een binaire semaphore, dus waarom je hem niet als signal kan gebruiken als maar 1 thread tegelijk iets mag benaderen is beyond me.

Nee dat helpt d:)b De theorie zit er hier wel ingebakken hoor, maar ik ga niet verder happen.
Niet dus. Een mutex kun je beter definieren als een binaire semafoor met een owner. Wanneer jij de mutex neemt ben jij de owner, wat je bij een binaire semafoor niet kan zeggen.

ASSUME makes an ASS out of U and ME


Acties:
  • 0 Henk 'm!

  • wacco
  • Registratie: Augustus 2002
  • Laatst online: 21-03-2023

wacco

cli, hlt.

Topicstarter
H!GHGuY schreef op dinsdag 19 mei 2009 @ 12:56:
[...]

Niet dus. Een mutex kun je beter definieren als een binaire semafoor met een owner. Wanneer jij de mutex neemt ben jij de owner, wat je bij een binaire semafoor niet kan zeggen.
Dat was me nu duidelijk ja. :P Blijf het vreemd vinden dat ik die semaphore code daarentegen ook niet aan de praat kan krijgen.
Soultaker schreef op maandag 18 mei 2009 @ 18:26:
Je kunt in principe toch een pthread condition en een pthread mutex gebruiken om zelf een echte semafoor te maken? Lijkt me niet heel veel werk. Dan kun je vervolgens een binaire semafoor gebruiken als je dat leuk vind.

Zoiets b.v.: - snip -

(Niet getest overigens.)
Werkt prima, thanks! Zo had ik me dus mutex code ingebeeld, maar dan gedaan met semaphoren. Exact het tegenovergestelde eigenlijk. Dit stukje code gaat zeker in de toolbox voor als ik weer mac code moet pennen. :9

Spolap: Interactive webcomic

Pagina: 1