MOS Source Code
Loading...
Searching...
No Matches
thread.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-3.0-or-later
2
3#include "mos/assert.hpp"
5#include "mos/mm/mm.hpp"
6
7#include <errno.h>
8#include <limits.h>
12#include <mos/mm/cow.hpp>
15#include <mos/syslog/printk.hpp>
16#include <mos/tasks/process.hpp>
19#include <mos/tasks/thread.hpp>
20#include <mos/tasks/wait.hpp>
21#include <mos_stdlib.hpp>
22#include <mos_string.hpp>
23
25
26static tid_t new_thread_id(void)
27{
28 static tid_t next = 1;
29 return (tid_t) { next++ };
30}
31
33{
34 pr_emerg("thread %p destroyed", this);
35}
36
38{
39 const auto t = mos::create<Thread>();
40 t->magic = THREAD_MAGIC_THRD;
41 t->tid = new_thread_id();
42 t->owner = owner;
43 t->state = THREAD_STATE_CREATED;
44 t->mode = tflags;
45 waitlist_init(&t->waiters);
46 linked_list_init(&t->signal_info.pending);
48 owner->thread_list.push_back(t);
49 return t;
50}
51
53{
54 MOS_ASSERT_X(thread != current_thread, "you cannot just destroy yourself");
55 if (!thread_is_valid(thread))
56 return;
57
58 thread_table.remove(thread->tid);
59
60 pr_dinfo2(thread, "destroying thread %pt", thread);
61 MOS_ASSERT_X(spinlock_is_locked(&thread->state_lock), "thread state lock must be held");
62 MOS_ASSERT_X(thread->state == THREAD_STATE_DEAD, "thread must be dead for destroy");
63
65
66 if (thread->mode == THREAD_MODE_USER)
67 {
68 const auto owner = thread->owner;
69 SpinLocker lock(&owner->mm->mm_lock);
70 vmap_t *const stack = vmap_obtain(owner->mm, (ptr_t) thread->u_stack.top - 1, NULL);
71 vmap_destroy(stack);
72 }
73
75}
76
77PtrResult<Thread> thread_new(Process *owner, thread_mode tmode, mos::string_view name, size_t stack_size, void *explicit_stack_top)
78{
79 const auto t = thread_allocate(owner, tmode);
80
81 t->name = name;
82
83 pr_dinfo2(thread, "creating new thread %pt, owner=%pp", t, owner);
84
85 // Kernel stack
87 stack_init(&t->k_stack, (void *) kstack_blk, MOS_STACK_PAGES_KERNEL * MOS_PAGE_SIZE);
88
89 if (tmode != THREAD_MODE_USER)
90 {
91 stack_init(&t->u_stack, NULL, 0); // kernel thread has no user stack
92 return t;
93 }
94
95 // User stack
96 const size_t user_stack_size = stack_size ? stack_size : MOS_STACK_PAGES_USER * MOS_PAGE_SIZE;
97 if (!explicit_stack_top)
98 {
99 auto stack_vmap = cow_allocate_zeroed_pages(owner->mm, user_stack_size / MOS_PAGE_SIZE, MOS_ADDR_USER_STACK, VALLOC_DEFAULT, VM_USER_RW);
100 if (stack_vmap.isErr())
101 {
102 pr_emerg("failed to allocate stack for new thread");
103 thread_destroy(std::move(t));
104 return stack_vmap.getErr();
105 }
106
107 stack_init(&t->u_stack, (void *) stack_vmap->vaddr, user_stack_size);
109 return t;
110 }
111
112 // check if the stack is valid
113 mm_lock_ctx_pair(owner->mm, NULL);
114 vmap_t *stack_vmap = vmap_obtain(owner->mm, (ptr_t) explicit_stack_top, NULL);
115 if (!stack_vmap)
116 {
117 pr_warn("invalid stack pointer %pt", explicit_stack_top);
118 goto done_efault;
119 }
120
121 // check if the stack vmap is valid
122 if (stack_vmap->content == VMAP_STACK) // has been claimed by another thread?
123 {
124 pr_warn("stack %pt has been claimed by another thread", explicit_stack_top);
125 goto done_efault;
126 }
127
128 // check if the stack is large enough
129 if (stack_vmap->npages < user_stack_size / MOS_PAGE_SIZE)
130 {
131 pr_warn("stack %pt is too small (size=%zu, required=%zu)", explicit_stack_top, stack_vmap->npages * MOS_PAGE_SIZE, user_stack_size);
132 goto done_efault;
133 }
134
135 // check if the stack is writable
136 if (!(stack_vmap->vmflags & VM_USER_RW))
137 {
138 pr_warn("stack %pt is not writable", explicit_stack_top);
139 goto done_efault;
140 }
141
142 {
143 const ptr_t stack_bottom = ALIGN_UP_TO_PAGE((ptr_t) explicit_stack_top) - user_stack_size;
144 vmap_t *second = vmap_split(stack_vmap, (stack_bottom - stack_vmap->vaddr) / MOS_PAGE_SIZE);
145 spinlock_release(&stack_vmap->lock);
146 stack_vmap = second;
147
148 stack_vmap->content = VMAP_STACK;
149 stack_vmap->type = VMAP_TYPE_PRIVATE;
150 spinlock_release(&stack_vmap->lock);
151 mm_unlock_ctx_pair(owner->mm, NULL);
152 stack_init(&t->u_stack, (void *) stack_bottom, user_stack_size);
153 t->u_stack.head = (ptr_t) explicit_stack_top;
154 return t;
155 }
156
157done_efault:
158 spinlock_release(&stack_vmap->lock);
159 mm_unlock_ctx_pair(owner->mm, NULL);
160 spinlock_acquire(&t->state_lock);
161 thread_destroy(std::move(t));
162 return -EFAULT; // invalid stack pointer
163}
164
166{
167 if (!thread_is_valid(thread))
168 return NULL;
169
170 thread_table.insert(thread->tid, thread);
171 return thread;
172}
173
175{
176 const auto ppthread = thread_table.get(tid);
177 if (ppthread == nullptr)
178 {
179 pr_warn("thread_get(%d) from pid %d (%s) but thread does not exist", tid, current_process->pid, current_process->name.c_str());
180 return NULL;
181 }
182
183 if (thread_is_valid(*ppthread))
184 return *ppthread;
185
186 return NULL;
187}
188
190{
191 auto target = thread_get(tid);
192 if (!target)
193 {
194 pr_warn("wait_for_tid(%d) from pid %d (%s) but thread does not exist", tid, current_process->pid, current_process->name.c_str());
195 return false;
196 }
197
198 if (target->owner != current_process)
199 {
200 pr_warn("wait_for_tid(%d) from process %pp but thread belongs to %pp", tid, current_process, target->owner);
201 return false;
202 }
203
204 bool ok = reschedule_for_waitlist(&target->waiters);
205 MOS_UNUSED(ok); // true: thread is dead, false: thread is already dead at the time of calling
206
207 return true;
208}
209
211{
212 MOS_ASSERT_X(thread_is_valid(t), "thread_handle_exit() called on invalid thread");
213 spinlock_acquire(&t->state_lock);
214 thread_exit_locked(std::move(t));
215}
216
217[[noreturn]] void thread_exit_locked(Thread *&&t)
218{
219 MOS_ASSERT_X(thread_is_valid(t), "thread_exit_locked() called on invalid thread");
220
221 pr_dinfo(thread, "thread %pt is exiting", t);
222
223 MOS_ASSERT_X(spinlock_is_locked(&t->state_lock), "thread state lock must be held");
224
225 t->state = THREAD_STATE_DEAD;
226
227 waitlist_close(&t->waiters);
228 waitlist_wake(&t->waiters, INT_MAX);
229
230 while (true)
231 reschedule();
233}
#define MOS_ASSERT_X(cond, msg,...)
Definition assert.hpp:15
#define MOS_UNREACHABLE()
Definition assert.hpp:11
#define MOS_STACK_PAGES_USER
Definition autoconf.h:29
#define MOS_ADDR_USER_STACK
Definition autoconf.h:1
#define MOS_PAGE_SIZE
Definition autoconf.h:6
#define MOS_STACK_PAGES_KERNEL
Definition autoconf.h:28
void push_back(const T &value)
Definition list.hpp:19
PtrResult< vmap_t > cow_allocate_zeroed_pages(MMContext *handle, size_t npages, ptr_t vaddr, valloc_flags hints, vm_flags flags)
Allocate zero-on-demand pages at a specific address.
Definition cow.cpp:81
MOSAPI void stack_init(downwards_stack_t *stack, void *mem_region_bottom, size_t size)
Definition stack.cpp:8
MOSAPI void linked_list_init(list_node_t *head_node)
Initialise a circular double linked list.
Definition list.cpp:15
#define list_node(element)
Get the ‘list_node’ of a list element. This is exactly the reverse of ‘list_entry’ above.
Definition list.hpp:74
#define va_phyframe(va)
Definition mm.hpp:79
#define phyframe_va(frame)
Definition mm.hpp:80
void mm_lock_ctx_pair(MMContext *ctx1, MMContext *ctx2)
Lock and unlock a pair of MMContext objects.
Definition mm.cpp:88
void vmap_finalise_init(vmap_t *vmap, vmap_content_t content, vmap_type_t type)
Finalize the initialization of a vmap object.
Definition mm.cpp:250
vmap_t * vmap_obtain(MMContext *mmctx, ptr_t vaddr, size_t *out_offset)
Get the vmap object for a virtual address.
Definition mm.cpp:185
vmap_t * vmap_split(vmap_t *vmap, size_t split)
Split a vmap object into two, at the specified offset.
Definition mm.cpp:205
#define mm_free_pages(frame, npages)
Definition mm.hpp:88
void mm_unlock_ctx_pair(MMContext *ctx1, MMContext *ctx2)
Definition mm.cpp:104
phyframe_t * mm_get_free_pages(size_t npages)
Definition mm.cpp:48
void vmap_destroy(vmap_t *vmap)
Destroy a vmap object, and unmmap the region.
Definition mm.cpp:164
@ VMAP_TYPE_PRIVATE
Definition mm.hpp:32
@ VMAP_STACK
Definition mm.hpp:24
@ VALLOC_DEFAULT
Default allocation flags.
Definition paging.hpp:21
#define THREAD_MAGIC_THRD
thread_mode
@ THREAD_MODE_USER
void define_syscall thread_exit(void)
Definition ksyscall.cpp:175
#define ALIGN_UP_TO_PAGE(addr)
Definition mos_global.h:76
#define MOS_UNUSED(x)
Definition mos_global.h:65
basic_string_view< char > string_view
T * create(Args &&...args)
Definition allocator.hpp:10
#define NULL
Definition pb_syshdr.h:46
#define current_thread
Definition platform.hpp:32
@ THREAD_STATE_DEAD
thread is dead, and will be cleaned up soon by the scheduler
Definition platform.hpp:72
@ THREAD_STATE_CREATED
created or forked, but not ever started
Definition platform.hpp:67
@ VM_USER_RW
Definition platform.hpp:57
#define current_process
Definition platform.hpp:33
#define pr_warn(fmt,...)
Definition printk.hpp:38
#define pr_emerg(fmt,...)
Definition printk.hpp:39
#define pr_dinfo(feat, fmt,...)
Definition printk.hpp:28
#define pr_dinfo2(feat, fmt,...)
Definition printk.hpp:27
void platform_context_cleanup(Thread *thread)
__nodiscard bool reschedule_for_waitlist(waitlist_t *waitlist)
Definition schedule.cpp:172
void reschedule(void)
reschedule.
Definition schedule.cpp:107
const char * name
Definition slab.cpp:35
should_inline bool spinlock_is_locked(const spinlock_t *lock)
Definition spinlock.hpp:71
#define spinlock_acquire(lock)
Definition spinlock.hpp:64
#define spinlock_release(lock)
Definition spinlock.hpp:65
mos::list< Thread * > thread_list
MMContext * mm
thread_mode mode
user-mode thread or kernel-mode
Process * owner
downwards_stack_t u_stack
user-mode stack
downwards_stack_t k_stack
kernel-mode stack
~Thread()
Definition thread.cpp:32
spinlock_t state_lock
protects the thread state
tid_t tid
thread_state_t state
thread state
Definition mm.hpp:59
vmap_content_t content
Definition mm.hpp:71
ptr_t vaddr
Definition mm.hpp:63
size_t npages
Definition mm.hpp:64
vm_flags vmflags
Definition mm.hpp:65
spinlock_t lock
Definition mm.hpp:61
vmap_type_t type
Definition mm.hpp:72
PtrResult< Thread > thread_new(Process *owner, thread_mode tmode, mos::string_view name, size_t stack_size, void *explicit_stack_top)
Definition thread.cpp:77
void thread_destroy(Thread *thread)
Definition thread.cpp:52
Thread * thread_allocate(Process *owner, thread_mode tflags)
Definition thread.cpp:37
bool thread_wait_for_tid(tid_t tid)
Definition thread.cpp:189
void thread_exit_locked(Thread *&&t)
Definition thread.cpp:217
Thread * thread_get(tid_t tid)
Definition thread.cpp:174
Thread * thread_complete_init(Thread *thread)
Definition thread.cpp:165
static tid_t new_thread_id(void)
Definition thread.cpp:26
should_inline bool thread_is_valid(const Thread *thread)
Definition thread.hpp:13
mos::HashMap< tid_t, Thread * > thread_table
Definition thread.cpp:24
s32 tid_t
Definition types.h:75
unsigned long ptr_t
Definition types.h:21
void waitlist_init(waitlist_t *list)
Definition wait.cpp:15
size_t waitlist_wake(waitlist_t *list, size_t max_wakeups)
Definition wait.cpp:37
void waitlist_close(waitlist_t *list)
Definition wait.cpp:68