Kilpailuehto , myös kilpailu [ 1] , on suunnitteluvirhe monisäikeisessä järjestelmässä tai sovelluksessa, jossa järjestelmän tai sovelluksen toiminta riippuu koodin osien suoritusjärjestyksestä. Virhe sai nimensä samanlaisesta suunnitteluvirheestä elektroniikkapiireissä (katso signaalikilpa ).
Termi kilpailutilanne viittaa tekniseen ammattislangiin ja on seurausta englanninkielisen vastineen huolimattomasta kirjaimellisesta käännöksestä. Tiukemmassa akateemisessa ympäristössä on tapana käyttää termiä concurrency uncertainty .
Kilpailutilanne on "kelluva" virhe ( heisenbug ), joka ilmestyy satunnaisesti ja " katoaa" kun yrität paikallistaa sen.
Jaetun muistin hallitsemattoman käytön vuoksi kilpailutilanne voi johtaa täysin erilaisiin virheisiin, joita voi tapahtua arvaamattomina aikoina, ja yritys toistaa virhe virheenkorjaustarkoituksiin samanlaisilla käyttöolosuhteilla saattaa epäonnistua.
Tärkeimmät seuraukset voivat olla:
Therac -25 sädehoitolaite oli ensimmäinen lääkinnällinen laite Yhdysvalloissa, joka turvasi yksinomaan ohjelmiston . Tämä laite toimi kolmessa tilassa:
Nämä kolme tilaa asetettiin pyörivällä levyllä, jossa oli reikä, jossa oli poikkeutusmagneetit elektronista hoitoa varten, ja kohde, jossa oli diffuusori röntgensäteitä varten. Ohjausohjelman ja näppäimistömoottorin välisen kilpailutilanteen vuoksi joskus tapahtui, että röntgenhoitotilassa levy oli "Elektroniterapia"-asennossa ja potilasta säteilytettiin suoraan 25 MeV elektronisuihkulla, joka johti ylivalottumiseen. Samanaikaisesti anturit näyttivät "Zero dose", joten käyttäjä saattoi toistaa toimenpiteen, mikä pahentaa tilannetta. Seurauksena ainakin kaksi potilasta kuoli.
Osa koodista on otettu Therac-6:sta ja Therac-20:sta. Samaan aikaan Therac-6:ssa ei ollut röntgenhoitoa, ja Therac-20:ssä oli laitteistoturvatoimenpiteitä, jotka estivät säteilyä syttymästä, kun levy oli väärässä asennossa.
Harkitse koodiesimerkkiä ( Javalla ).
haihtuva int x ; // Säie 1: while ( ! stop ) { x ++ ; … } // Säie 2: while ( ! stop ) { if ( x % 2 == 0 ) System . ulos . println ( "x=" + x ); … }Olkoon x=0. Oletetaan, että ohjelma suoritetaan seuraavassa järjestyksessä:
Helpoin tapa ratkaista tämä on kopioida muuttuja x paikalliseen muuttujaan. Tässä on korjattu koodi:
// Säie 2: while ( ! stop ) { int välimuistissa_x = x ; if ( välimuistissa_x % 2 == 0 ) Järjestelmä . ulos . println ( "x=" + välimuistissa_x ); … }Tämä menetelmä toimii luonnollisesti vain, kun muuttujia on vain yksi ja kopiointi tapahtuu yhdessä konekäskyssä.
Monimutkaisempi ja "kallimpi", mutta myös yleisempi ratkaisumenetelmä on säietynkronointi , nimittäin:
int x ; // Säie 1: while ( ! stop ) { synkronoitu ( someObject ) { x ++ ; } … } // Säie 2: while ( ! stop ) { synchronized ( someObject ) { if ( x % 2 == 0 ) System . ulos . println ( "x=" + x ); } … }Tässä tapahtuu ennen kuin semantiikka ei vaadi avainsanaa volatile.
Oletetaan, että muuttujia on kaksi (ja avainsanalla volatileei ole vaikutusta), ja toisella säikeellä System.out.printlnon sen sijaan monimutkaisempi käsittely. Tässä tapauksessa molemmat menetelmät ovat epätyydyttäviä: ensimmäinen, koska yksi muuttuja voi muuttua, kun toista kopioidaan; toinen johtuu siitä, että liian paljon koodia synkronoidaan.
Näitä menetelmiä voidaan yhdistää kopioimalla "vaarallisia" muuttujia synkronoituun lohkoon. Toisaalta tämä poistaa yhden konekäskyn rajoituksen, toisaalta sen avulla pääset eroon liian suurista synkronointilohkoista.
haihtuva int x1 , x2 ; // Säie 1: while ( ! stop ) { synkronoitu ( someObject ) { x1 ++ ; x2 ++ ; } … } // Säie 2: while ( ! stop ) { int välimuistissa_x1 , välimuistissa_x2 ; synkronoitu ( jokuObjekti ) { välimuistissa_x1 = x1 ; välimuistissa_x2 = x2 ; } if (( välimuistiin_x1 + välimuistiin_x2 ) % 100 == 0 ) DoSomethingComplicated ( välimuisti_x1 , välimuisti_x2 ); … }Ei ole ilmeisiä tapoja havaita ja korjata kilpailuolosuhteet. Paras tapa päästä eroon kilpailuista on suunnitella oikein moniajojärjestelmä.
On olemassa virheluokka (ja niitä hyödyntävät hyökkäystyypit), jotka sallivat etuoikeutettujen ohjelmien vaikuttaa muiden ohjelmien toimintaan muuttamalla julkisia resursseja (yleensä väliaikaisia tiedostoja ; tiedosto on kaikkien tai osan kirjoitettavissa). järjestelmän käyttäjiä ohjelmoijan virheen vuoksi.
Hyökkäävä ohjelma voi tuhota tiedoston sisällön, jolloin uhriohjelma kaatuu, tai tietoja muuttamalla pakottaa ohjelman suorittamaan jonkin toiminnon sen oikeuksien tasolla.
Tästä syystä ohjelmistot, joilla on vakavia tietoturvavaatimuksia, kuten verkkoselain , käyttävät salauslaatuisia satunnaislukuja väliaikaisten tiedostojen nimeämiseen.