ОС1/Модификације октобар 2025
Modifikacija: joinAll (30p)
Doodati sistemske pozive thread_add_child i thread_join_all.
thread_add_child registruje dete-nit za tekuću (pozivajuću) nit.
thread_join_all blokira tekuću nit dok se sva njena deca ne izvrše.
U okviru C++ API dodati nestatičke metode addChild(Thread* child) i joinAll() u klasu Thread.
Napisati test program: nit A pravi 3×B i 1×C, svaki B pravi 3×C. Roditelj čeka svu svoju decu pre nego što nastavi.
Rešenje
TCB.hpp
Dodato na vrhu fajla (forward declaration):
class Semaphore;
Nova polja u private sekciji:
TCB* parent; // pokazivač na roditelja (nullptr ako nema)
int childCount; // broj nezavršene dece
Semaphore* childrenSem; // semafor na koji roditelj čeka u joinAll
Nove metode u public sekciji:
static void addChild(TCB* parent, TCB* child);
static void joinAllChildren();
TCB.cpp
Dodat include na vrhu:
#include "../h/Semaphore.hpp"
U konstruktoru, inicijalizacija novih polja (posle this->finished = false;):
this->parent = nullptr;
this->childCount = 0;
this->childrenSem = nullptr;
Izmenjen dispatch() — else grana proširena da obavesti roditelja kad dete završi:
void TCB::dispatch() {
TCB *old = running;
if (!old->isFinished()) {
Scheduler::put(old);
} else {
if (old->parent != nullptr) {
old->parent->childCount--;
if (old->parent->childCount == 0 && old->parent->childrenSem != nullptr) {
old->parent->childrenSem->sem_signal();
}
}
}
running = Scheduler::get();
contextSwitch(&old->context, &running->context);
}
Nove funkcije:
void TCB::addChild(TCB* parent, TCB* child) {
child->parent = parent;
parent->childCount++;
if (parent->childrenSem == nullptr) {
Semaphore::createSemaphore(&parent->childrenSem, 0);
}
}
void TCB::joinAllChildren() {
if (running->childCount <= 0) return;
running->childrenSem->sem_wait();
}
syscall_c.hpp
Dodate deklaracije:
void thread_add_child(thread_t child);
void thread_join_all();
syscall_c.cpp
void thread_add_child(thread_t child) {
if (child == nullptr) return;
do_syscall(0x16, (uint64)child);
}
void thread_join_all() {
do_syscall(0x17);
}
RISCV.cpp (handleSupervisorTrap)
Dodate nove case grane u switch (posle case 0x13 dispatch):
case 0x16: {
TCB *child = (TCB*)arg1;
TCB::addChild(TCB::running, child);
break;
}
case 0x17: {
TCB::joinAllChildren();
break;
}
syscall_cpp.hpp
Dodate metode u klasu Thread (public sekcija):
void addChild(Thread* child);
void joinAll();
syscall_cpp.cpp
void Thread::addChild(Thread* child) {
thread_add_child(child->getMyHandle());
}
void Thread::joinAll() {
thread_join_all();
}
Test primer
#include "../h/syscall_cpp.hpp"
#include "../h/syscall_c.hpp"
#include "../h/printing.hpp"
class ThreadC : public Thread {
public:
ThreadC(int id) : Thread(), id(id) {}
private:
int id;
void run() override {
printString(" C");
printInt(id);
printString(" started\n");
volatile int sum = 0;
for (int i = 0; i < 3000; i++)
for (int j = 0; j < 3000; j++)
sum += j;
printString(" C");
printInt(id);
printString(" finished\n");
}
};
class ThreadB : public Thread {
public:
ThreadB(int id) : Thread(), id(id) {}
private:
int id;
void run() override {
printString(" B");
printInt(id);
printString(" started, creating 3 C children\n");
ThreadC* c[3];
for (int i = 0; i < 3; i++) {
c[i] = new ThreadC(id * 10 + i);
c[i]->start();
this->addChild(c[i]);
}
printString(" B");
printInt(id);
printString(" waiting for children...\n");
this->joinAll();
printString(" B");
printInt(id);
printString(" all children done!\n");
for (int i = 0; i < 3; i++) delete c[i];
}
};
void joinAllTest() {
printString("A started, creating 3 B and 1 C\n");
ThreadB* b[3];
for (int i = 0; i < 3; i++) {
b[i] = new ThreadB(i);
b[i]->start();
thread_add_child(b[i]->getMyHandle());
}
ThreadC* c = new ThreadC(99);
c->start();
thread_add_child(c->getMyHandle());
printString("A waiting for all children...\n");
thread_join_all();
printString("\n=== A: ALL children done! ===\n");
for (int i = 0; i < 3; i++) delete b[i];
delete c;
}
Napomena: start() mora biti pozvan PRE addChild() jer start() interno poziva thread_create() koji postavlja myHandle. Bez toga, getMyHandle() vraća nullptr.
Očekivan output
A started, creating 3 B and 1 C
A waiting for all children...
B0 started, creating 3 C children
B0 waiting for children...
B1 started, creating 3 C children
B1 waiting for children...
B2 started, creating 3 C children
B2 waiting for children...
C99 started
C99 finished
C0 started
C0 finished
C1 started
C1 finished
...svi C završe...
B0 all children done!
B1 all children done!
B2 all children done!
=== A: ALL children done! ===
Kako radi
Svaki TCB ima polje parent (ko je roditelj) i childCount (koliko dece još radi). Roditelj se blokira na semaforu (childrenSem, inicijalno 0). Kad nit završi, u dispatch() se dekrementira parent->childCount. Tek kad poslednje dete spusti brojač na 0, poziva se sem_signal() koji deblokirara roditelja.