Feedback
Feedback

If you are having issues with the exercises, please create a ticket on DevZone: devzone.nordicsemi.com
Click or drag files to this area to upload. You can upload up to 2 files.

Thread life cycle

A thread is the basic unit of runnable code. The vast majority of firmware code will run in threads, whether it’s a user-defined thread, a thread created by the RTOS (for example, a system workqueue thread), or a thread created by an RTOS subsystem (for example, a logger module), or a thread created by a library (for example, AT Monitor library). A thread has the following items:

  • Thread control block: This is of type k_thread. For each thread, there will be an instance of a thread control block within the RTOS that keeps track of a thread’s information, specifically its metadata.
  • Stack: Each thread will have its own stack. The stack area’s size must be set to align with the specific processing requirements of the thread. The next section will cover how to set the right thread size.
  • Entry point function: This is the body of the thread or, in other words, the functionality implemented by it. It usually contains an infinite loop, as exiting the entry point will terminate the thread. The entry point function can have three optional argument values that can be passed to it on start.
  • Thread priority: The priority is just a signed integer that governs the “type” of the thread. It instructs the scheduler how to allocate CPU time to the thread. We will dive into this in the next topic, Scheduler in-depth.
  • Optional thread options: As covered in the nRF Connect SDK Fundamentals course – Lesson 7, by using this optional field, you can make the thread receive special treatment under specific circumstances.
  • Optional starting delay: You can instruct the kernel to immediately place the created thread in the queue of ready threads (ready queue) by passing K_NO_WAIT which is simply a start delay of 0. Or we can specify an optional start delay.

Threads are created using either the K_THREAD_DEFINE() macro or the k_thread_create() function. In both cases, a stack needs to be allocated statically (dynamic threads are not supported in Zephyr RTOS as of V3.4.0). The K_THREAD_DEFINE() macro manages the stack allocation itself, and the desired stack size is passed as a parameter to the same macro that was covered in the nRF Connect SDK Fundamentals course. Conversely, if you use the k_thread_create() function, you must allocate a stack using the K_THREAD_STACK_DEFINE() macro in advance.

As was covered in the fundamentals course, when creating a thread, you can start it immediately or after specifying a certain delay. Once the thread is started, it is placed in the queue of ready threads (ready queue).

Note

There is also the option to create a thread with the delay set to K_FOREVER, which effectively makes the thread inactive. To activate, call k_thread_start(), which will add the thread to the queue of ready threads (ready queue).

Definition

The ready queue is the queue of threads that has the state Ready. The scheduler only cares about the threads in the ready queue when deciding which one should be the current Running thread.

If the scheduler picks up the thread for execution, its state transitions to Running. Scheduling policies are covered in the next topic, Scheduling in-depth. The thread will stay Running until:

  • The thread changes to an Unready state, meaning it has either the Sleeping, Suspended, or Waiting state.
    • Sleeping: The thread decides to sleep for some time by calling k_sleep() or its derivatives.
    • Suspended: Another thread suspends the thread by calling k_thread_suspend().
    • Waiting: The thread waits for a kernel object (for example, a mutex or a semaphore) that is unavailable.
  • The thread yields on its own or is preempted by the scheduler.
    • The thread yields by calling k_yield() to give up the CPU, and putting itself at the end of the ready queue.
    • The thread is preempted by the scheduler in a rescheduling point when there is a higher priority thread in the ready queue. When preempted, the thread is placed at the end of the ready queue.

Terminate or abort

  • The thread execution ends by termination or aborting.
    • A termination takes place when the entry point function of the thread is exited. This happens in a few situations where a thread has defined non-repetitive tasks, and it’s done with those tasks.
    • Aborting can happen automatically if the thread encounters a fatal error condition, such as de-referencing a null pointer, in which case the RTOS aborts the thread. Alternatively, a thread can be deliberately aborted by another thread or by itself using the k_thread_abort() function.
Thread lift cycle Zephyr

More details on threads can be found on the Threads page of the Zephyr Project documentation.

Register an account
Already have an account? Log in
(All fields are required unless specified optional)

  • 8 or more characters
  • Upper and lower case letters
  • At least one number or special character

Forgot your password?
Enter the email associated with your account, and we will send you a link to reset your password.