ОС1/Модификације фебруар 2023

Извор: SI Wiki
Пређи на навигацију Пређи на претрагу

Sledeće modifikacije su se pojavile na odbrani projekta u februarskom roku 2023.

Mogli ste da izabere da radite ili 1. modifikaciju ili 2. modifikaciju.

Modifikacija bez asinhrone promene konteksta

  • Dodati sistemski poziv čija je deklaracija void thread_join(thread_t* handle) koji tekuću nit zaustavlja dok se ne završi izvršavanje niti označene ručkom handle. U okviru C++ API dodati nestatičku metodu klasi Thread sa deklaracijom void join(). Napisati test program koristeći C++ API sa tri niti, gde će jedna nit čekati da se završe druge dve. Svaka nit na početku ispisuje poruku koja je to nit, zatim radi neku obradu(implementirati ugnježdenim petljama) i na kraju ispisuje poruku da se završila.

Rešenje

syscall_c.hpp

class TCB;
typedef TCB* thread_t;

void thread_join(thread_t* handle);


syscall_c.cpp

void thread_join(thread_t* handle)
{
    uint64 number = 0x03; // Mogao je bilokoji, slobodan, ulaz

    __asm__ volatile("mv a1, %0" : : "r" (handle));
    __asm__ volatile("mv a0, %0" : : "r" (number));

    __asm__ volatile("ecall");
}


TCB.hpp

typedef TCB* thread_t;

class TCB
{
public:
	// ...
private:
	// ...
    static void join (thread_t* handle);
    // ...
};


TCB.cpp

/* Glavni deo resenja */
void TCB::join(thread_t* handle)
{
	while(!(*handle)->isFinished())
		TCB::dispatch();
}


riscv.cpp

case 0x03: // Mogao je bilokoji, slobodan, ulaz
{
	thread_t *handle;
	__asm__ volatile ("mv %0, a1" : "=r" (handle));

	TCB::join(handle);

    break;
}


syscall_cpp.hpp

class Thread
{
public:
	// ...
    void join(); // Obavezno nestaticka metoda
protected:
    // ...
private:
    thread_t myHandle; // Ovo je vec postojalo
};

syscall_cpp.cpp

void Thread::join()
{
	if (myHandle)
		thread_join(&myHandle);
}


main.cpp

#include "../h/syscall_cpp.hpp"

bool finishedA = false;
bool finishedB = false;

class WorkerA: public Thread
{
    void mod_fun_A(void* arg);
public:
    WorkerA():Thread() {}

    void run() override
    {
        mod_fun_A(nullptr);
    }
};

class WorkerB: public Thread
{
    void mod_fun_B(void* arg);
public:
    WorkerB():Thread() {}

    void run() override
    {
        mod_fun_B(nullptr);
    }
};

void WorkerA::mod_fun_A(void *arg)
{
    for (uint64 i = 0; i < 10; i++)
    {
        printString("A: i=");
        printInt(i);
        printString("\n");

        for (uint64 j = 0; j < 10000; j++)
        {
            for (uint64 k = 0; k < 30000; k++)
            {
                /* busy wait */
            }

            thread_dispatch();
        }
    }

    printString("A finished!\n");
    finishedA = true;
}

void WorkerB::mod_fun_B(void *arg)
{
    for (uint64 i = 0; i < 16; i++)
    {
        printString("B: i=");
        printInt(i);
        printString("\n");

        for (uint64 j = 0; j < 10000; j++)
        {
            for (uint64 k = 0; k < 30000; k++)
            {
                /* busy wait */
            }

            thread_dispatch();
        }
    }

    printString("B finished!\n");
    finishedB = true;
    thread_dispatch();
}


int main()
{
    Riscv::w_stvec((uint64) &Riscv::supervisorTrap);

    Thread* main = new Thread(nullptr, nullptr);
    printString("Main Created\n");
    TCB::running = main->getMyHandle();

    /* Modifikacija */
    Thread* threads[2];

    threads[0] = new WorkerA();
    printString("ThreadA created\n");

    threads[1] = new WorkerB();
    printString("ThreadB created\n");

    for(int i = 0; i < 2; i++)
        threads[i]->start();

    threads[0]->join(); // Blokiraj main nit(u ovom slucaju) dok se nit 0 ne zavrsi
    threads[1]->join(); // Blokiraj main nit(u ovom slucaju) dok se nit 1 ne zavrsi

    for (auto thread: threads)
        delete thread;

    printString("Main Stopped\n");

    return 0;
}

Dodatno

Dodatno, prilikom ispitivanja, tražili su da nit A čeka da se završi nit B. U tom slučaju morali smo ovo:

threads[0] = new WorkerA();
printString("ThreadA created\n");

threads[1] = new WorkerB();
printString("ThreadB created\n");


for(int i = 0; i < 2; i++)
    threads[i]->start();

da definisemo globalno u fajlu "main.cpp" i da nakon kreiranja main-a, odmah uradimo dispatch.

A onda da unutar funkcije "mod_fun_A", stavimo:

threads[1]->join();

threads[1] je nit B.

Modifikacija sa asinhronom promenom konteksta

  • Dodati sitemski poziv čija je deklaracija void thread_join(thread_t* handle, time_t t) koji tekuću nit zaustavlja dok se ne završi izvršavanje niti označenom ručkom handle ukoliko je t jednako nuli, a ukoliko je t veće od nule onda se tekuća nit zaustavlja maksimalno t vremena ili kraće ukoliko se nit označena sa handle završi. U okviru C++ API dodati metodu klasi Thread sa deklaracijom void join(time_t).

Napisati test program koristeći C++ API sa tri niti, gde će dve niti čekati da se završi treća nit, ali jedna treba da čeka maksimalno neko vreme t, a druga dok se zaista ne završi treća nit. Treća nit treba da traje dovoljno dugo kako bi se ispoljilo opisano ponašanje. Svaka nit na početku ispisuje poruku koja je to nit, zatim radi neku obradu(implementirati ugnježdenim petljama) i na kraju ispisuje poruku da se završila. Dodatno, nit koja čeka maksimalno t vremena treba nakon što istekne to vreme da ispiše poruku da je nastavila izvršavanje.