Repository navigation
Expand file tree
/
Copy paththread.h
More file actions
244 lines (215 loc) · 7.61 KB
/
Copy paththread.h
File metadata and controls
244 lines (215 loc) · 7.61 KB
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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
#pragma once
#include <cxx/linked_list.h>
#include <elf/elf.h>
#include <libkernel/measure.h>
#include "core/vm.h"
#include "platform/exception.h"
namespace core::thread {
using tid_t = int64_t;
struct Thread;
/**
* @brief The IPC structure for inter-thread communication.
*/
struct Ipc final {
/// @brief Finite state machine states describing IPC lifecycle
enum class State {
/// @brief Is not blocked. Requires State != IPC_WAIT.
INITIAL,
/// @brief Is waiting for `Send()`. Requires State == IPC_WAIT.
AWAIT_SEND,
/// @brief Is waiting for `Receive()`. Requires State == IPC_WAIT.
AWAIT_RECEIVE,
/// @brief Is waiting for `Reply()`. Requires State == IPC_WAIT.
AWAIT_REPLY,
/// @brief Receiver has replied to the awaiting sender. Requires State == IPC_DEFERRED.
REPLIED,
/// @brief Sender has sent to the awaiting replier. Requires State == IPC_DEFERRED.
SENT
};
/// @brief The current state of the IPC, see the `Ipc::State` enum
State state = State::INITIAL;
/// @brief Buffer to hold send/reply
uint8_t buf[MaxMessageLength];
union {
/// @brief Metadata to keep when thread is sending data (`state != AWAIT_SEND`)
struct {
/// @brief Sent message length
size_t msg_len;
/// @brief The user's reply buffer maximum size
size_t expected_reply_len;
/// @brief Pointer to the user's reply buffer in the thread's ASPACE
char* user_reply_buf;
/// @brief The thread to send the message to
Thread* receiver;
} send;
/// @brief Metadata to keep when thread is expecting to receive (`state == AWAIT_SEND`)
struct {
/// @brief The user's result TID buffer
tid_t* user_tid_buf;
/// @brief The user's buffer to copy sent messages into
char* user_send_buf;
/// @brief The maximum size of `user_send_buf`
size_t expected_send_len;
} receive;
};
};
/**
* @brief The thread descriptor structure.
*/
struct Thread final : public cxx::IntrusiveListNode<Thread> {
/// @brief Finite state machine states describing thread lifecycle
enum class State {
/// @brief This thread is uninitialized
INVALID,
/// @brief This thread is currently being created
CREATING,
/// @brief This thread is in the scheduler queue
READY,
/// @brief This thread is the current thread
RUNNING,
/// @brief This thread is blocked on an IPC cycle
IPC_WAIT,
/// @brief This thread has a deferred IPC action waiting
IPC_DEFERRED,
/// @brief This thread is blocked on an event
EVENT_WAIT
};
/// @brief The name of the task, to be used for debug only
char name[64];
/// @brief The user-facing thread ID
int64_t user_tid;
/// @brief The thread's parent's thread ID
int64_t user_pid;
/// @brief The priority of the thread
uint64_t priority;
/// @brief The state of the thread, see the `Thread::State` enum
State state;
/// @brief Thread's entry address, here for ease of access
uint64_t entry_address;
/// @brief Thread's _Exit() function address, here for exceptions
uint64_t exit_address;
/// @brief Address for thread's fake TLS section, 0 if there is no TLS
uint64_t tls_address;
/// @brief Saved thread state
platform::exception::Frame saved_context;
/// @brief The address space of the thread
mem::AddressSpace aspace;
/// @brief The ELF image of the thread
elf::Parser image;
/// @brief IPC context
Ipc ipc;
/// @brief Threads waiting to send IPC to this thread
cxx::IntrusiveList<Thread> mailbox;
/// @brief Error flag (this should really be a state)
bool thread_error;
public:
Thread(mem::Page* l0, uint64_t priority, elf::Parser&);
~Thread() = delete;
/**
* @brief Set the system call return code
*
* @param rc The return value
*/
void set_retcode(uint64_t rc) {
libkernel::log(LL::VERBOSE, "%lld for %s\r\n", rc, dump());
saved_context.x[0] = rc;
}
/**
* @brief Removes this thread from all the scheduling queues. This
* will invalidate the current thread.
*/
void remove_from_scheduler();
/**
* @brief Add this thread back to the scheduling queues. This will
* invalidate the current thread.
*/
void add_to_scheduler();
/**
* @brief Destroys the current thread and frees it. This will invalidate
* the current thread.
*/
void destory();
/**
* @brief Pops one receiver out of the mailbox and copies the sender's
* message into the receiving buffer. Returns the message length.
*/
size_t receive_one_mailbox();
/**
* @brief Dump the thread name and state as a printable string.
*/
char const* dump(bool print_state = false) const;
/**
* @brief Mark the current thread as in an error state
*/
void set_error_state() { thread_error = true; }
public:
libkernel::PerfMonitor& get_event_block_perf() const;
libkernel::PerfMonitor& get_ipc_block_perf() const;
libkernel::PerfMonitor& get_runtime_perf() const;
libkernel::PerfMonitor& get_reschedule_perf() const;
};
/**
* @brief Initializes all the tasking structures and such.
*/
void init();
/**
* @brief Creates a new thread and adds it to the scheduling queue.
*
* @param name Name of the new thread, for debugging only.
* @param image The ELF image for the thread to load.
* @param priority The priority of the new thread.
* @return `user_tid` of the created thread if it succeeds
*/
Result<int64_t> create(const char* name, void* image_ptr, int priority);
/**
* @brief Invoke the scheduler to decide which task to switch to next.
*
* @return The old thread.
*/
void schedule();
/**
* @brief Switches the current task to the `other` task. The user address
* space in `TTBR0` is also switched by this function, and the task address
* space is made visible.
*
* @param other
* @return The old thread.
*/
Thread* switch_to(Thread* other);
/**
* @brief Get the thread pointer by tid.
*
* @param tid
* @return Thread of the given tid if found.
*/
Result<Thread*> get_thread_by_tid(int64_t tid);
/**
* @brief Sample the idle thread's time vs. non-idle runtime
*/
void sample_idle_stats();
/**
* @brief Get the pointer of the current thread.
*
* @return Current thread.
*/
static inline Thread* cur_thread() {
extern Thread* __current_thread;
return __current_thread;
}
/**
* @brief Saves the exception frame context into the per-task structure.
*
* @param frame The frame to save.
*/
static inline void save_thread_state(platform::exception::Frame* frame) {
cxx::memcpy(&(cur_thread()->saved_context), frame, sizeof(platform::exception::Frame));
}
/**
* @brief Restores the per-task frame context into the exception frame.
*
* @param where The the per-task frame should be restored into.
*/
static inline void restore_thread_state(platform::exception::Frame* where) {
cxx::memcpy(where, &(cur_thread()->saved_context), sizeof(platform::exception::Frame));
}
} // namespace core::thread