summaryrefslogtreecommitdiff
path: root/src/Task.cc
blob: 938b3de40d9e82b846aafbe243804e34eb7ddc5b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#include "Task.h"
#include "TaskMan.h"
#include "Exceptions.h"
#include "Printer.h"
#include "Local.h"

static Balau::LocalTmpl<Balau::Task> localTask;

Balau::Task::Task() {
    size_t size = stackSize();
    m_stack = malloc(size);
    coro_create(&m_ctx, coroutine, this, m_stack, size);

    m_taskMan = TaskMan::getTaskMan();
    m_taskMan->registerTask(this);

    m_tls = g_tlsManager->createTLS();
    void * oldTLS = g_tlsManager->getTLS();
    g_tlsManager->setTLS(m_tls);
    localTask.set(this);
    g_tlsManager->setTLS(oldTLS);

    m_status = STARTING;
    m_loop = NULL;
}

Balau::Task::~Task() {
    if (m_loop)
        ev_loop_destroy(m_loop);
    free(m_stack);
    free(m_tls);
}

void Balau::Task::coroutine(void * arg) {
    Task * task = reinterpret_cast<Task *>(arg);
    Assert(task);
    try {
        task->m_status = RUNNING;
        task->Do();
        task->m_status = STOPPED;
    }
    catch (GeneralException & e) {
        Printer::log(M_WARNING, "Task %s caused an exception: `%s' - stopping.", task->getName(), e.getMsg());
        task->m_status = FAULTED;
    }
    catch (...) {
        Printer::log(M_WARNING, "Task %s caused an unknown exception - stopping.", task->getName());
        task->m_status = FAULTED;
    }
    coro_transfer(&task->m_ctx, &task->m_taskMan->m_returnContext);
}

void Balau::Task::switchTo() {
    m_status = RUNNING;
    void * oldTLS = g_tlsManager->getTLS();
    g_tlsManager->setTLS(m_tls);
    coro_transfer(&m_taskMan->m_returnContext, &m_ctx);
    g_tlsManager->setTLS(oldTLS);
    if (m_status == RUNNING)
        m_status = IDLE;
}

void Balau::Task::yield(bool override) {
    if (m_loop && override) {
        ev_run(m_loop, 0);
    } else {
        coro_transfer(&m_ctx, &m_taskMan->m_returnContext);
    }
}

Balau::Task * Balau::Task::getCurrentTask() {
    return localTask.get();
}

void Balau::Task::waitFor(Balau::Events::BaseEvent * e, bool override) {
    struct ev_loop * loop = m_loop;
    if (!override)
        m_loop = NULL;
    e->registerOwner(this);
    m_loop = loop;
}

void Balau::Task::setPreemptible(bool enable) {
    if (!m_loop && !enable) {
        m_loop = ev_loop_new(EVFLAG_AUTO);
    } else if (m_loop) {
        ev_loop_destroy(m_loop);
        m_loop = NULL;
    }
}

struct ev_loop * Balau::Task::getLoop() {
    if (m_loop)
        return m_loop;
    else
        return getTaskMan()->getLoop();
}

void Balau::Events::BaseEvent::doSignal() {
    m_signal = true;
    m_task->getTaskMan()->signalTask(m_task);
}

Balau::Events::TaskEvent::TaskEvent(Task * taskWaited) : m_taskWaited(taskWaited) {
    m_taskWaited->m_waitedBy.push_back(this);
}

Balau::Events::Timeout::Timeout(ev_tstamp tstamp) {
    set(tstamp);
}

void Balau::Events::Timeout::set(ev_tstamp tstamp) {
    m_evt.set<Timeout, &Timeout::evt_cb>(this);
    m_evt.set(tstamp);
}

void Balau::Events::Timeout::gotOwner(Task * task) {
    m_evt.set(task->getLoop());
    m_evt.start();
}

void Balau::Events::Timeout::evt_cb(ev::timer & w, int revents) {
    doSignal();
}

void Balau::Events::Custom::gotOwner(Task * task) {
    m_loop = task->getLoop();
}