[Kos-dev] "Race condition" 1

d2 kos-dev@enix.org
28 May 2003 15:52:59 +0200


>>>>> "Thomas" == Thomas Petazzoni <thomas.petazzoni@enix.org> writes:
    Thomas> Et pourquoi ma première solution qui consistait à
    Thomas> supprimer le thread dans cpl0_switch_no_return_internal
    Thomas> juste après le changement de pile n'est pas valide ?

Ok, je vois mieux ou tu en es. Tu t'es dit : "finalement il suffit de
supprimer le thread_to_be_destroyed juste apres avoir change de pile
dans cpl0_switch_no_return_internal". Car a priori ce code ne peut
etre appele qu'avec IE a 0 (puisque gros ASSERT). Donc a priori entre
le moment ou on positionne thread_to_be_destroyed a 'myself' dans
cpl0_switch_no_return et le moment ou on fait movl %eax,%esp puis call
delete_pending_thread dans cpl0_switch_no_return_internal il y aurait
forcement preemption (puisqu'on arrive forcement a
delete_pending_thread par ce chemin). Bon, effectivement, je vois.

En gros on a un code qui fait la chose suivante (je trace a partir de
cpl0_switch_no_return) :
  - Verif interruptions disabled
  - Verif thread_to_be_destroyed == NULL
  - thread_to_be_destroyed <- myself;
  - call cpl0_switch_no_return_internal(next_cpu_state)
     - movl 0x4(%esp),%eax // recup next_cpu_state
     - movl %eax,%esp
     - call delete_pending_thread
       - thread_to_destroy <- thread_to_be_destroyed
       - thread_to_be_destroyed <- NULL
       - suppr thread_to_destroy
       - ret
     - pop registres
     - iret

Hypothese : en dehors de ce chemin, thread_to_be_destroyed == NULL.

3 possibilites "classiques" :
  - Il existe un autre chemin que celui-ci qui mene a modifier
    thread_to_be_destroyed
  - Il y'a ecrasement de thread_to_be_destroyed par un autre thread
    (probleme memoire) => afficher l'adresse thread_to_be_destroyed et
    verifier que ca ressemble a un thread
  - Il y'a preemption par un autre thread qui se termine sur ce
    chemin.

je crois pas trop a la derniere explication. Pour la 1ere, faudrait
faire un gros grep et voir ou cette variable peut etre modifiee. Quant
a la 2eme, elle est plausible, mais faudrait des preuves. La seule
possibilite de preemption, ca serait au niveau de suppr
thread_to_destroy (y'a du gros code derriere qui fait peut-etre des
choses bizarres avec IE), mais j'ai des gros doutes vu que
thread_to_be_destroyed est positionne a false avant.

Autres explications moins "classiques" : probleme d'optimisation gcc
sur le chemin (?) => volatile dans le .h sur
thread_to_be_destroyed. Ou alors probleme de cache (j'y crois a
0.00001% ! surtout dans bochs). Ou alors problemes plus delicats,
genre on est a cheval sur 2 contextes (registres de l'ancien thread a
supprimer, pile du nouveau thread a elire, current_thread == nouveau
thread), qui font que gcc fait n'importe quoi (mais la je maitrise
pas). 1 solution si on s'approche de cette explication : cf fin de
mail.

Tu peux rajouter un test IE == 0 (IE du eflags du thread en cours, ie
de celui qui se termine ; pas le eflags du thread destination) dans le
delete_pending_thread() ?  Et meme avant, carrement juste avant et
juste apres le movl %eax,%esp ?

Sinon, si tu fais comme ca (ie call delete_pending_thread dans
cpl0_switch_no_return_internal apres chgt de pile), tu peux enlever
tout appel de delete_pending_thread a la fois dans
cpl0_switch_with_return() et dans execute_thread(), puisqu'ils ne
servent plus a rien vu que c'est cpl0_switch_no_return_internal qui se
charge de tout depuis la pile du thread destination. Tu peux a ce
moment-la aussi enlever le IE a 0 lors de la creation de nouveaux
threads.

Si ca marche pas : eviter d'etre bancal ancien/nouveau thread (cf
supra) : essaye de rajouter les call delete_pending_thread depuis le
reschedule_after_interrupt, et dans le execute_thread en mettant le IE
du nouveau thread a 0 pour le coup.

A lundi !

--
d2