---
# System prepended metadata

title: L2 - GRUPA 1

---

# L2 - GRUPA 1

## Zadanie 1
:::success
Autor: Mikołaj Depta	
:::

Poniższy algorytm ma w zamierzeniu implementować interfejs Lock dla dowolnej liczby n wątków. Czy ten algorytm spełnia warunek 1. wzajemnego wykluczania (ang. mutual exclusion), 2. niezagłodzenia (ang. starvation–freedom), 3. niezakleszczenia (ang. deadlock–freedom)? Zmiennymiwspółdzielonymi przez wątki są `turn` i `busy`.

```java=
class Foo implements Lock {
    private int turn;
    private boolean busy = false;
    public void lock() {
        int me = ThreadID.get(); /*get id of my thread*/
        do {
            do {
                turn = me;
            } while (busy);
        busy = true;
        } while (turn != me);
    }
    
    public void unlock() {
        busy = false;
    }
}
```

## 1. Wzajemne wykluczenie

Założmy, nie wprost, że dwa wątki dostały się do sekcji krytycznej.

Z kodu oznacza to, że zaszły następujące relacje między zdarzeniami:

`Write_B(turn=B) → Read_B(busy==false) → Write_B(busy=true) → Read_B(turn==B)`
`Write_A(turn=A) → Read_A(busy==false) → Write_A(busy=true) → Read_A(turn==A)`

Oraz z fakty, że oba wątki są w sekcji krytycznej, wiemy że:

`Read_B(turn==B) → Write_A(turn=A)`
`Read_A(turn==A) → Write_B(turn=B)`

Wówczas:

`Read_B(turn==B) → Write_A(turn=A)`

`Write_A(turn=A) → Read_A(busy==false) → Write_A(busy=true) → Read_A(turn==A)`

`Read_A(turn==A) → Write_B(turn=B)`

`Write_B(turn=B) → Read_B(busy==false) → Write_B(busy=true) → Read_B(turn==B)`

Mamy cykl co jest sprzeczne z antyzwrotnością relacji `→`.

## 2. niezagłodzenia

Może dojść do zagłodzenia w następującym scenariuszu.
```
A: 
    lock()
        Write_A(turn=A)
        idzie spać
B:
    lock()
        Write_B(turn=B)
        Read_B(busy==false)
        Write_B(busy=true)
        
        // teraz A może zostać obudzony ale i tak będzie kręcił się w pętli
        
        Read_B(turn==B)
        wykonuje sekcję krytyczną 
    unlock()
        Write_B(busy)
    znowu wykonaj lock() do momentu Write_B(busy=true)
```

Nie ma żadnego mechanizmu gwarantującego, że wątek `B` w końcu ustąpi `A`.

## 3. niezakleszczenia

```
A:
    lock()
        Write_A(turn=A) 
        Read_A(busy==false)
        Write_A(busy=true)
        idzie spać
B:
    lock()
        Write_B(turn=B) 
        Read_B(busy==true)
        zapętla się
        idzie spać
A:
    kontunuacja lock()
        Read_A(turn==B)
        wraca do Write_A(turn=A)
        Read_A(busy==true)
        pętli się
Mamy deadlock
```

Deadlock występuje tutaj ponieważ, wątek oznajmia, że sekcja krytyczna jest zajęta (`busy==true`) przy czym nie ma gwarancji, że faktycznie do niej wejdzie a co za tym idzie, potencjalnie z niej nie wyjdzie i nie wywoła `unlock()`.

## Zadanie 2
:::success
Autor: Marcin Sarnecki	
:::
```java=
public void lock() {
    flag[i] = true; 
    victim = i;
    while (flag[j] && victim == i) {};
}

public void unlock() {
  int i = ThreadID.get(); 
  flag[i] = false;
  int j = 1 - i;
  while (flag[j] == true) {}
}
```
**Zakleszczenie**
Rozważmy możliwość zakleszczenia w miejscu dodanych linijek. Założmy, że doszło do zakleszczenia. Bez straty ogólności założmy, że wątek 0 jest w funkcji unlock. Wtedy flag[0] = false, zatem wątek 1 nie zakleszczy się w funkcji lock() w linijce 4. W przypadku, w którym oba wątki znajdą się w lock() nie dojdzie do zakleszczenia ze względu na zmienną victim. W przypadku, w którym oba wątki będą w unlock() również nie dojdzie do zakleszczenia ze względu na linijki 9 i 11.  

**Głodzenie**
Głodzenie występuje. Rozważmy przypadek w którym wątek 0 właśnie skończył sekcję krytyczną, ustawia flag[0]=false, zaczyna czekać w linijce 11 (wątek 1 czeka na wejście do sekcji krytycznej, więc flag[1]=true) i oddaje procesor wątkowi 1. Wątek 1 wchodzi do sekcji krytycznej, wykona ją i przechodzi do sekcji unlock. Przechodzi przez nią bez problemu, ponieważ flag[0]=false. Może więc wejść ponownie do funkcji *lock*, ustawić swoją flagę na true i wejść do sekcji krytycznej. Wątek 1 może teraz cały czas przechodzić przez unlock() i blokować wątek 0.

## Zadanie 3
:::success
Autor: Wiktor Bukowski	
:::

**Właśność r-ograniczonego czekania** oznacza, że wątek, który wykonał sekcję wejściową będzie "wyprzedzony" co najwyżej r razy przez taki wątek, który wykonał tę sekcję po nim.
**Sekcja wejściowa** to część, w której ustawiana jest flaga oraz victim.
**Sekcja oczekiwania** to pętla ```while(flag[j] && victim == i) {};```

Algorytm Petersona:
```java=
public void lock() {
 flag[i] = true; 
 victim = i;
 while (flag[j] && victim == i) {};
}
public void unlock() {
 flag[i] = false; 
}
```

Oznaczmy jako A ten z wątków, który jako pierwszy wykonał sekcję wejściową. Drugi z nich oznaczmy jako B. Załóżmy, że B jako pierwszy wykonał sekcję krytyczną.
$D_A \rightarrow D_B$
$CS_B \rightarrow CS_A$
$D_A \rightarrow W_A \rightarrow CS_A$
$D_B \rightarrow W_B \rightarrow CS_B$
Wtedy z przechodniości $\rightarrow$:
$D_A \rightarrow D_B \rightarrow CS_B \rightarrow CS_A$

Jeśli B jako drugi wykonał sekcję wejściową, to ustawił zmienną `victim` na samego siebie, przez co musiał potem czekać w sekcji oczekiwania do wykonania przez A sekcji krytycznej i zmiany jego flagi. W związku z tym B nie mógł wykonać sekcji krytycznej przed A.

## Zadanie 4
:::success
Autor: Marcin Wróbel	
:::

![](https://i.imgur.com/X52gW04.png)
**a) wzajemne wykluczanie**
Dowód indukcyjny, teza:
W danym wierzchołku co najwyżej jeden wątek zajął lock'a.

n=liczba wątków

Dla **n==2**
Dla liści jest to prawdziwe, ze względu na to, że algorytm Petersona działa.

Dla **n>2**
Rozpatrzmy korzeń v.
W dzieciach wierzchołka v co najwyżej jeden wątek dostał lock'a z założenia indukcyjnego. Więc co najwyżej 2 wątki mają dostęp do wierzchołka v. Stąd z działania algorytmu Petersona w wierzchołku też co najwyżej jeden wątek dostał lock'a wierzchołka v.
**b) zakleszczenie**
Dowód indukcyjny, teza:
W danym wierzchołku jeżeli w jego poddrzewie jest wątek chcący uzyskać dostęp do sekcji krytycznej to zajmie lock'a w danym wierchołku.

Dla **n==2**
Dla liści jest to prawdziwe, ze względu na to, że algorytm Petersona działa.

Dla **n>2**
Rozpatrzmy korzeń v.
W dzieciach wierzchołka v wiemy, że jakiś wątek uzyska lock'a z założenia indukcyjnego. Łącząc to z tym, że wiemy, że algorytm Petersena działa, jakiś wątek dostanie lock'a w wierzchołku v.

Stąd jakiś wątek zajmie lock'a w korzeniu. Wiemy, więc, że ten wątek dostanie dostęp do sekcji krytycznej.

**c) zagłodzenie**
Wiedząc, że algorytm Petersona nie zagładza żadnego wątku, wiemy, że żadne poddrzewo wierzchołka nie zostanie zagłodzone. Stąd indukcyjnie

Dla **n==2** nie nastąpi zagłodzenie, ze względu na to, że algorytm Petersona działa.

Dla **n>2**
Korzeń v ma dwoje dzieci, w których żaden wątek nie zostaje zagłodzony, z założenia indukcyjnego. Z tego, że algorytm Petersona działa żadne poddrzewo nie zostanie zagłodzone, więc żaden wątek z obu poddrzew nie zostanie zagłodzony.


## Zadanie 5
:::success
Autor: Michał Hetnar	
:::
1.Czy istnieje taka liczba r, być może zależna od n, że
algorytm tree-lock spełnia własność r-ograniczonego
czekania (ang. r-Bounded Waiting)? Jako sekcję wejściową
(ang. doorway section) algorytmu przyjmij fragment kodu
przed pętlą while zamka w odpowiednim liściu.


Jeżeli nie założymy że wątkom przydzielany czas jest równomiernie:

możemy mieć sytułację że wątek który przejdzie doorway section w swoim liściu nie dostanie wiecej czasu procesora, nic nie powstrzyma procesów wyżej od przejścia przed nim nieskończoną liczbę razy.

Możemy założyć że wątkom przydzielany czas jest równomiernie:
![](https://i.imgur.com/XuvMKcq.jpg)
w tym przypadku przed 8 wykonały się wszystkie wątki ale 8 i 7 (bo musiała być przed nią ) nie liczymy wiec
R= N-2

2.  Pokaż, być może modyfikując nieco oryginalny algorytm, że
założenie o numerach wątków w poprzednim zadaniu może być
łatwo usunięte.

Każdy z zamków traktuje
jeden z rywalizujących o niego wątków jako wątek o numerze 0 a
drugi jako wątek 1.
```java=
public void lock() {
 flag[i] = true;
victim = i;
while (flag[i] && victim == i) {};
}
public void unlock() {
 flag[i] =
 ```
 ```java=
int flags = 0;

 public void lock() {
 flags += 1;
 victim = ThreadID.get();
 while (flags==2 && victim == ThreadID.get()) {};
}
public void unlock() {
 flags -=1;
 ```

## Zadanie 6
:::success
Autor: Wojciech Pokój	
:::


W dobrze zaprojektowanym programie wielowątkowym
rywalizacja o zamki powinna być niewielka. Najbardziej
praktyczną miarą wydajności algorytmu implementującego zamek
jest więc liczba kroków potrzebna do zajęcia wolnego zamku,
jeśli tylko jeden wątek się o to ubiega. Poniższy kod jest
propozycją uniwersalnego wrappera, który ma przekształcić
dowolny zamek na zamek wykonujący tylko stałą liczbę kroków w
opisanym przypadku. Czy ten algorytm spełnia warunek a)
wzajemnego wykluczania, b) niezagłodzenia, c)
niezakleszczenia? Załóż, że oryginalny zamek spełnia te
warunki.

``` java=
class FastPath implements Lock {
    private Lock lock;
    private int x, y = -1;
    public void lock() {
        int i = ThreadID.get();
        x = i; // I’m here
        while (y != -1) {} // is the lock free?
        y = i; // me again?
        if (x != i) // Am I still here?
            lock.lock(); // slow path
    }

    public void unlock() {
        y = -1;
        lock.unlock();
    }
}
```

**Wzajemne wykluczenie**

Własność nie jest spełniona

Przykład dla dwóch wątków:

Oba wątki dochodzą do linii 8 jednocześnie.
w tym miejscu x ma już jakąś ustaloną wartość (wskazuje na jeden z wątków), więc jeden wątek zajmnie kłódkę (lock.lock), a drugi wyjdzie z zamka, w efekcie czego oba wątki uzyskają dostęp do sekcji krytycznej

**Niezagłodzenie**

Własność nie jest spełniona i jest już widoczna dla dwóch wątków:

Wystarczy że powolny wątek utknie w linijce 7 w czasie gdy drugi wątek będzie bardzo szybko wchodzić i wychodzić do i z sekcji krytycznej

**Niezakleszczenie**

Własność jest spełniona

Załóżmy nie wprost że któryś z wątków zablokował się na pętli w linii 7,
tj. zmienna y ma wartość różną od -1 ale żaden wątek nie wykonuje sekcji krytycznej. Ale żeby tak było któryś z wątków musiał wcześniej wyjść z sekcji krytycznej więc ustawił y na wartość -1. Sprzeczność

## Zadanie 7
:::success
Autor: Jan Dalecki	
:::

![](https://i.imgur.com/p9Q6t7h.png)

$X$ - zbiór wszystkich wątków

**a)**

Załóżmy, że co najmniej 2 wątki otrzymały wartość STOP.

Z kodu:
$$
read_A(last == A) \rightarrow return_A(STOP)
$$
$$
write_A(last = A) \rightarrow read_A(goRight == false) \rightarrow write_A(goRight = true)
$$
$$
write_B(last = B) \rightarrow read_B(goRight == false) \rightarrow write_B(goRight = true)
$$

Założenia:

$$
\forall C \in X\backslash A \ read_A(last == A) \rightarrow write_{C}(last = C)
$$
$$
\forall C \in X\backslash B \ read_B(last == B) \rightarrow write_{C}(last = C)
$$

Z założeń wynika, że procesy nie mogą wykonać zapisau pod `last` dopóki dany proces nie odczyta wartości `last`. Dany proces zmieni wartość `goRight` na `true`.

Zmienna `goRight` nie będzie miała wartości `false` dla drugiego procesu.

**b)**

Załóżmy, że wszystkie wątki otrzymały wartość DOWN.

Z kodu
$$
read_X(last \not= X) \rightarrow return_X(DOWN)
$$

Założenia $\forall A \in X$, $\exists B \in X\backslash A$:
$$
write_A(last = A) \rightarrow write_{B}(last = B) \rightarrow read_A(last \not= A) 
$$

staje się jasne, że to nie może zajść dla procesu który jako ostatni wykona podstawienie pod `last`.

c)

Załóżmy, że wszystkie wątki otrzymały wartość RIGHT.

Z kodu:
$$
read_X(goRight == true) \rightarrow return_X(RIGHT) 
$$

$$
read_A(goRight == false) \rightarrow write_A(goRight = true) \rightarrow return_A(STOP)
$$
lub 
$$
read_A(goRight == false) \rightarrow write_A(goRight = true) \rightarrow return_A(DOWN)
$$

Założenia:
$$
\forall A \in X \ read_A(goRight == true) \rightarrow return_A(RIGHT)
$$
$$
\exists B \in X \ read_B(goRight == false) \rightarrow write_B(goRight = true)
$$
Widzimy, że $B$ nie zwróci RIGHT.

## Zadanie 8
:::success
Autor: Bartosz Głowacki	
:::

![](https://i.imgur.com/3pl8zDx.png)

Wiemy że:
1) w pierwszym wierzchołku na początku znajduje się N wątków
2) w każdym wierzchołku co najwyżej k-1 (gdzie k to liczba wątków w tym wierzchiłku) wątków idzie na prawo to samo dotyczy lewa
3) na każdym poziomie drzewa sumaryczna liczba wątków wynosi maksymalnie N (wynika to z fakty że w każdym kroku wątek schodzi dokładnie  poziom w drzewie niżej)
4) gdy w wierzchołku znajduje się 1 wątek dostanie on STOP

### Policzmy więc ile maksymalnie wątków może znaleźć się w każdym wierzchołku

W wierzchołku $0$ znajduje się $N$ wątków
#### Weźmy teraz wierzchołki $2, 5, 9, ...$ (wierzchołki wysunięte maksymalnie w lewo)
Wątki w tych wierzchołkach mogą znaleźć się tam tylko idąc w lewo z wierzchołka wyżej a z **(2)** wiemy że maksymalna liczba wątków będzie w każdym kroku zmniejszać się o jeden mamy zatem
maksymalnie $N-1$ wątków w wierzchołku $2$
maksymalnie $N-2$ wątków  w wierzchołku $5$
...

#### Weźmy teraz wierzchołki $1, 3, 6, ...$ (wierzchołki wysunięte maksymalnie w prawo)
Dla nich możemy przeprowadzić analogiczne co wyżej rozumowanie

#### Weźmy teraz wierzchołek 4
mogą do niego wejść wątki z wierzchołków $1$ i $2$
załóżmy że w wierzchołku $1$ mamy $K_1$ wątków a w wierzchołku 2 $K_2$ wątków.

wiemy że $K_1, K_2 \in [0, N-1]$
z **(2)** wiemy że do wierzchołka 4 trafi co najwyżej $max(K_1-1, 0) + max(K_2-1,0)$ wątków
z **(3)** wiemy że $K_1+K_2 \le N$
w takim razie w wierzchołku $4$ znajduje się maksymalnie $N-2$ wątków

#### Rozumowanie indukcyjne
widzimy więc że na $0$ poziomie drzewa wierzchołki mają maksymalnie $N$ wątków
na pierwszym $N-1$ wątków na drugim $N-2$ możemy więc wysnuć hipotezę że na wysokości $k$ drzewa w każdym wierzchołku może znaleźć się maksymalnie $N-k$ wierzchołków

żałóżmy więc że na poziomie $k$ mamy $N-k$ maksymalnie wątków ma wierzchołek i pokażmy że na poziomie $k+1$ mamy maksymalnie $N-k-1$ wątków na wierzchołek

zatem weźmy dowolny wierzchołek $c$ i wierzchołki $a$, $b$ z krótych można przejść do wierzchołka $c$.
Wierzchołki $a$ i $b$ mają odpowiednio $K_a$, $K_b$ wątków

mamy zatem:
wiemy że $K_a, K_b \in [0, N-k]$
z **(2)** wiemy że do wierzchołka $c$ trafi co najwyżej $max(K_a-1, 0) + max(K_b-1,0)$ wątków
z **(3)** wiemy że $K_a+K_b \le N$
w takim razie w wierzchołku $c$ znajduje się maksymalnie $N-k-1$ wątków co dowodzi tezy

#### Konkluzja
wiemy zatem że na poziomie $k$ drzewa wierzchołki mają co najwyżej $N-k$ wątków

można zatem zauważyć że na poziomie $N-1$ drzewa mamy maksymalnie $1$ wątek a z **(4)** wiemy że w takim razie jeśli wątek tam dojdzie dostanie STOP

1) w takim razie każdy wątek dostanie kiedyś STOP ponieważ prędzej czy później natrafi na wierzchołek w którym będzie jedyny lub dostanie STOP wcześniej
2) zatem sumując odpowiednio liczbę wierzchołków z każdego poziomu liczbę wierzchołków można ograniczyć do $1 + 2 + \ldots + N=\frac{N+1}{2} \times N$

![](https://i.imgur.com/t4pDt84.png)