ОС1/Модификације фебруар 2023
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čkomhandle. U okviru C++ API dodati nestatičku metodu klasiThreadsa deklaracijomvoid 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čkomhandleukoliko jetjednako nuli, a ukoliko jetveće od nule onda se tekuća nit zaustavlja maksimalnotvremena ili kraće ukoliko se nit označena sahandlezavrši. U okviru C++ API dodati metodu klasiThreadsa deklaracijomvoid 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.