|
|
| Ред 1: |
Ред 1: |
| == Modifikacija: joinAll ==
| |
|
| |
|
| Dodati sistemske pozive <code>thread_add_child</code> i <code>thread_join_all</code>.
| |
| <code>thread_add_child</code> registruje dete-nit za tekuću (pozivajuću) nit.
| |
| <code>thread_join_all</code> blokira tekuću nit dok se sva njena deca ne izvrše.
| |
| U okviru C++ API dodati nestatičke metode <code>addChild(Thread* child)</code> i <code>joinAll()</code> u klasu <code>Thread</code>.
| |
| 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):
| |
| <syntaxhighlight lang="cpp">
| |
| class Semaphore;
| |
| </syntaxhighlight>
| |
|
| |
| Nova polja u <code>private</code> sekciji:
| |
| <syntaxhighlight lang="cpp">
| |
| TCB* parent; // pokazivač na roditelja (nullptr ako nema)
| |
| int childCount; // broj nezavršene dece
| |
| Semaphore* childrenSem; // semafor na koji roditelj čeka u joinAll
| |
| </syntaxhighlight>
| |
|
| |
| Nove metode u <code>public</code> sekciji:
| |
| <syntaxhighlight lang="cpp">
| |
| static void addChild(TCB* parent, TCB* child);
| |
| static void joinAllChildren();
| |
| </syntaxhighlight>
| |
|
| |
| ==== TCB.cpp ====
| |
| Dodat include na vrhu:
| |
| <syntaxhighlight lang="cpp">
| |
| #include "../h/Semaphore.hpp"
| |
| </syntaxhighlight>
| |
|
| |
| U konstruktoru, inicijalizacija novih polja (posle <code>this->finished = false;</code>):
| |
| <syntaxhighlight lang="cpp">
| |
| this->parent = nullptr;
| |
| this->childCount = 0;
| |
| this->childrenSem = nullptr;
| |
| </syntaxhighlight>
| |
|
| |
| Izmenjen <code>dispatch()</code> — else grana proširena da obavesti roditelja kad dete završi:
| |
| <syntaxhighlight lang="cpp">
| |
| 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);
| |
| }
| |
| </syntaxhighlight>
| |
|
| |
| Nove funkcije:
| |
| <syntaxhighlight lang="cpp">
| |
| 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();
| |
| }
| |
| </syntaxhighlight>
| |
|
| |
| ==== syscall_c.hpp ====
| |
| Dodate deklaracije:
| |
| <syntaxhighlight lang="cpp">
| |
| void thread_add_child(thread_t child);
| |
| void thread_join_all();
| |
| </syntaxhighlight>
| |
|
| |
| ==== syscall_c.cpp ====
| |
| <syntaxhighlight lang="cpp">
| |
| void thread_add_child(thread_t child) {
| |
| if (child == nullptr) return;
| |
| do_syscall(0x16, (uint64)child);
| |
| }
| |
|
| |
| void thread_join_all() {
| |
| do_syscall(0x17);
| |
| }
| |
| </syntaxhighlight>
| |
|
| |
| ==== RISCV.cpp (handleSupervisorTrap) ====
| |
| Dodate nove case grane u switch (posle case 0x13 dispatch):
| |
| <syntaxhighlight lang="cpp">
| |
| case 0x16: {
| |
| TCB *child = (TCB*)arg1;
| |
| TCB::addChild(TCB::running, child);
| |
| break;
| |
| }
| |
| case 0x17: {
| |
| TCB::joinAllChildren();
| |
| break;
| |
| }
| |
| </syntaxhighlight>
| |
|
| |
| ==== syscall_cpp.hpp ====
| |
| Dodate metode u klasu <code>Thread</code> (public sekcija):
| |
| <syntaxhighlight lang="cpp">
| |
| void addChild(Thread* child);
| |
| void joinAll();
| |
| </syntaxhighlight>
| |
|
| |
| ==== syscall_cpp.cpp ====
| |
| <syntaxhighlight lang="cpp">
| |
| void Thread::addChild(Thread* child) {
| |
| thread_add_child(child->getMyHandle());
| |
| }
| |
|
| |
| void Thread::joinAll() {
| |
| thread_join_all();
| |
| }
| |
| </syntaxhighlight>
| |
|
| |
| ==== Test primer ====
| |
| <syntaxhighlight lang="cpp">
| |
| #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;
| |
| }
| |
| </syntaxhighlight>
| |
|
| |
| '''Napomena:''' <code>start()</code> mora biti pozvan PRE <code>addChild()</code> jer <code>start()</code> interno poziva <code>thread_create()</code> koji postavlja <code>myHandle</code>. Bez toga, <code>getMyHandle()</code> vraća <code>nullptr</code>.
| |
|
| |
| ==== Očekivan output ====
| |
| <pre>
| |
| 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! ===
| |
| </pre>
| |
|
| |
| ==== Kako radi ====
| |
| Svaki TCB ima polje <code>parent</code> (ko je roditelj) i <code>childCount</code> (koliko dece još radi). Roditelj se blokira na semaforu (<code>childrenSem</code>, inicijalno 0). Kad nit završi, u <code>dispatch()</code> se dekrementira <code>parent->childCount</code>. Tek kad poslednje dete spusti brojač na 0, poziva se <code>sem_signal()</code> koji deblokirara roditelja.
| |