1use crate::{
24 process::{current_task_clone, get_all_tasks, thread_ops},
25 sync::SpinLock,
26 syscall::error::SyscallError,
27};
28use alloc::{collections::BTreeMap, string::String, sync::Arc, vec::Vec};
29use core::sync::atomic::{AtomicU64, Ordering};
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33enum Op {
34 Root = 0,
35 Create = 1,
36 Join = 2,
37 Exit = 3,
38 Current = 4,
39 Yield = 5,
40 Kill = 6,
41 Stats = 7,
42}
43
44#[derive(Clone)]
46enum OpState {
47 Root,
48 Create {
50 created_tid: Option<u32>,
51 },
52 Join {
53 target_tid: u32,
54 },
55 Static(Op),
56}
57
58fn op_of(state: &OpState) -> Op {
59 match state {
60 OpState::Root => Op::Root,
61 OpState::Create { .. } => Op::Create,
62 OpState::Join { .. } => Op::Join,
63 OpState::Static(op) => *op,
64 }
65}
66
67const THREAD_CREATE_REQ_LEN: usize = 32;
79
80pub struct ThreadsScheme {
81 states: SpinLock<BTreeMap<u64, OpState>>,
82}
83
84impl ThreadsScheme {
85 pub fn new() -> Self {
87 ThreadsScheme {
88 states: SpinLock::new(BTreeMap::new()),
89 }
90 }
91
92 fn alloc_file_id(op: Op) -> u64 {
93 static NEXT_SEQ: AtomicU64 = AtomicU64::new(1);
94 let seq = NEXT_SEQ.fetch_add(1, Ordering::Relaxed);
95 ((op as u64) << 32) | (seq & 0xFFFF_FFFF)
96 }
97
98 fn state_for(&self, file_id: u64) -> Result<(Op, OpState), SyscallError> {
99 let states = self.states.lock();
100 let st = states
101 .get(&file_id)
102 .cloned()
103 .ok_or(SyscallError::BadHandle)?;
104 drop(states);
105 Ok((op_of(&st), st))
106 }
107
108 fn take_state(&self, file_id: u64) -> Result<OpState, SyscallError> {
109 self.states
110 .lock()
111 .remove(&file_id)
112 .ok_or(SyscallError::BadHandle)
113 }
114
115 fn parse_join(path: &str) -> Result<u32, SyscallError> {
120 let tid_str = path.strip_prefix("join/").ok_or(SyscallError::NotFound)?;
121 if tid_str.is_empty() || !tid_str.bytes().all(|b| b.is_ascii_digit()) {
122 return Err(SyscallError::InvalidArgument);
123 }
124 let target_tid: u32 = tid_str.parse().map_err(|_| SyscallError::InvalidArgument)?;
125
126 let cur = current_task_clone().ok_or(SyscallError::Fault)?;
127 if target_tid == cur.tid {
128 return Err(SyscallError::InvalidArgument);
129 }
130 use crate::process::{current_task_id, get_child_task_id_by_tid};
131 let parent_id = current_task_id().ok_or(SyscallError::Fault)?;
132 get_child_task_id_by_tid(parent_id, target_tid).ok_or(SyscallError::NotFound)?;
133 Ok(target_tid)
134 }
135
136 fn handle_kill_open(path: &str) -> Result<(), SyscallError> {
140 let tid_str = path.strip_prefix("kill/").ok_or(SyscallError::NotFound)?;
141 if tid_str.is_empty() || !tid_str.bytes().all(|b| b.is_ascii_digit()) {
142 return Err(SyscallError::InvalidArgument);
143 }
144 let target_tid: u32 = tid_str.parse().map_err(|_| SyscallError::InvalidArgument)?;
145 let cur = current_task_clone().ok_or(SyscallError::Fault)?;
146 thread_ops::kill_thread(cur.tid, target_tid)
147 }
148
149 fn root_listing(&self) -> Result<String, SyscallError> {
151 let cur = current_task_clone().ok_or(SyscallError::Fault)?;
152 let mut out = String::new();
153 if let Some(tasks) = get_all_tasks() {
154 for t in tasks {
155 if t.tgid == cur.tgid {
156 out.push_str(itoa_u32(t.tid).as_str());
157 out.push('\n');
158 }
159 }
160 }
161 Ok(out)
162 }
163
164 fn handle_create_write(&self, buf: &[u8]) -> Result<u32, SyscallError> {
167 if buf.len() != THREAD_CREATE_REQ_LEN {
168 return Err(SyscallError::InvalidArgument);
169 }
170 let req_le = |i: usize| {
171 let mut raw = [0u8; 8];
172 raw.copy_from_slice(&buf[i..i + 8]);
173 u64::from_le_bytes(raw)
174 };
175 let entry = req_le(0);
176 let stack_size = req_le(8);
177 let arg0 = req_le(16);
178 let tls_base = req_le(24);
179
180 let child = thread_ops::create_user_thread_with_kernel_stack(
181 thread_ops::UserEntryContext::ring3(),
182 entry,
183 stack_size,
184 arg0,
185 tls_base,
186 )?;
187 Ok(child.tid)
188 }
189}
190
191impl Default for ThreadsScheme {
192 fn default() -> Self {
193 Self::new()
194 }
195}
196
197fn itoa_u32(mut v: u32) -> String {
199 if v == 0 {
200 return String::from("0");
201 }
202 let mut buf = [0u8; 10];
203 let mut i = buf.len();
204 while v > 0 {
205 i -= 1;
206 buf[i] = b'0' + (v % 10) as u8;
207 v /= 10;
208 }
209 String::from(unsafe { core::str::from_utf8_unchecked(&buf[i..]) })
211}
212
213use crate::vfs::scheme::{
214 finalize_pseudo_stat, DirEntry, FileFlags, FileStat, OpenFlags, OpenResult, Scheme,
215 DEV_THREADFS, DT_DIR, DT_REG,
216};
217
218impl Scheme for ThreadsScheme {
219 fn open(&self, path: &str, _flags: OpenFlags) -> Result<OpenResult, SyscallError> {
220 let path = path.trim_start_matches('/');
222
223 let (op, state) = match path {
224 "" | "/" => (Op::Root, OpState::Root),
225 "create" => (Op::Create, OpState::Create { created_tid: None }),
226 "exit" => (Op::Exit, OpState::Static(Op::Exit)),
227 "current" => (Op::Current, OpState::Static(Op::Current)),
228 "yield" => (Op::Yield, OpState::Static(Op::Yield)),
229 "stats" => (Op::Stats, OpState::Static(Op::Stats)),
230 p if p.starts_with("join/") => {
231 let target = Self::parse_join(p)?;
232 (Op::Join, OpState::Join { target_tid: target })
233 }
234 p if p.starts_with("kill/") => {
235 Self::handle_kill_open(p)?;
236 (Op::Kill, OpState::Static(Op::Kill))
237 }
238 _ => return Err(SyscallError::NotFound),
239 };
240
241 let file_id = Self::alloc_file_id(op);
242 self.states.lock().insert(file_id, state);
243
244 let flags = if op == Op::Root {
245 FileFlags::DIRECTORY
246 } else {
247 FileFlags::empty()
248 };
249 Ok(OpenResult {
250 file_id,
251 size: None,
252 flags,
253 })
254 }
255
256 fn read(&self, file_id: u64, offset: u64, buf: &mut [u8]) -> Result<usize, SyscallError> {
257 if buf.is_empty() {
258 return Ok(0);
259 }
260 let (_, state) = self.state_for(file_id)?;
261
262 match state {
263 OpState::Root => {
264 let content = self.root_listing()?;
265 serve_string(&content, offset, buf)
266 }
267 OpState::Create { created_tid } => {
268 let Some(tid) = created_tid else {
269 return Err(SyscallError::InvalidArgument);
271 };
272 if offset != 0 {
273 return Ok(0);
274 }
275 let bytes = tid.to_le_bytes();
276 let n = bytes.len().min(buf.len());
277 buf[..n].copy_from_slice(&bytes[..n]);
278 Ok(n)
279 }
280 OpState::Join { target_tid } => {
281 let (_tid, status) = thread_ops::join_task(target_tid)?;
283 if offset != 0 {
284 return Ok(0);
285 }
286 let bytes = status.to_le_bytes();
287 let n = bytes.len().min(buf.len());
288 buf[..n].copy_from_slice(&bytes[..n]);
289 Ok(n)
290 }
291 OpState::Static(op) => match op {
292 Op::Current => {
293 let tid = thread_ops::current_thread_tid()?;
294 if offset != 0 {
295 return Ok(0);
296 }
297 let bytes = tid.to_le_bytes();
298 let n = bytes.len().min(buf.len());
299 buf[..n].copy_from_slice(&bytes[..n]);
300 Ok(n)
301 }
302 Op::Yield => {
303 crate::process::yield_task();
304 Ok(0)
305 }
306 Op::Kill => Ok(0),
307 Op::Stats => {
308 let (allocated, active) = thread_ops::kernel_user_stack_stats();
309 let mut content = String::from("allocated ");
310 content.push_str(itoa_u32(allocated as u32).as_str());
311 content.push('\n');
312 content.push_str("active ");
313 content.push_str(itoa_u32(active as u32).as_str());
314 content.push('\n');
315 serve_string(&content, offset, buf)
316 }
317 _ => Err(SyscallError::PermissionDenied),
318 },
319 }
320 }
321
322 fn write(&self, file_id: u64, _offset: u64, buf: &[u8]) -> Result<usize, SyscallError> {
323 let (op, _) = self.state_for(file_id)?;
324 match op {
325 Op::Create => {
326 let tid = self.handle_create_write(buf)?;
327 if let Some(OpState::Create { created_tid }) = self.states.lock().get_mut(&file_id)
328 {
329 *created_tid = Some(tid);
330 } else {
331 return Err(SyscallError::BadHandle);
332 }
333 Ok(THREAD_CREATE_REQ_LEN)
334 }
335 Op::Exit => {
336 if buf.len() < 4 {
337 return Err(SyscallError::InvalidArgument);
338 }
339 let code = i32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]);
340 thread_ops::exit_current_thread(code)
342 }
343 _ => Err(SyscallError::PermissionDenied),
344 }
345 }
346
347 fn close(&self, file_id: u64) -> Result<(), SyscallError> {
348 self.take_state(file_id)?;
349 Ok(())
350 }
351
352 fn stat(&self, file_id: u64) -> Result<FileStat, SyscallError> {
353 let op = (file_id >> 32) as u64;
354 let is_dir = op == Op::Root as u64;
355 let st = if is_dir {
356 FileStat {
357 st_ino: file_id,
358 st_mode: 0o040555,
359 st_nlink: 2,
360 ..FileStat::zeroed()
361 }
362 } else {
363 FileStat {
364 st_ino: file_id,
365 st_mode: 0o100666,
366 st_nlink: 1,
367 ..FileStat::zeroed()
368 }
369 };
370 Ok(finalize_pseudo_stat(st, DEV_THREADFS, 0))
371 }
372
373 fn readdir(&self, _file_id: u64) -> Result<Vec<DirEntry>, SyscallError> {
374 Ok(alloc::vec![
375 DirEntry {
376 ino: Self::alloc_file_id(Op::Create),
377 file_type: DT_REG,
378 name: String::from("create"),
379 },
380 DirEntry {
381 ino: Self::alloc_file_id(Op::Join),
382 file_type: DT_DIR,
383 name: String::from("join"),
384 },
385 DirEntry {
386 ino: Self::alloc_file_id(Op::Exit),
387 file_type: DT_REG,
388 name: String::from("exit"),
389 },
390 DirEntry {
391 ino: Self::alloc_file_id(Op::Current),
392 file_type: DT_REG,
393 name: String::from("current"),
394 },
395 DirEntry {
396 ino: Self::alloc_file_id(Op::Yield),
397 file_type: DT_REG,
398 name: String::from("yield"),
399 },
400 DirEntry {
401 ino: Self::alloc_file_id(Op::Kill),
402 file_type: DT_DIR,
403 name: String::from("kill"),
404 },
405 DirEntry {
406 ino: Self::alloc_file_id(Op::Stats),
407 file_type: DT_REG,
408 name: String::from("stats"),
409 },
410 ])
411 }
412}
413
414fn serve_string(content: &str, offset: u64, buf: &mut [u8]) -> Result<usize, SyscallError> {
416 if offset >= content.len() as u64 {
417 return Ok(0);
418 }
419 let start = offset as usize;
420 let end = core::cmp::min(start + buf.len(), content.len());
421 buf[..end - start].copy_from_slice(&content.as_bytes()[start..end]);
422 Ok(end - start)
423}
424
425pub fn init_threads_scheme() -> Arc<ThreadsScheme> {
427 Arc::new(ThreadsScheme::new())
428}