---
# System prepended metadata

title: 'Ćwiczenia 3, grupa śr. 10-12, 25 października 2023'
tags: [pwit, ćwiczenia, PRW23]

---

# Ćwiczenia 3, grupa śr. 10-12, 25 października 2023

###### tags: `PRW23` `ćwiczenia` `pwit`

## Deklaracje

Gotowość rozwiązania zadania należy wyrazić poprzez postawienie X w odpowiedniej kolumnie! Jeśli pożądasz zreferować dane zadanie (co najwyżej jedno!) w trakcie dyskusji oznacz je znakiem ==X== na żółtym tle.

**UWAGA: Tabelkę wolno edytować tylko wtedy, gdy jest na zielonym tle!**

:::danger
|                        | 1     | 2   | 3   | 4   | 5   | 6   | 7   | 8   |
| ----------------------:| ----- | --- | --- | --- | --- | --- | --- | --- |
Mikołaj Balwicki         |       |     |     |     |     |     |     |     |  
Konrad Kowalczyk         |   X   |     |  X  |  X  |  X  |     |     |     |
Jakub Krzyżowski         |       |     |  X  |  X  |  X  |     |     |     |  
Łukasz Magnuszewski      |       |     |     |     |     |     |     |     |  
Marcin Majchrzyk         |       |     |  X  |  X  |  X  |  X  |  X  |  X  |  
Jan Marek                |    X  |     |  X  |  X  |  X  |  X  |  X  |     |  
Michał Mękarski          |       |     |     |     |     |     |     |     |  
Marcin Mierzejewski      |       |     |     |     |     |     |     |     |  
Juan Jose Nieto Ruiz     |      x |   x  |     |     |  x   |  x   |   x  |  x   |  
Dominik Olejarz          |       |     |     |     |     |     |     |     |  
Konrad Oleksy            |       |     |  X   |   X  |  X   |  X   |  X   |     |  
Javier Rábago Montero    |   X   |  X  |  X  |  X  |  X  |     |     |  X  |  
:::


Tutaj można zadeklarować zaległe zadania 6 i 9 z listy 2:

:::danger
|                        | 2.6     | 2.9   |
| ----------------------:| -----   | ---   |
| Javier Rábago Montero  |         |   X   |
|                        |         |       |
:::





:::info
**Uwaga:** Po rozwiązaniu zadania należy zmienić kolor nagłówka na zielony.
:::



## Zadanie 1
:::success
Autor: Konrad Kowalczyk	
:::

![](https://hackmd.io/_uploads/SJZVeIIMp.png)


<b>Wzajemne wykluczenie - spełnia</b>
Wątek musi ustawić swoją flagę na `true`, żeby wejść do sekcji krytycznej.
Rozpatrzmy dwa przypadki:
- Wątek, który znalazł się w sekcji krytycznej jako pierwszy (wątek `i`) ma mniejszy numer niż ten, który chce się dostać (wątek `j`) - wtedy wątek `j`, aby wejść, musiałby nie wykryć żadnego wątku podczas iterowania się w pierwszej pętli `for`. Jedyną taką możliwością jest sytuacja, w której wątek `i` ustawiłby swoją flagę na `true` po tym, jak wątek `j` sprawdzi jego flagę w pętli `for`. Jednak zauważmy, że wątek `j` ustawił swoją flagę na `true` przed wejściem do pętli `for`, a zatem wątek `i` wtedy wykryłby wątek `j` podczas iterowania się w drugiej pętli `for`, i musiałby na niego zaczekać, co jest sprzeczne z założeniem. Możliwe jest, że jakiś inny wątek sprawi, że wątek `j` ustawi swoją flagę na `false`, ale wtedy wątek `j` przerwie pętlę `for` i przejdzie ponownie przez `while`, gdzie tym razem będzie musiał już wykryć wątek `i` i na pewno nie wejdzie do sekcji krytycznej, gdy `i` wciąż w niej jest.
- Wątek, który znalazł się w sekcji krytycznej jako pierwszy (wątek `i`) ma większy numer niż ten, który chce się dostać (wątek `j`) - wtedy wątek `j`, aby wejść, musiałby nie wykryć żadnego wątku podczas iterowania się w drugiej pętli `for`. Jedyną taką możliwością jest sytuacja, w której wątek `i` ustawiłby swoją flagę na `true` po tym, jak wątek `j` sprawdzi jego flagę w pętli `for`. Jednak zauważmy, że w momencie iterowania się w drugiej pętli `for` wątek `j` ma ustawioną swoją flagę na `true`. Wątek `i`, po ustawieniu swojej flagi na `true` musiałby przeiterować się przez pierwszą pętlę `for`, gdzie w takiej sytuacji wykryłby wątek `j` i musiałby na niego zaczekać, co jest sprzeczne z założeniem.

<b>Niezakleszczenie - spełnia</b>
W pętli `do {...} while` wątek `i` sprawdza tylko wątki o numerze mniejszym od niego. To oznacza, że z pętli zawsze wyjdzie co najmniej wątek o najmniejszym numerze. A skoro wątek o następnym numerze mógł czekać tylko na niego, to on też zostanie odblokowany i tak dalej. W takim razie wątki nie mogą zakleszczyć się w pierwszej pętli `while`. Zauważmy też, że niemożliwym jest, aby wszystkie wątki zakleszczyły się w drugiej pętli `while`, ponieważ wątek o najwyższym numerze w ogóle nie wykonuje pętli `for`. A wątek o numerze o jeden niższym mógł czekać tylko na niego, a zatem na pewno się odblokuje i tak dalej.
<b>Niezagłodzenie - nie spełnia</b>
Możemy zauważyć, że wątek o numerze 0 zawsze przejdzie przez `do {...} while`. Może zajść sytuacja, w której pozostałe wątki będą na niego czekać. Jeśli nie wybudzą się w porę, wątek 0 zdąży ponownie ustawić swoją flagę na `true` i ponownie nic nie zatrzyma go przed kolejnym wejściem do sekcji krytycznej.

## Zadanie 2
:::success
Autor: Juan Jose Nieto Ruiz	
:::
The theorem that states you need exactly n shared bits to implement a lock for n threads is known as the "bit constraint theorem" in mutual exclusion theory. To prove this theorem, we can use a proof by contradiction.

Suppose it's possible to implement a lock for n threads with fewer than n bits of shared memory, say k bits, where k < n. Now, we'll examine the contradiction this creates.

Each bit in shared memory can be in one of two states: 0 or 1. Therefore, there are a total of 2^k possible combinations of states for k bits of shared memory.

Now, consider that there are n different threads attempting to acquire the lock. Each thread can be in one of the following states: locked (waiting to acquire the lock) or unlocked (either already acquired the lock or doing something else). For n different threads, there are 2^n possible combinations of states for those threads.

Since there are 2^k possible combinations of states in the k bits of shared memory and 2^n possible combinations of states for the n threads, and k < n, then by the Pigeonhole Principle, there will be at least one combination of states in the k bits that repeats for different combinations of states of the n threads.

This fact leads to a contradiction. If two different sets of states for the n threads match the same combination of states in the k bits of shared memory, then you cannot guarantee proper mutual exclusion. If a thread relies only on the k bits to determine if it can acquire the lock, it won't be able to distinguish between these two sets of threads, which can lead to a violation of mutual exclusion.


## Zadanie 3
:::success
Autor: Jakub Krzyżowski	
:::
![](https://hackmd.io/_uploads/HkVGrPBMT.png)
![](https://hackmd.io/_uploads/HyA0gqBGa.png)
Linearyzowalne:
$r.write(1) \rightarrow r.read(1) \rightarrow r.write(2) \rightarrow r.read(2)$

![](https://hackmd.io/_uploads/S1CVbcBf6.png)
Linearyzowalne:
$r.write(2) \rightarrow r.write(1) \rightarrow r.read(1) \rightarrow r.read(1)$

![](https://hackmd.io/_uploads/Sy36-5Hzp.png)
Nielinearyzowalne:
W kolejce FIFO żeby ściągnąć y z p najpierw trzeba ściągnąć x.

![](https://hackmd.io/_uploads/rJfzz5Sfp.png)
Nielinearyzowalne:
H|p jest taka sama jak poprzednio, a H|q jest symetryczna.

## Zadanie 4
:::success
Autor:Jan Marek	
:::

![](https://hackmd.io/_uploads/rydvWLIzT.png)
![](https://hackmd.io/_uploads/ByPs-IIGa.png)
Nie mamy oczekujących wywołań, więć nie musimy rozszerzać H do G (a więc "H=G").

**(1)**
![](https://hackmd.io/_uploads/H1Bp-U8fa.png)
**Historia G:**
```
B r.write(1)
A r.read(1)
C r.write(2)
A r: 1
C r: void
B r: void
B r.read(2)
B r: 2
```
**Historia S:**
```
B r.write(1)
B r: void
A r.read(1)
A r: 1
C r.write(2)
C r: void
B r.read(2)
B r: 2
```

```
→G =  {B r.write(1) -> B r.read(2),
       A r.read(1) -> B r.read(2),
       C r.write(2) -> B r.read(2)}
```
```
→S = {B r.write(1) -> A r.read(1) -> C r.write(2) -> B r.read(2)}
```

**(2)**
![](https://hackmd.io/_uploads/H191fILM6.png)
**Historia G:**
```
B r.write(1)
A r.read(1)
C r.write(2)
A r: 1
C r: void
B r: void
B r.read(1)
B r: 1    
```
**Historia S:**
```
C r.write(2)
C r: void
B r.write(1)
B r: void
A r.read(1)
A r: 1
B r.read(1)
B r: 1
```

```
→G = {C r.write(2) -> B r.read(1),
      B r.write(1) -> B r.read(1),
      A r.read(1) -> B r.read(1)}
```
```
→S = {C r.write(2) -> B r.write(1) -> A r.read(1) -> B r.read(1)}
```

**(3)**
![](https://hackmd.io/_uploads/SywQMILfp.png)
**Historia G:**
```
A p.enq(x)
A p: void
B p.enq(y)
B p: void
A p.deq()
A p: y
```

G nie odpowiada żadnej legalnej sekwencyjnej historii S, ponieważ `p.enq(x) -> p.enq(y) -> p.deq(y)`, co jest sprzeczne. Przed wykonaniem p.deq(y) pierwszym elementem w kolejce p będzie x, więc wykonanie p.deq(y) jest niemożliwe.

**(4)**
![](https://hackmd.io/_uploads/HySHzL8G6.png)
**Historia G:**
```
A p.enq(x)
A p: void
B q.enq(y)
B q: void
A q.enq(x)
A q: void
B p.enq(y)
B p: void
A p.deq()
A p: y
B q.deq()
B q: x
```

Znów G nie odpowiada żadnej legalnej sekwencyjnej historii S, ponieważ mamy:
1. `p.enq(x) -> p.enq(y) -> p.deq(y)`
oraz
2. `q.enq(y) -> q.enq(x) -> q.deq(x)`
Co jest niemożliwe.

## Zadanie 5
:::success
Autor: Konrad Oleksy	
:::
```java=
class MergeSort implements Runnable {
    protected int arr[];
    protected int help_arr[];
    protected int l, r;

    MergeSort(int arr[],int help_arr[], int l, int r) {
        this.arr = arr;
        this.l = l;
        this.r = r;
        this.help_arr = help_arr;
        
    }

    private void merge(int l, int m, int r) {

         for (int i = l; i <= r; i++) {
            this.help_arr[i] = arr[i];
        }

        int left_index = l;
        int left_end =m;

        int right_index = m+1;
        int right_end= r;

        int index=l;

        while (left_index <= left_end && right_index <= right_end) {
            if (help_arr[left_index] <= help_arr[right_index]) {
                arr[index] = help_arr[left_index];
                left_index += 1;
            } else {
                arr[index] = help_arr[right_index];
                right_index += 1;
            }
            index += 1;
        }
        while (left_index <= left_end) {
            arr[index] = help_arr[left_index];
            index += 1;
            left_index += 1;
        }
        while (right_index <= right_end) {
            arr[index] = help_arr[right_index];
            index += 1;
            right_index += 1;
        }
    }

    public void sort(int l, int r) {
        if (l < r) {
            int m = (l + r) / 2;
            sort(l, m);
            sort(m + 1, r);
            merge(l, m, r);
        }
    }

    @Override
    public void run() {

        if (this.l < this.r) {
            int m = (this.l + this.r) / 2;
            MergeSort left = new MergeSort(arr,help_arr, this.l, m);
            MergeSort right = new MergeSort(arr,help_arr, m + 1, this.r);
            Thread t1 = new Thread(left);
            Thread t2 = new Thread(right);
            t1.start();
            t2.start();
            try {
                t1.join();
                t2.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.merge(this.l, m, this.r);
        }
    }
}


public class Zadanie5 {

    public static void main(String[] args) {
        int arr[] = {4, 3, 2, 1};
        MergeSort w = new MergeSort(arr,new int[arr.length], 0, arr.length-1);

        Thread t = new Thread(w);

        t.start();
        try {
            t.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        for (int i = 0; i < arr.length; i++)
            System.out.printf("%d ", arr[i]);


    }
}

```


## Zadanie 6
:::success
Autor:Marcin Majchrzyk	
:::
```java=
public class MergeSort implements Runnable {
    protected int[] arr;
    protected int[] helper;
    protected int l, r;

    MergeSort(int[] arr, int l, int r, int[] helper) {
        this.arr = arr;
        this.l = l;
        this.r = r;

        this.helper = helper;
    }

    private void merge(int l, int m, int r) {
        int leftSize = m - l + 1;
        int rightSize = r - m;

        int arrPosition = l;
        int leftPosition = l, rightPosition = l + leftSize;

        int leftEnd = leftPosition + leftSize, rightEnd = rightPosition + rightSize;

        System.arraycopy(arr, leftPosition,  helper, leftPosition,  leftSize);
        System.arraycopy(arr, rightPosition, helper, rightPosition, rightSize);

        while (leftPosition < leftEnd && rightPosition < rightEnd) {
            if (helper[leftPosition] <= helper[rightPosition]) {
                arr[arrPosition] = helper[leftPosition];
                leftPosition++;
            } else {
                arr[arrPosition] = helper[rightPosition];
                rightPosition++;
            }
            arrPosition++;
        }

        System.arraycopy(helper, leftPosition,  arr, arrPosition, leftEnd - leftPosition);
        System.arraycopy(helper, rightPosition, arr, arrPosition, rightEnd - rightPosition);
    }

    private void sortSmall(int l, int r)
    {
        if (arr[l] > arr[r])
        {
            int t = arr[l];
            arr[l] = arr[r];
            arr[r] = t;
        }
    }

    @Override
    public void run() {
        if (this.l < this.r) {
            int m = (this.l + this.r) / 2;

            if (r - l < 4) {
                sortSmall(l, m);
                sortSmall(m+1, r);
            } else {
                MergeSortv2 left = new MergeSort(arr, this.l, m, helper);
                MergeSortv2 right = new MergeSort(arr, m + 1, this.r, helper);

                Thread t1 = new Thread(left);

                t1.start();

                right.run();

                try {
                    t1.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            this.merge(this.l, m, this.r);
        }
    }
}

```

## Zadanie 7
:::success
Autor: Jan Marek	
:::
```java
class MergeSort implements Runnable {
    protected int arr[];
    protected int shared_arr[];
    protected int left, right;

    public static final int MAX_THREAD_COUNT = 10;
    private static int active_threads_count = 0;
    private final Object lock = new Object();

    MergeSort(int arr[], int shared_arr[], int left, int right) {
        this.arr = arr;
        this.shared_arr = shared_arr;
        this.left = left;
        this.right = right;
    }

    private void push_to_shared_arr(int arr[], int left, int middle, int right, int left_size, int right_size) {
        for (int i = 0; i < left_size; i++) {
            shared_arr[left + i] = arr[left + i];
        }
        for (int i = 0; i < right_size; i++) {
            shared_arr[middle + 1 + i] = arr[middle + 1 + i];
        }
    }

    private void merge(int left, int middle, int right) {
        int left_size = middle - left + 1;
        int right_size = right - middle;
        push_to_shared_arr(arr, left, middle, right, left_size, right_size);
        int i = 0, j = 0; // i - wsk. w lewej tab, j - wsk. w prawej tab
        int k = left; // akt. wsk.
        while (i < left_size && j < right_size) {
            if (shared_arr[left + i] <= shared_arr[middle + 1 + j]) {
                arr[k] = shared_arr[left + i];
                i += 1;
            } else {
                arr[k] = shared_arr[middle + 1 + j];
                j += 1;
            }
            k += 1;
        }
        while (i < left_size) {
            arr[k] = shared_arr[left + i];
            k += 1;
            i += 1;
        }
        while (j < right_size) {
            arr[k] = shared_arr[middle + 1 + j];
            k += 1;
            j += 1;
        }
    }

    public void sort(int left, int right) {
        if (left < right) {
            int middle = (left + right) / 2;
            sort(left, middle);
            sort(middle + 1, right);
            merge(left, middle, right);
        }
    }

    @Override
    public void run() {

        synchronized (lock) {
            active_threads_count += 1;
        }
      
        if (this.left < this.right) {
            boolean sort_serially;
            synchronized (lock) {
                sort_serially = active_threads_count > MAX_THREAD_COUNT - 2;
            }
            if (sort_serially) {
                this.sort(this.left, this.right);
            }
            else {
                int middle = (this.left + this.right) / 2;
                MergeSort left_sort = new MergeSort(arr, shared_arr, this.left, middle);
                MergeSort right_sort = new MergeSort(arr, shared_arr, middle + 1, this.right);
                Thread thread_1 = new Thread(left_sort);
                Thread thread_2 = new Thread(right_sort);
                thread_1.start();
                thread_2.start();
                try {
                    thread_1.join();
                    thread_2.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                this.merge(this.left, middle, this.right);
            }
        }
        
        synchronized (lock) {
            active_threads_count -= 1;
        }

    }
}


public class RookieMergeSort {

    public static void main(String[] args) {
        int arr[] = {4, 3, 55, 2, 1, 18, 77, 0, -100, 12, 12};
        int[] shared_arr = new int[11];
        MergeSort sort = new MergeSort(arr, shared_arr, 0, 10);

        Thread main_thread = new Thread(sort);

        main_thread.start();
        try {
            main_thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        for (int i = 0; i < 11; i++)
            System.out.printf("%d ", arr[i]);


    }
}
```

## Zadanie 8
:::danger
Autor: Marcin Majchrzyk	
:::

