Sensor: Noter kommentarer til dette arket, hvis studenter kommer opp med ting som hadde fortjent å stå her. Jeg kommer vel til å gjøre denne veiledningen tilgjengelig for dem til neste år... 1a) Dette var samme spørsmålet som 1a) på eksamen i fjor, vel. Disable interrupt og test&set/swap assembly instruksjoner bør være med. Busy wait/spin locks er også et greit svar. 1b) RaceCondision: En bug som kommer til overflaten ved uheldig timing/scheduling. Det klassiske eksemplet er vel med suspend og resume: t1(){ if(not ready) suspend(); t2(){ ready = true; resume(t1);... hvor t1 blir avbrutt etter å ha gjort testen, men før suspend-kallet. 1c) Guard: En test som avgjør om en kode er kjørbar eller ikke. I sanntidskontekst kan «alternativet» til å kjøre koden gjerne være at tråden blir suspendert (Heller enn å kjøre en «else»-del). Men den store forskjellen fra en if-setning er tidspunktet guard'en blir evaluert på. En guard blir gjerne evaluert når variablene som inngår i testen kan ha forandret seg heller enn som en del av den «lineære» programflyten. 1d) POSIX: Condision Variables Java: Wait/Notify/NotifyAll ADA: (VI kan ikke blokke innenfra en region, men har guarder for å holde oss utenfor til alt er faktisk klart, og vi har requeue i tilfelle vi alikevel skulle trenge å blokke.) 1e) Ada feiler på det punktet med parametre - Vi kan ikke teste på parametrene til et kall i guardene, (noe som fører til «double interactions»...) 1f) Når du kaller notify vet du ikke (i prinsippet) hvilken tråd som blir vekket opp, noe som, hvis du har tråder som venter på forskjellige ting innenfor objektet, kan føre til at du kan få vekket «feil» tråd opp... En del refleksjoner omkring vedlikehold, utvidelser og nedarving kan godt komme her i tillegg. 1g) Dette står ikke i boken i vranglåskapittelet, men står som alternativ i transaksjonslefsen... Vi kan unngå «mutual exclusion» ved å ikke blokke resurser, men i stedet å detektere det at vi vil ha en opptatt resurs, og så håndtere det (med feilhåndteringsteknikker f.eks.). «Optimistic concurrency control» er begrepet i lefsen som omhandler dette. 1h) Det var en del spørsmål på eksamen på dette punktet (~5), hvor noen ikke tok det som en selvfølge at kallene skulle være mutual exclusive... Hvis mange har misforstått dette får vi evt. ta ut oppgaven. Implementasjonen ligner på lese/skrivelåser med unntak av at kallene til measure() også er exclusive hverandre, mens kall til read() ikke er det... Det er ikke nok å bare gjøre config() og measure() til synchronized methods - vi må skaffe oss styring på køene for å kunne prioritere: Hva med:
public: void Sensor::config(){ startconfig()... endconfig() void Sensor::measure(){ startmeasure()... endmeasure() private int m_busy = 0; int m_waitingconfigrequests = 0; Sensor::startConfig(){ // Run if not busy. while(busy){ m_waitingconfigrequests++; wait(); m_waitingconfigrequests--; busy = 1; Sensor::endConfig(){ busy=0; Sensor::startMeasure(){ // run if not busy and no waiting config requests while(busy && m_waitingconfigrequests>0) wait(); busy = 1; Sensor::endMeasure(){ busy = 0; Hmm. denne er vel ikke helt rettferdig - en fare for starvation hvis vi er uheldig?
2a) Dynamisk Redundans: Recovery Blocks Statisk Redundans: N-versjonsprog. 2b) Backward Error Recovery: Transaksjoner, Recovery Blocks Forward Error Recovery er vel vanskeligere å sette et kort navn på siden dette er den «normale, velkjente» måten å håndtere feil på... Vi detekter en feil, og lar så koden forholde seg til eller ta konsekvensen av feilen. Et eksempel blir derved: if((cfp = fopen(«~/.shconfig»,«r»)) == 0){ cfp = fopen(«/etc/shconfig»,«r») 2c) Backward error recovery er det å sikre en konsistent systemtilstand ved feil, ved å «lagre unna» slike konsistente tilstander innimellom. En slik unnalagret konsistent tilstand er et recovery point. 2d) Her venter vi, ærlig talt, noe lignende figuren på s.... i læreboken... Forklaringer er også velkomne. 2e) Her kan det være stor variasjon omkring implementasjonene siden B&W bruker delt variabelsynkronisering heller enn meldingssending og ting blir dermed seende veldig forskjellig ut. Start Boundary, etablering av recovery point hvis vi planlegger backward error recovery, etablering av medlemskap - Hvem er med i aksjonen. Implementeres gjerne som en eksplisitt påmelding til ActionManager (en melding sendt). For delt variabel-løsningen kommer det ut som en synkronisering i starten av rolle-utførelsen som sikrer at bare en tråd tar den samme rollen i samme aksjon i gangen. Recoverypoint implementeres gjerne ved at vi lagrer en sekvens av log-recorder (med «før»-info) som tilhører aksjonen, men det er selvfølgelig også mulig å lagre «sjekkpunkter» - tverrrsnitt av den konsistente tilstanden ved aksjonens start - hvis det finnes slike konsistene tilstander. Side Boundary står for det at tråder som ikke er en del av prosessen ikke får tilgang til «intern tilstand» i aksjonen. Låsing, med «growing og shrinking phase» er standardimplementasjonen her. End boundary er markeringen av at aksjonen er ferdig, vi er i en konsistent tilstand igjen. Mer konkret må vi fastslå at alle deltagere lyktes i det vi prøvde å oppnå, og markere det tidspunktet imellom låsingens growing og shrinking-fase. Det skjer gjerne ved en form for avstemming imellom alle deltagere - tofase commit. 2f) To ting som jeg kan komme på: Akseptansetester: At vi i stedet for å teste på feiltilstander, tester på krav til normalutførelsen - til den riktige tilstanden - rimelighet, konsistens, timing osv. En god akseptansetest kan detektere uforutsette feil. Statisk redundans: Ved å regne den samme tingen ut på flere (forskjellige?) måter kan vi avdekke feil på en måte som også vil dekke uforutsette feil. 2g) En transaksjon har en «standard feilhåndtering» innebygget - abort - å annulere alle effektene av akksjonen. (En atomic action har ikke det - her tar vi fritt stilling til feilhåndteringen.) 2h) Tofase commit: Beskrevet under end-boundary i 2e) Det virker ved at (og her må vi vel regne med en del sekvensdiagrammer...) Transaksjonsmanager sender ut en «preparetocommit» til alle deltagere, de svarer med OK eller FAIL. Når alle har svart tas avgjørelsen og deltagerene får beskjed om enten commit eller abort.
2i) Transajons Manager. En gjenbrukbar modul eller en server som yter ett sett tjenester - det problemet som løses av denne direkte er vel designmessig i utgangspunktet, men vi kan vel også gå inn på *hva* modulen gjør: Holde oversikt over aktive transaksjoner i systemet, (forvalte transaksjons ID'er), holde rede på medlemskap i transkasjonene, avholde avstemmingene. 2j) Vanskelig oppgave på den måten at det ikke er åpenbart hva som er den gode løsningen på dette problemet. En hernedød opplisting av hvordan vi unngår vranglåser generelt synes jeg ikke er veldig bra - svaret bør relateres til den konteksten som er gitt. Vi har et begrenset sett med oppgaver her - dvs. det går an å faktisk sette opp avhengigheter og analysere seg frem til hvor problemene er. Men det blir mindre og mindre hensiktsmessig jo større systemet blir. Å kreve at resurser blir allokert i en globalt gitt rekkefølge kan være en god måte her? Ineffektivt, men. En global lock-manager med vranglåsunngåelse som feature er mulig i denne settingen. Men det representerer jo en mulig ytelsesflaskehals. Å detektere at vranglåser (ved f.eks. timeout eller i en evt. lockmanager) har skjedd og «abortere» aksjoner er en mulighet. Men det er ikke alltid mulig/attraktivt å abortere.... 3a) I OCCAM can ikke to tråder skrive til samme variabel - det blir kompilatorfeil. 3b) Jeg antar her at trådene kjører igjen og igjen, men det går vel ikke tydelig frem fra oppgaven. Vi må ta høyde for at noen ikke gjør det slik... T1 = (t1wa -> t1wb -> t1sa -> t1sb -> T1). T2 = (t2wb -> t2wa -> t2sb -> t2sa -> T2). SEMA = (t1wa -> t1sa -> SEMA t2wa -> t2sa -> SEMA). SEMB = (t1wb -> t1sb -> SEMB t2wb -> t2sb -> SEMB). 3c) Se eget figur-ark 3d) Vranglås: En tilstand i det totale, sammensatte diagrammet med ingen piler ut. 3e) Java kanal: Når vi *vet* at objektet skal brukes bare av to tråder lar dette oss forenkle en del - vi trenger ikke hindre flere samtidige sendere f.eks. Hva med noe slikt: class Channel { void send(int i){ m_value = i; m_sent = true; notifyall(); while(m_received == false) wait(); m_received = false;
int receive(){ while(sent = false) wait(); int tmp = m_value; m_received = true; notifyall(); m_sent = false; return tmp; private: bool m_sent = 0; bool m_received = 0; int m_value; 3f) Det jeg leter etter her var koblingen til feilhåndteringsteorien - jeg ville sagt at fasit'en her er «Atomic Action» eller «Transaksjon». Men gi gjerne kredit til andre gode forslag. 4) Designoppgaven Et viktig poeng her er at kommunikasjonen i utgangspunktet ikke er pålitelig når vi snakker om prosesspar... Når enhver melding inneholdt hele tilstanden var ikke dette noe problem - en hvilken som helst melding overskrev alle tidligere. Nå må vi *sikre* oss at all informasjon - alle oppdateringer når frem til slaven. Det å «forutsette pålitelig kommunikasjon» er litt for enkelt, siden prosesspar er en grunnleggende mekanisme på linje med pålitelig kommunikasjon - det kan tenkes at de som lager pålitelig kommunikasjon kan komme til å forutsette prosesspar... Jeg ville diskutert grundig følgende to punkter: Hvordan konsistensen sikres under normal drift. Et system med ack'ing av mottatte meldinger kombinert med enten sekvensnumre på meldingene, eller et system med agregering av alle meldinger siden forrige ack? Et annet punkt som det må tas stilling til er hva som skjer ved oppstart av en (slave) prosess - den må få oversendt *hele* tilstanden - en egen del av protokollen for dette. Gitt at dette ikke kan sendes som en melding får vi et konsistensproblem hvis vanlige oppdateringer skjer paralellt. Vi får altså spesialbehandling av nystartede slaver, noe som betyr at masteren må ta stilling til om den har en aktiv slave eller ikke... Vi kan ikke vente komplette løsninger på alle aspekter av dette som svar. Hvis «de er på rett spor» i deteksjon av problemene og foreslår rimelige løsninger er det vel ok - Vi bør forvente mye på presentasjonssiden - svarene skal være oversiktlige.