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
//! RIOT (C) thread implementation
use riot_sys as raw;

use super::{NoSuchThread, StackStats, StackStatsError};
use crate::helpers::PointerToCStr;

/// Offloaded tools for creation
mod creation;
pub use creation::{scope, spawn, CountedThread, CountingThreadScope};

/// Wrapper around a valid (not necessarily running, but in-range) [riot_sys::kernel_pid_t] that
/// provides access to thread details and signaling.
// Possible optimization: Make this NonZero
#[derive(Debug, PartialEq, Copy, Clone)]
pub struct KernelPID(pub(crate) raw::kernel_pid_t);

// Converting the raw constants into consistently typed ones

// pub(crate) const KERNEL_PID_UNDEF: riot_sys::kernel_pid_t = riot_sys::KERNEL_PID_UNDEF as _;
const KERNEL_PID_FIRST: riot_sys::kernel_pid_t = riot_sys::KERNEL_PID_FIRST as _;
const KERNEL_PID_LAST: riot_sys::kernel_pid_t = riot_sys::KERNEL_PID_LAST as _;
pub(crate) const KERNEL_PID_ISR: riot_sys::kernel_pid_t = riot_sys::KERNEL_PID_ISR as _;

// Converting the raw constants into consistently typed ones for use in match branches. If
// that becomes a pattern, it might make sense to introduce a macro that forces a bunch of
// symbols (with different capitalizations) into a given type and makes an enum with a
// from_int method out of it.

// This is special because it is not part of the enum but a cast -1
// unsafe: Side effect free C macros
const STATUS_NOT_FOUND: i32 = unsafe { riot_sys::macro_STATUS_NOT_FOUND() as _ };

const STATUS_STOPPED: i32 = riot_sys::thread_status_t_STATUS_STOPPED as i32;
const STATUS_SLEEPING: i32 = riot_sys::thread_status_t_STATUS_SLEEPING as i32;
const STATUS_MUTEX_BLOCKED: i32 = riot_sys::thread_status_t_STATUS_MUTEX_BLOCKED as i32;
const STATUS_RECEIVE_BLOCKED: i32 = riot_sys::thread_status_t_STATUS_RECEIVE_BLOCKED as i32;
const STATUS_SEND_BLOCKED: i32 = riot_sys::thread_status_t_STATUS_SEND_BLOCKED as i32;
const STATUS_REPLY_BLOCKED: i32 = riot_sys::thread_status_t_STATUS_REPLY_BLOCKED as i32;
const STATUS_FLAG_BLOCKED_ANY: i32 = riot_sys::thread_status_t_STATUS_FLAG_BLOCKED_ANY as i32;
const STATUS_FLAG_BLOCKED_ALL: i32 = riot_sys::thread_status_t_STATUS_FLAG_BLOCKED_ALL as i32;
const STATUS_MBOX_BLOCKED: i32 = riot_sys::thread_status_t_STATUS_MBOX_BLOCKED as i32;
const STATUS_RUNNING: i32 = riot_sys::thread_status_t_STATUS_RUNNING as i32;
const STATUS_PENDING: i32 = riot_sys::thread_status_t_STATUS_PENDING as i32;

#[derive(Debug)]
#[non_exhaustive]
pub enum Status {
    // I would not rely on any properties of the assigned values, but it might make the conversion
    // points easier on the generated code if it can be reasoned down to a simple check of whether
    // it's in range.
    Stopped = STATUS_STOPPED as isize,
    Sleeping = STATUS_SLEEPING as isize,
    MutexBlocked = STATUS_MUTEX_BLOCKED as isize,
    ReceiveBlocked = STATUS_RECEIVE_BLOCKED as isize,
    SendBlocked = STATUS_SEND_BLOCKED as isize,
    ReplyBlocked = STATUS_REPLY_BLOCKED as isize,
    FlagBlockedAny = STATUS_FLAG_BLOCKED_ANY as isize,
    FlagBlockedAll = STATUS_FLAG_BLOCKED_ALL as isize,
    MboxBlocked = STATUS_MBOX_BLOCKED as isize,
    Running = STATUS_RUNNING as isize,
    Pending = STATUS_PENDING as isize,

    /// A status value not known to riot-wrappers. Don't match for this explicitly: Other values
    /// may, at any minor riot-wrappers update, become actual process states again.
    Other, // Not making this Other(i32) as by the time this is reached, the code can't react
           // meaningfully to it, and if that shows up in any debug output, someone will need to
           // reproduce this anyway and can hook into from_int then.
}

impl Status {
    fn from_int(status: i32) -> Self {
        match status {
            STATUS_STOPPED => Status::Stopped,
            STATUS_SLEEPING => Status::Sleeping,
            STATUS_MUTEX_BLOCKED => Status::MutexBlocked,
            STATUS_RECEIVE_BLOCKED => Status::ReceiveBlocked,
            STATUS_SEND_BLOCKED => Status::SendBlocked,
            STATUS_REPLY_BLOCKED => Status::ReplyBlocked,
            STATUS_FLAG_BLOCKED_ANY => Status::FlagBlockedAny,
            STATUS_FLAG_BLOCKED_ALL => Status::FlagBlockedAll,
            STATUS_MBOX_BLOCKED => Status::MboxBlocked,
            STATUS_RUNNING => Status::Running,
            STATUS_PENDING => Status::Pending,
            _ => Status::Other,
        }
    }
}

impl KernelPID {
    pub fn new(pid: raw::kernel_pid_t) -> Option<Self> {
        if unsafe { raw::pid_is_valid(pid) } != 0 {
            Some(KernelPID(pid))
        } else {
            None
        }
    }

    pub fn all_pids() -> impl Iterator<Item = KernelPID> {
        // Not constructing the KernelPID manually but going through new serves as a convenient
        // validation of the construction (all_pids will panic if the rules of pid_is_valid change,
        // and then this function *should* be reevaluated). As pid_is_valid is static inline, the
        // compiler should be able to see through the calls down to there that the bounds checked
        // for there are the very bounds used in the construction here.
        (KERNEL_PID_FIRST..=KERNEL_PID_LAST)
            .map(|i| KernelPID::new(i).expect("Should be valid by construction"))
    }

    pub fn get_name(&self) -> Option<&str> {
        let ptr = unsafe { raw::thread_getname(self.0) };

        // If the thread stops, the name might be not valid any more, but then again the getname
        // function might already have returned anything, and thread names are generally strings in
        // .text. Unwrapping because by the time non-ASCII text shows up in there, something
        // probably already went terribly wrong.
        unsafe { ptr.to_lifetimed_cstr()? }.to_str().ok()
    }

    /// Get the current status of the thread of that number, if one currently exists
    #[doc(alias = "thread_getstatus")]
    pub fn status(&self) -> Result<Status, NoSuchThread> {
        // unsafe: Side effect free, always-callable C function
        let status = unsafe { raw::thread_getstatus(self.0) } as _;
        if status == STATUS_NOT_FOUND {
            Err(NoSuchThread)
        } else {
            Ok(Status::from_int(status))
        }
    }

    #[doc(alias = "thread_wakeup")]
    pub fn wakeup(&self) -> Result<(), NoSuchThread> {
        let success = unsafe { raw::thread_wakeup(self.0) };
        match success {
            1 => Ok(()),
            _ => Err(NoSuchThread),
        }
    }

    /// Pick the thread_t out of sched_threads for the PID
    #[doc(alias = "thread_get")]
    fn thread(&self) -> Result<*const riot_sys::thread_t, NoSuchThread> {
        // unsafe: C function's "checked" precondition met by type constraint on PID validity
        let t = unsafe { riot_sys::thread_get_unchecked(self.0) };
        // .as_ref() would have the null check built in, but we can't build a shared refernce out
        // of this, only ever access its fields with volatility.
        if t == 0 as *mut _ {
            Err(NoSuchThread)
        } else {
            Ok(crate::inline_cast(t))
        }
    }

    pub fn priority(&self) -> Result<u8, NoSuchThread> {
        let thread = self.thread()?;
        Ok(unsafe { (*thread).priority })
    }

    /// Gather information about the stack's thread.
    ///
    /// A None being returned can have two reasons:
    /// * The thread does not exist, or
    /// * develhelp is not active.
    ///
    /// This is not backed by C functions (as most of the rest of this crate is), but rather a
    /// practical way to access struct members that may or may not be present in a build.
    pub fn stack_stats(&self) -> Result<StackStats, StackStatsError> {
        #[cfg(riot_develhelp)]
        {
            let thread = self.thread()?;
            return Ok(StackStats {
                // This cast is relevant because different platforms (eg. native and arm) disagree on
                // whether that's an i8 or u8 pointer. Could have made it c_char, but a) don't want to
                // alter the signatures and b) it's easier to use on the Rust side with a clear type.
                start: unsafe { (*thread).stack_start as _ },
                size: unsafe { (*thread).stack_size as _ },
                free: unsafe { riot_sys::thread_measure_stack_free((*thread).stack_start) }
                    as usize,
            });
        }
        #[cfg(not(riot_develhelp))]
        return Err(StackStatsError::InformationUnavailable);
    }
}

impl Into<raw::kernel_pid_t> for &KernelPID {
    fn into(self) -> raw::kernel_pid_t {
        self.0
    }
}

impl Into<raw::kernel_pid_t> for KernelPID {
    fn into(self) -> raw::kernel_pid_t {
        self.0
    }
}

/// PID of the currently active thread
#[doc(alias = "thread_getpid")]
pub fn get_pid() -> KernelPID {
    // Ignoring the volatile in thread_getpid because it's probably not necessary (any application
    // will only ever see a consistent current PID).
    KernelPID(unsafe { raw::thread_getpid() })
}

/// Put the current thread in the "sleeping" state, only to be continue when something calls
/// [KernelPID::wakeup()] on its PID.
#[doc(alias = "thread_sleep")]
pub fn sleep() {
    unsafe { raw::thread_sleep() }
}