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

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

Модификација: јоинАлл (30п)

Доодати системске позиве thread_add_child и thread_join_all. thread_add_child региструје дете-нит за текућу (позивајућу) нит. thread_join_all блокира текућу нит док се сва њена деца не изврше. У оквиру C++ АПИ додати нестатичке методе addChild(Thread* child) и joinAll() у класу Thread. Написати тест програм: нит А прави 3×Б и 1×Ц, сваки Б прави 3×Ц. Родитељ чека сву своју децу пре него што настави.

Решење

ТЦБ.хпп

Додато на врху фајла (форwард децларатион):

class Semaphore;

Нова поља у private секцији:

TCB* parent;            // pokazivač na roditelja (nullptr ako nema)
int childCount;         // broj nezavršene dece
Semaphore* childrenSem; // semafor na koji roditelj čeka u joinAll

Нове методе у public секцији:

static void addChild(TCB* parent, TCB* child);
static void joinAllChildren();

ТЦБ.цпп

Додат инцлуде на врху:

#include "../h/Semaphore.hpp"

У конструктору, иницијализација нових поља (после this->finished = false;):

this->parent = nullptr;
this->childCount = 0;
this->childrenSem = nullptr;

Измењен dispatch() — елсе грана проширена да обавести родитеља кад дете заврши:

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);
}

Нове функције:

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();
}

сyсцалл_ц.хпп

Додате декларације:

void thread_add_child(thread_t child);
void thread_join_all();

сyсцалл_ц.цпп

void thread_add_child(thread_t child) {
    if (child == nullptr) return;
    do_syscall(0x16, (uint64)child);
}

void thread_join_all() {
    do_syscall(0x17);
}

РИСЦВ.цпп (хандлеСупервисорТрап)

Додате нове цасе гране у сwитцх (после цасе 0x13 диспатцх):

case 0x16: {
    TCB *child = (TCB*)arg1;
    TCB::addChild(TCB::running, child);
    break;
}
case 0x17: {
    TCB::joinAllChildren();
    break;
}

сyсцалл_цпп.хпп

Додате методе у класу Thread (публиц секција):

void addChild(Thread* child);
void joinAll();

сyсцалл_цпп.цпп

void Thread::addChild(Thread* child) {
    thread_add_child(child->getMyHandle());
}

void Thread::joinAll() {
    thread_join_all();
}

Тест пример

#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;
}

Напомена: start() мора бити позван ПРЕ addChild() јер start() интерно позива thread_create() који поставља myHandle. Без тога, getMyHandle() враћа nullptr.

Очекиван оутпут

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! ===

Како ради

Сваки ТЦБ има поље parent (ко је родитељ) и childCount (колико деце још ради). Родитељ се блокира на семафору (childrenSem, иницијално 0). Кад нит заврши, у dispatch() се декрементира parent->childCount. Тек кад последње дете спусти бројач на 0, позива се sem_signal() који деблокирара родитеља.