kernel/services/threads/workqueue.html · kernel/services/threads/workqueue.rst 공식 원문 ↗

Zephyr 3.7.0 · Kernel · Threads

Workqueue Threads

ISR과 high-priority context의 긴 처리를 전용 worker thread로 넘기고 FIFO로 안전하게 실행합니다.

Official pathkernel/services/threads/workqueue.html
Source filekernel/services/threads/workqueue.rst
Source versionZephyr 3.7.0
TranslationFull · reviewed

본문 기준: Zephyr 3.7.0 LTS3 · 이후 유지보수 패치는 별도입니다.

Part 1

요약·해설

중첩 가능한 item 상태, schedule과 reschedule deadline 정책, drain·plug·cancel·flush 및 실무 race 회피 규칙을 연결합니다.

Lifecycle

queued · running · canceling

Variants

delayable · triggered · user

Safety

flush · cancel · return values

Part 2

접을 수 있는 영어 원문 전체

영어 원문 전체 펼치기
원문 SHA-256 2C905EC3CEA73FA0B4D6CE4E711081B24732DEC859B98E897D7082AA64548895
.. _workqueues_v2:

Workqueue Threads
#################

.. contents::
    :local:
    :depth: 1

A :dfn:`workqueue` is a kernel object that uses a dedicated thread to process
work items in a first in, first out manner. Each work item is processed by
calling the function specified by the work item. A workqueue is typically
used by an ISR or a high-priority thread to offload non-urgent processing
to a lower-priority thread so it does not impact time-sensitive processing.

Any number of workqueues can be defined (limited only by available RAM). Each
workqueue is referenced by its memory address.

A workqueue has the following key properties:

* A **queue** of work items that have been added, but not yet processed.

* A **thread** that processes the work items in the queue. The priority of the
  thread is configurable, allowing it to be either cooperative or preemptive
  as required.

Regardless of workqueue thread priority the workqueue thread will yield
between each submitted work item, to prevent a cooperative workqueue from
starving other threads.

A workqueue must be initialized before it can be used. This sets its queue to
empty and spawns the workqueue's thread.  The thread runs forever, but sleeps
when no work items are available.

.. note::
   The behavior described here is changed from the Zephyr workqueue
   implementation used prior to release 2.6.  Among the changes are:

   * Precise tracking of the status of cancelled work items, so that the
     caller need not be concerned that an item may be processing when the
     cancellation returns.  Checking of return values on cancellation is still
     required.
   * Direct submission of delayable work items to the queue with
     :c:macro:`K_NO_WAIT` rather than always going through the timeout API,
     which could introduce delays.
   * The ability to wait until a work item has completed or a queue has been
     drained.
   * Finer control of behavior when scheduling a delayable work item,
     specifically allowing a previous deadline to remain unchanged when a work
     item is scheduled again.
   * Safe handling of work item resubmission when the item is being processed
     on another workqueue.

   Using the return values of :c:func:`k_work_busy_get()` or
   :c:func:`k_work_is_pending()`, or measurements of remaining time until
   delayable work is scheduled, should be avoided to prevent race conditions
   of the type observed with the previous implementation.  See also `Workqueue
   Best Practices`_.

Work Item Lifecycle
********************

Any number of **work items** can be defined. Each work item is referenced
by its memory address.

A work item is assigned a **handler function**, which is the function
executed by the workqueue's thread when the work item is processed. This
function accepts a single argument, which is the address of the work item
itself.  The work item also maintains information about its status.

A work item must be initialized before it can be used. This records the work
item's handler function and marks it as not pending.

A work item may be **queued** (:c:enumerator:`K_WORK_QUEUED`) by submitting it to a
workqueue by an ISR or a thread.  Submitting a work item appends the work item
to the workqueue's queue.  Once the workqueue's thread has processed all of
the preceding work items in its queue the thread will remove the next work
item from the queue and invoke the work item's handler function. Depending on
the scheduling priority of the workqueue's thread, and the work required by
other items in the queue, a queued work item may be processed quickly or it
may remain in the queue for an extended period of time.

A delayable work item may be **scheduled** (:c:enumerator:`K_WORK_DELAYED`) to a
workqueue; see `Delayable Work`_.

A work item will be **running** (:c:enumerator:`K_WORK_RUNNING`) when it is running
on a work queue, and may also be **canceling** (:c:enumerator:`K_WORK_CANCELING`)
if it started running before a thread has requested that it be cancelled.

A work item can be in multiple states; for example it can be:

* running on a queue;
* marked canceling (because a thread used :c:func:`k_work_cancel_sync()` to
  wait until the work item completed);
* queued to run again on the same queue;
* scheduled to be submitted to a (possibly different) queue

*all simultaneously*.  A work item that is in any of these states is **pending**
(:c:func:`k_work_is_pending()`) or **busy** (:c:func:`k_work_busy_get()`).

A handler function can use any kernel API available to threads. However,
operations that are potentially blocking (e.g. taking a semaphore) must be
used with care, since the workqueue cannot process subsequent work items in
its queue until the handler function finishes executing.

The single argument that is passed to a handler function can be ignored if it
is not required. If the handler function requires additional information about
the work it is to perform, the work item can be embedded in a larger data
structure. The handler function can then use the argument value to compute the
address of the enclosing data structure with :c:macro:`CONTAINER_OF`, and
thereby obtain access to the additional information it needs.

A work item is typically initialized once and then submitted to a specific
workqueue whenever work needs to be performed. If an ISR or a thread attempts
to submit a work item that is already queued the work item is not affected;
the work item remains in its current place in the workqueue's queue, and
the work is only performed once.

A handler function is permitted to re-submit its work item argument
to the workqueue, since the work item is no longer queued at that time.
This allows the handler to execute work in stages, without unduly delaying
the processing of other work items in the workqueue's queue.

.. important::
    A pending work item *must not* be altered until the item has been processed
    by the workqueue thread. This means a work item must not be re-initialized
    while it is busy. Furthermore, any additional information the work item's
    handler function needs to perform its work must not be altered until
    the handler function has finished executing.

.. _k_delayable_work:

Delayable Work
**************

An ISR or a thread may need to schedule a work item that is to be processed
only after a specified period of time, rather than immediately. This can be
done by **scheduling** a **delayable work item** to be submitted to a
workqueue at a future time.

A delayable work item contains a standard work item but adds fields that
record when and where the item should be submitted.

A delayable work item is initialized and scheduled to a workqueue in a similar
manner to a standard work item, although different kernel APIs are used.  When
the schedule request is made the kernel initiates a timeout mechanism that is
triggered after the specified delay has elapsed. Once the timeout occurs the
kernel submits the work item to the specified workqueue, where it remains
queued until it is processed in the standard manner.

Note that work handler used for delayable still receives a pointer to the
underlying non-delayable work structure, which is not publicly accessible from
:c:struct:`k_work_delayable`.  To get access to an object that contains the
delayable work object use this idiom:

.. code-block:: c

   static void work_handler(struct k_work *work)
   {
           struct k_work_delayable *dwork = k_work_delayable_from_work(work);
           struct work_context *ctx = CONTAINER_OF(dwork, struct work_context,
	                                           timed_work);
           ...


Triggered Work
**************

The :c:func:`k_work_poll_submit` interface schedules a triggered work
item in response to a **poll event** (see :ref:`polling_v2`), that will
call a user-defined function when a monitored resource becomes available
or poll signal is raised, or a timeout occurs.
In contrast to :c:func:`k_poll`, the triggered work does not require
a dedicated thread waiting or actively polling for a poll event.

A triggered work item is a standard work item that has the following
added properties:

* A pointer to an array of poll events that will trigger work item
  submissions to the workqueue

* A size of the array containing poll events.

A triggered work item is initialized and submitted to a workqueue in a similar
manner to a standard work item, although dedicated kernel APIs are used.
When a submit request is made, the kernel begins observing kernel objects
specified by the poll events. Once at least one of the observed kernel
object's changes state, the work item is submitted to the specified workqueue,
where it remains queued until it is processed in the standard manner.

.. important::
    The triggered work item as well as the referenced array of poll events
    have to be valid and cannot be modified for a complete triggered work
    item lifecycle, from submission to work item execution or cancellation.

An ISR or a thread may **cancel** a triggered work item it has submitted
as long as it is still waiting for a poll event. In such case, the kernel
stops waiting for attached poll events and the specified work is not executed.
Otherwise the cancellation cannot be performed.

System Workqueue
*****************

The kernel defines a workqueue known as the *system workqueue*, which is
available to any application or kernel code that requires workqueue support.
The system workqueue is optional, and only exists if the application makes
use of it.

.. important::
    Additional workqueues should only be defined when it is not possible
    to submit new work items to the system workqueue, since each new workqueue
    incurs a significant cost in memory footprint. A new workqueue can be
    justified if it is not possible for its work items to co-exist with
    existing system workqueue work items without an unacceptable impact;
    for example, if the new work items perform blocking operations that
    would delay other system workqueue processing to an unacceptable degree.

How to Use Workqueues
*********************

Defining and Controlling a Workqueue
====================================

A workqueue is defined using a variable of type :c:struct:`k_work_q`.
The workqueue is initialized by defining the stack area used by its
thread, initializing the :c:struct:`k_work_q`, either zeroing its
memory or calling :c:func:`k_work_queue_init`, and then calling
:c:func:`k_work_queue_start`. The stack area must be defined using
:c:macro:`K_THREAD_STACK_DEFINE` to ensure it is properly set up in
memory.

The following code defines and initializes a workqueue:

.. code-block:: c

    #define MY_STACK_SIZE 512
    #define MY_PRIORITY 5

    K_THREAD_STACK_DEFINE(my_stack_area, MY_STACK_SIZE);

    struct k_work_q my_work_q;

    k_work_queue_init(&my_work_q);

    k_work_queue_start(&my_work_q, my_stack_area,
                       K_THREAD_STACK_SIZEOF(my_stack_area), MY_PRIORITY,
		       NULL);

In addition the queue identity and certain behavior related to thread
rescheduling can be controlled by the optional final parameter; see
:c:func:`k_work_queue_start()` for details.

The following API can be used to interact with a workqueue:

* :c:func:`k_work_queue_drain()` can be used to block the caller until the
  work queue has no items left.  Work items resubmitted from the workqueue
  thread are accepted while a queue is draining, but work items from any other
  thread or ISR are rejected.  The restriction on submitting more work can be
  extended past the completion of the drain operation in order to allow the
  blocking thread to perform additional work while the queue is "plugged".
  Note that draining a queue has no effect on scheduling or processing
  delayable items, but if the queue is plugged and the deadline expires the
  item will silently fail to be submitted.
* :c:func:`k_work_queue_unplug()` removes any previous block on submission to
  the queue due to a previous drain operation.

Submitting a Work Item
======================

A work item is defined using a variable of type :c:struct:`k_work`.  It must
be initialized by calling :c:func:`k_work_init`, unless it is defined using
:c:macro:`K_WORK_DEFINE` in which case initialization is performed at
compile-time.

An initialized work item can be submitted to the system workqueue by
calling :c:func:`k_work_submit`, or to a specified workqueue by
calling :c:func:`k_work_submit_to_queue`.

The following code demonstrates how an ISR can offload the printing
of error messages to the system workqueue. Note that if the ISR attempts
to resubmit the work item while it is still queued, the work item is left
unchanged and the associated error message will not be printed.

.. code-block:: c

    struct device_info {
        struct k_work work;
        char name[16]
    } my_device;

    void my_isr(void *arg)
    {
        ...
        if (error detected) {
            k_work_submit(&my_device.work);
	}
	...
    }

    void print_error(struct k_work *item)
    {
        struct device_info *the_device =
            CONTAINER_OF(item, struct device_info, work);
        printk("Got error on device %s\n", the_device->name);
    }

    /* initialize name info for a device */
    strcpy(my_device.name, "FOO_dev");

    /* initialize work item for printing device's error messages */
    k_work_init(&my_device.work, print_error);

    /* install my_isr() as interrupt handler for the device (not shown) */
    ...


The following API can be used to check the status of or synchronize with the
work item:

* :c:func:`k_work_busy_get()` returns a snapshot of flags indicating work item
  state.  A zero value indicates the work is not scheduled, submitted, being
  executed, or otherwise still being referenced by the workqueue
  infrastructure.
* :c:func:`k_work_is_pending()` is a helper that indicates ``true`` if and only
  if the work is scheduled, queued, or running.
* :c:func:`k_work_flush()` may be invoked from threads to block until the work
  item has completed.  It returns immediately if the work is not pending.
* :c:func:`k_work_cancel()` attempts to prevent the work item from being
  executed.  This may or may not be successful. This is safe to invoke
  from ISRs.
* :c:func:`k_work_cancel_sync()` may be invoked from threads to block until
  the work completes; it will return immediately if the cancellation was
  successful or not necessary (the work wasn't submitted or running).  This
  can be used after :c:func:`k_work_cancel()` is invoked (from an ISR)` to
  confirm completion of an ISR-initiated cancellation.

Scheduling a Delayable Work Item
================================

A delayable work item is defined using a variable of type
:c:struct:`k_work_delayable`. It must be initialized by calling
:c:func:`k_work_init_delayable`.

For delayed work there are two common use cases, depending on whether a
deadline should be extended if a new event occurs. An example is collecting
data that comes in asynchronously, e.g. characters from a UART associated with
a keyboard.  There are two APIs that submit work after a delay:

* :c:func:`k_work_schedule()` (or :c:func:`k_work_schedule_for_queue()`)
  schedules work to be executed at a specific time or after a delay.  Further
  attempts to schedule the same item with this API before the delay completes
  will not change the time at which the item will be submitted to its queue.
  Use this if the policy is to keep collecting data until a specified delay
  since the **first** unprocessed data was received;
* :c:func:`k_work_reschedule()` (or :c:func:`k_work_reschedule_for_queue()`)
  unconditionally sets the deadline for the work, replacing any previous
  incomplete delay and changing the destination queue if necessary.  Use this
  if the policy is to keep collecting data until a specified delay since the
  **last** unprocessed data was received.

If the work item is not scheduled both APIs behave the same.  If
:c:macro:`K_NO_WAIT` is specified as the delay the behavior is as if the item
was immediately submitted directly to the target queue, without waiting for a
minimal timeout (unless :c:func:`k_work_schedule()` is used and a previous
delay has not completed).

Both also have variants that allow
control of the queue used for submission.

The helper function :c:func:`k_work_delayable_from_work()` can be used to get
a pointer to the containing :c:struct:`k_work_delayable` from a pointer to
:c:struct:`k_work` that is passed to a work handler function.

The following additional API can be used to check the status of or synchronize
with the work item:

* :c:func:`k_work_delayable_busy_get()` is the analog to :c:func:`k_work_busy_get()`
  for delayable work.
* :c:func:`k_work_delayable_is_pending()` is the analog to
  :c:func:`k_work_is_pending()` for delayable work.
* :c:func:`k_work_flush_delayable()` is the analog to :c:func:`k_work_flush()`
  for delayable work.
* :c:func:`k_work_cancel_delayable()` is the analog to
  :c:func:`k_work_cancel()` for delayable work; similarly with
  :c:func:`k_work_cancel_delayable_sync()`.

Synchronizing with Work Items
=============================

While the state of both regular and delayable work items can be determined
from any context using :c:func:`k_work_busy_get()` and
:c:func:`k_work_delayable_busy_get()` some use cases require synchronizing
with work items after they've been submitted.  :c:func:`k_work_flush()`,
:c:func:`k_work_cancel_sync()`, and :c:func:`k_work_cancel_delayable_sync()`
can be invoked from thread context to wait until the requested state has been
reached.

These APIs must be provided with a :c:struct:`k_work_sync` object that has no
application-inspectable components but is needed to provide the
synchronization objects.  These objects should not be allocated on a stack if
the code is expected to work on architectures with
:kconfig:option:`CONFIG_KERNEL_COHERENCE`.

Workqueue Best Practices
************************

Avoid Race Conditions
=====================

Sometimes the data a work item must process is naturally thread-safe, for
example when it's put into a :c:struct:`k_queue` by some thread and processed
in the work thread. More often external synchronization is required to avoid
data races: cases where the work thread might inspect or manipulate shared
state that's being accessed by another thread or interrupt.  Such state might
be a flag indicating that work needs to be done, or a shared object that is
filled by an ISR or thread and read by the work handler.

For simple flags :ref:`atomic_v2` may be sufficient.  In other cases spin
locks (:c:struct:`k_spinlock`) or thread-aware locks (:c:struct:`k_sem`,
:c:struct:`k_mutex` , ...) may be used to ensure data races don't occur.

If the selected lock mechanism can :ref:`api_term_sleep` then allowing the
work thread to sleep will starve other work queue items, which may need to
make progress in order to get the lock released. Work handlers should try to
take the lock with its no-wait path. For example:

.. code-block:: c

   static void work_handler(struct work *work)
   {
           struct work_context *parent = CONTAINER_OF(work, struct work_context,
	                                              work_item);

           if (k_mutex_lock(&parent->lock, K_NO_WAIT) != 0) {
                   /* NB: Submit will fail if the work item is being cancelled. */
                   (void)k_work_submit(work);
		   return;
	   }

	   /* do stuff under lock */
	   k_mutex_unlock(&parent->lock);
	   /* do stuff without lock */
   }

Be aware that if the lock is held by a thread with a lower priority than the
work queue the resubmission may starve the thread that would release the lock,
causing the application to fail.  Where the idiom above is required a
delayable work item is preferred, and the work should be (re-)scheduled with a
non-zero delay to allow the thread holding the lock to make progress.

Note that submitting from the work handler can fail if the work item had been
cancelled.  Generally this is acceptable, since the cancellation will complete
once the handler finishes.  If it is not, the code above must take other steps
to notify the application that the work could not be performed.

Work items in isolation are self-locking, so you don't need to hold an
external lock just to submit or schedule them. Even if you use external state
protected by such a lock to prevent further resubmission, it's safe to do the
resubmit as long as you're sure that eventually the item will take its lock
and check that state to determine whether it should do anything.  Where a
delayable work item is being rescheduled in its handler due to inability to
take the lock some other self-locking state, such as an atomic flag set by the
application/driver when the cancel is initiated, would be required to detect
the cancellation and avoid the cancelled work item being submitted again after
the deadline.

Check Return Values
===================

All work API functions return status of the underlying operation, and in many
cases it is important to verify that the intended result was obtained.

* Submitting a work item (:c:func:`k_work_submit_to_queue`) can fail if the
  work is being cancelled or the queue is not accepting new items.  If this
  happens the work will not be executed, which could cause a subsystem that is
  animated by work handler activity to become non-responsive.
* Asynchronous cancellation (:c:func:`k_work_cancel` or
  :c:func:`k_work_cancel_delayable`) can complete while the work item is still
  being run by a handler.  Proceeding to manipulate state shared with the work
  handler will result in data races that can cause failures.

Many race conditions have been present in Zephyr code because the results of
an operation were not checked.

There may be good reason to believe that a return value indicating that the
operation did not complete as expected is not a problem.  In those cases the
code should clearly document this, by (1) casting the return value to ``void``
to indicate that the result is intentionally ignored, and (2) documenting what
happens in the unexpected case.  For example:

.. code-block:: c

   /* If this fails, the work handler will check pub->active and
    * exit without transmitting.
    */
   (void)k_work_cancel_delayable(&pub->timer);

However in such a case the following code must still avoid data races, as it
cannot guarantee that the work thread is not accessing work-related state.

Don't Optimize Prematurely
==========================

The workqueue API is designed to be safe when invoked from multiple threads
and interrupts. Attempts to externally inspect a work item's state and make
decisions based on the result are likely to create new problems.

So when new work comes in, just submit it. Don't attempt to "optimize" by
checking whether the work item is already submitted by inspecting snapshot
state with :c:func:`k_work_is_pending` or :c:func:`k_work_busy_get`, or
checking for a non-zero delay from
:c:func:`k_work_delayable_remaining_get()`. Those checks are fragile: a "busy"
indication can be obsolete by the time the test is returned, and a "not-busy"
indication can also be wrong if work is submitted from multiple contexts, or
(for delayable work) if the deadline has completed but the work is still in
queued or running state.

A general best practice is to always maintain in shared state some condition
that can be checked by the handler to confirm whether there is work to be
done.  This way you can use the work handler as the standard cleanup path:
rather than having to deal with cancellation and cleanup at points where items
are submitted, you may be able to have everything done in the work handler
itself.

A rare case where you could safely use :c:func:`k_work_is_pending` is as a
check to avoid invoking :c:func:`k_work_flush` or
:c:func:`k_work_cancel_sync`, if you are *certain* that nothing else might
submit the work while you're checking (generally because you're holding a lock
that prevents access to state used for submission).

Suggested Uses
**************

Use the system workqueue to defer complex interrupt-related processing from an
ISR to a shared thread. This allows the interrupt-related processing to be
done promptly without compromising the system's ability to respond to
subsequent interrupts, and does not require the application to define and
manage an additional thread to do the processing.

Configuration Options
**********************

Related configuration options:

* :kconfig:option:`CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE`
* :kconfig:option:`CONFIG_SYSTEM_WORKQUEUE_PRIORITY`
* :kconfig:option:`CONFIG_SYSTEM_WORKQUEUE_NO_YIELD`

API Reference
**************

.. doxygengroup:: workqueue_apis

Part 3

한국어 전문 번역

Workqueue는 전용 thread가 work item을 FIFO 순서로 처리하는 kernel object입니다. 각 item에 지정된 handler function을 호출해 처리합니다. ISR이나 high-priority thread가 급하지 않은 처리를 lower-priority thread로 넘겨 time-sensitive processing에 미치는 영향을 줄일 때 주로 사용합니다.

RAM이 허용하는 만큼 workqueue를 정의하고 memory address로 참조합니다. 핵심 구성은 아직 처리되지 않은 item queue와 이를 처리하는 configurable-priority thread입니다. Cooperative queue도 다른 thread를 굶기지 않도록 기본적으로 item 하나를 처리할 때마다 yield합니다.

Workqueue는 사용 전에 초기화해야 합니다. Queue를 비우고 worker thread를 spawn하며, 이 thread는 영구 실행하되 item이 없을 때 sleep합니다.

Work item lifecycle

각 work item은 address로 참조하며 handler function과 status 정보를 가집니다. 사용 전에 handler를 기록하고 not-pending 상태로 초기화합니다.

ISR 또는 thread가 item을 제출하면 K_WORK_QUEUED가 되고 queue 끝에 붙습니다. 앞선 item 처리가 끝나면 worker가 이를 제거하고 handler를 호출합니다. Queue priority와 앞선 work 양에 따라 빨리 처리되거나 오래 머물 수 있습니다.

Delayable item은 K_WORK_DELAYED로 schedule될 수 있습니다. Handler가 실행 중이면 K_WORK_RUNNING이며, 이미 실행을 시작한 뒤 thread가 synchronized cancellation을 요청하면 K_WORK_CANCELING도 함께 표시될 수 있습니다.

Work item의 중첩 가능한 상태
DELAYED

미래 queue 제출을 기다림

QUEUED

queue에 있지만 아직 시작 전

RUNNING

handler 실행 중

CANCELING

동기 취소 완료를 기다림

FLUSHING

동기 flush 진행 중

여러 상태가 동시에 성립할 수 있으며 0 mask만 idle입니다.

Handler는 thread에서 가능한 kernel API를 쓸 수 있지만 blocking operation은 queue의 뒤 item을 모두 막으므로 주의해야 합니다. 추가 data가 필요하면 k_work를 더 큰 structure에 embed하고 CONTAINER_OF로 enclosing object를 찾습니다.

보통 item은 한 번 초기화하고 필요할 때마다 같은 queue에 제출합니다. 이미 queued인 item을 다시 제출하면 위치도 횟수도 바뀌지 않아 한 번만 실행됩니다. 반면 handler가 실행 중에는 item이 queue에서 빠진 상태라 handler 자체가 다시 제출해 work를 단계로 나눌 수 있습니다.

Delayable work

ISR 또는 thread가 즉시가 아니라 일정 시간 뒤 처리하려면 delayable item을 schedule합니다. Standard work 필드에 언제 어느 queue로 제출할지 기록하는 필드가 추가됩니다. Delay가 끝나면 kernel timeout이 item을 target queue에 제출하고 이후에는 일반 item처럼 처리합니다.

Delayable handler도 public k_work_delayable이 아니라 underlying k_work pointer를 받습니다. Container에 접근하려면 다음 idiom으로 parent delayable object를 먼저 얻습니다.

static void work_handler(struct k_work *work)
{
        struct k_work_delayable *dwork = k_work_delayable_from_work(work);
        struct work_context *ctx = CONTAINER_OF(dwork, struct work_context,
                                                timed_work);
        ...

Triggered work

k_work_poll_submit interface는 poll event의 resource가 available해지거나 signal이 올라오거나 timeout될 때 triggered item을 제출합니다. Dedicated thread가 k_poll로 기다리거나 active polling할 필요가 없습니다.

Triggered item에는 trigger가 될 poll event array pointer와 array size가 추가됩니다. Submit하면 kernel이 event가 참조하는 object를 관찰하고, 하나라도 state를 바꾸면 target queue에 item을 제출합니다.

아직 poll event를 기다리는 동안에는 ISR 또는 thread가 취소할 수 있습니다. 이미 event가 trigger했다면 취소할 수 없습니다.

System workqueue

Kernel은 application과 kernel code가 함께 쓸 수 있는 system workqueue를 제공합니다. 이 queue는 optional이며 application이 실제로 사용할 때만 존재합니다.

Workqueue 사용법

Queue 정의와 제어

k_work_q variable과 K_THREAD_STACK_DEFINE stack을 준비하고 memory를 zeroing하거나 k_work_queue_init()을 호출한 뒤 k_work_queue_start()로 시작합니다.

#define MY_STACK_SIZE 512
#define MY_PRIORITY 5

K_THREAD_STACK_DEFINE(my_stack_area, MY_STACK_SIZE);

struct k_work_q my_work_q;

k_work_queue_init(&my_work_q);

k_work_queue_start(&my_work_q, my_stack_area,
                   K_THREAD_STACK_SIZEOF(my_stack_area), MY_PRIORITY,
                   NULL);

Start의 마지막 cfg parameter로 queue name과 item 사이 yield 동작 등을 제어할 수 있습니다. k_work_queue_drain()은 queue가 빌 때까지 caller를 block하고, queue thread 자체의 resubmission만 허용합니다. plug를 유지하면 caller가 후속 작업을 하는 동안에도 외부 submit을 차단합니다. Delayable item schedule에는 영향이 없으며 plugged 상태에서 deadline이 끝나면 제출은 조용히 실패합니다. k_work_queue_unplug()이 이 차단을 제거합니다.

일반 work item 제출

k_work variable을 k_work_init()으로 초기화하거나 K_WORK_DEFINE으로 compile-time 정의합니다. k_work_submit()은 system queue, k_work_submit_to_queue()는 지정 queue에 제출합니다.

다음 예제는 ISR의 error message 출력을 system workqueue로 넘깁니다. 이미 queued인 동안 ISR이 다시 제출하면 상태가 바뀌지 않아 해당 추가 message는 출력되지 않습니다.

struct device_info {
    struct k_work work;
    char name[16]
} my_device;

void my_isr(void *arg)
{
    ...
    if (error detected) {
        k_work_submit(&my_device.work);
    }
    ...
}

void print_error(struct k_work *item)
{
    struct device_info *the_device =
        CONTAINER_OF(item, struct device_info, work);
    printk("Got error on device %s\n", the_device->name);
}

/* initialize name info for a device */
strcpy(my_device.name, "FOO_dev");

/* initialize work item for printing device's error messages */
k_work_init(&my_device.work, print_error);

/* install my_isr() as interrupt handler for the device (not shown) */
...

상태와 동기화 API에는 busy flag snapshot, pending helper, 완료까지 기다리는 flush, ISR에서도 가능한 asynchronous cancel, thread에서 완료까지 기다리는 cancel_sync가 있습니다. Async cancellation 반환만으로 handler가 끝났다고 가정하면 안 됩니다.

Delayable item schedule

k_work_schedule()과 queue variant는 item이 아직 schedule되지 않았을 때 deadline을 설정합니다. 같은 item을 다시 schedule해도 기존 deadline을 바꾸지 않으므로 첫 미처리 data부터 일정 시간 모으는 정책에 적합합니다.

k_work_reschedule()과 queue variant는 이전 deadline과 필요하면 destination queue까지 무조건 바꿉니다. 마지막 미처리 data가 들어온 시점부터 일정 시간 더 모으는 debounce 정책에 적합합니다. Item이 schedule되지 않은 상태에서는 두 계열이 같습니다.

K_NO_WAIT이면 최소 timeout을 거치지 않고 target queue에 direct submit합니다. 단, schedule 계열에서 기존 delay가 아직 끝나지 않았다면 기존 schedule을 유지합니다.

Work item과 동기화

busy_get 계열은 어디서나 state snapshot을 볼 수 있지만 flush·cancel_sync 계열은 thread context에서 목표 상태까지 기다립니다. 이 API에 넘기는 k_work_sync는 application이 내부를 검사할 수 없는 synchronization object이며, CONFIG_KERNEL_COHERENCE architecture에서는 stack이 아니라 coherent memory에 두어야 합니다.

Workqueue best practices

Race condition 피하기

k_queue처럼 producer와 consumer API 자체가 thread-safe한 data도 있지만, handler와 다른 thread/interrupt가 공유 flag나 object를 함께 읽고 쓰면 보통 외부 synchronization이 필요합니다. 단순 flag는 atomic, 그 밖에는 spinlock·semaphore·mutex 등을 사용합니다.

Sleep 가능한 lock을 handler가 기다리면 그 queue의 다른 item이 진행하지 못해 lock을 풀 work까지 굶을 수 있습니다. Handler에서는 no-wait path를 시도하고 실패하면 다시 제출하는 방식을 고려합니다.

static void work_handler(struct work *work)
{
        struct work_context *parent = CONTAINER_OF(work, struct work_context,
                                                   work_item);

        if (k_mutex_lock(&parent->lock, K_NO_WAIT) != 0) {
                /* NB: Submit will fail if the work item is being cancelled. */
                (void)k_work_submit(work);
                return;
        }

        /* do stuff under lock */
        k_mutex_unlock(&parent->lock);
        /* do stuff without lock */
}

Handler에서 resubmit은 cancellation 중이면 실패할 수 있습니다. 대개 handler가 끝나면 cancellation도 완료되므로 괜찮지만, 반드시 work가 수행돼야 한다면 실패를 application에 알리는 별도 경로가 필요합니다.

Item 자체의 submit/schedule state는 self-locking이므로 단순 제출을 위해 외부 lock을 잡을 필요는 없습니다. 다만 handler가 lock을 못 얻어 스스로 reschedule하는 구조에서는 application이 cancellation을 시작했음을 atomic flag 같은 self-locking state로 알려 재제출을 막아야 합니다.

반환값 확인

모든 work API는 operation 결과를 반환합니다. Submit은 canceling item이나 draining/plugged queue 때문에 실패할 수 있고, async cancel은 handler 실행이 끝나기 전에 반환할 수 있습니다. 결과를 무시하면 subsystem 정지나 shared-state data race가 생길 수 있습니다.

실패가 문제가 되지 않는다고 확신한다면 반환값을 void로 cast해 의도적 무시임을 표시하고 예상 밖 경우에 어떤 동작을 하는지 주석으로 설명합니다.

/* If this fails, the work handler will check pub->active and
 * exit without transmitting.
 */
(void)k_work_cancel_delayable(&pub->timer);

그래도 뒤 코드에서는 work thread가 shared state에 접근 중일 수 있다는 가정으로 race를 막아야 합니다.

성급한 최적화 금지

Workqueue API는 여러 thread와 interrupt에서 안전하게 호출되도록 설계됐습니다. 새 work가 생기면 그대로 submit하십시오. is_pending, busy_get, remaining_get snapshot을 먼저 보고 제출을 생략하는 최적화는 검사 직후 상태가 바뀌어 잘못된 결정을 만들기 쉽습니다.

Shared state에 실제 할 일이 있는지를 나타내는 condition을 따로 두고 handler가 이를 확인하게 하면 handler를 표준 cleanup path로 사용할 수 있습니다. 다른 context가 submit하지 못하게 하는 lock을 이미 보유한 드문 경우에만 pending snapshot으로 flush/cancel_sync 호출을 피하는 검사가 안전할 수 있습니다.

권장 용도와 설정

System workqueue를 사용해 ISR의 복잡한 처리를 shared thread로 미룹니다. Interrupt 처리 자체는 빠르게 끝내 다음 interrupt에 응답하면서 application이 별도 worker thread를 관리하지 않아도 됩니다.

관련 option은 CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE, CONFIG_SYSTEM_WORKQUEUE_PRIORITY, CONFIG_SYSTEM_WORKQUEUE_NO_YIELD입니다.

API 참고: workqueue_apis

Static definition·handler·state

static K_WORK_DELAYABLE_DEFINE(<dwork>, <work_handler>);
static K_WORK_USER_DEFINE(<work>, <work_handler>);
static K_WORK_DEFINE(<work>, <work_handler>);
   Accessed via k_work_busy_get().  May co-occur with other flags.
   Accessed via k_work_busy_get().  May co-occur with other flags.
K_WORK_DELAYABLE_DEFINE(work, work_handler)

Static delayable work item을 첫 사용 전에 정의·초기화합니다. Runtime에서 k_work_init_delayable()을 쓸 수 있다면 ROM 초기값을 만들고 startup에 복사하는 이 macro보다 runtime 초기화가 ROM을 절약할 수 있습니다.

매개변수

  • work: delayable work item symbol
  • work_handler: 처리할 때마다 호출할 함수
K_WORK_USER_DEFINE(work, work_handler)

Static user work item을 첫 사용 전에 정의·초기화합니다.

매개변수

  • work: user work item symbol
  • work_handler: 처리할 때마다 호출할 함수
K_WORK_DEFINE(work, work_handler)

Static non-delayable workqueue item을 첫 사용 전에 정의·초기화합니다.

매개변수

  • work: work item symbol
  • work_handler: 처리할 때마다 호출할 함수
k_work_handler_t

Workqueue를 실행하는 thread가 호출하는 일반 work item handler type입니다: void (*)(struct k_work *work).

매개변수

  • work: handler를 제공한 work item
k_work_user_handler_t

User workqueue thread가 item을 처리할 때 호출하는 user work handler type입니다: void (*)(struct k_work_user *work).

매개변수

  • work: user work item 주소
[anonymous]

Work item 상태 flag를 묶는 anonymous enum입니다. 각 상태는 동시에 둘 이상 성립할 수 있습니다.

K_WORK_RUNNING

Workqueue thread에서 handler가 실행 중인 상태 flag입니다. k_work_busy_get()으로 확인하며 다른 flag와 함께 나타날 수 있습니다.

K_WORK_CANCELING

Work item cancellation이 진행 중인 상태 flag입니다. 다른 flag와 함께 나타날 수 있습니다.

K_WORK_QUEUED

Queue에 제출됐지만 아직 실행을 시작하지 않은 상태 flag입니다. 다른 flag와 함께 나타날 수 있습니다.

K_WORK_DELAYED

Delayable item이 미래 queue 제출을 기다리도록 schedule된 상태 flag입니다. 다른 flag와 함께 나타날 수 있습니다.

K_WORK_FLUSHING

Synchronized work item의 flush가 진행 중인 상태 flag입니다. 다른 flag와 함께 나타날 수 있습니다.

Core work item API

k_work_init(work, handler)

Non-delayable work structure를 처음 제출하기 전에 초기화합니다. 같은 object에 다시 호출할 필요는 없으며 handler를 바꾸려면 item이 idle일 때만 재초기화해야 합니다. ISR에서도 호출할 수 있습니다.

매개변수

  • work: 초기화할 work structure
  • handler: item이 실행할 handler
k_work_busy_get(work)

현재 busy state flag의 snapshot을 반환합니다. 0이면 item이 idle로 보이지만 결과를 검사하기 전에 상태가 바뀔 수 있으므로 필요하면 lock을 사용합니다. ISR에서도 호출할 수 있습니다.

매개변수

  • work: work item

반환값

  • bit mask: K_WORK_DELAYED·QUEUED·RUNNING·CANCELING·FLUSHING의 조합
k_work_is_pending(work)

k_work_busy_get()이 0이 아닌지 검사해 item이 pending인지 반환합니다. Live snapshot이라 곧바로 오래된 값이 될 수 있습니다.

매개변수

  • work: work item

반환값

  • true: non-idle state
  • false: idle로 관찰됨
k_work_submit_to_queue(queue, work)

Work item을 지정 queue에 제출합니다. queue가 NULL이면 가장 최근 제출 queue를 재사용합니다. 이미 queue에 있으면 위치를 바꾸지 않습니다. ISR에서도 호출할 수 있습니다.

매개변수

  • queue: target queue 또는 최근 queue를 뜻하는 NULL
  • work: work item

반환값

  • 0: 이미 queue에 제출됨
  • 1: 새로 queue에 들어감
  • 2: 실행 중이었고 그 실행 queue에 다시 queue됨
  • -EBUSY: canceling 또는 draining/plugged queue라 거부
  • -EINVAL: NULL queue인데 실행 이력이 없음
  • -ENODEV: queue가 시작되지 않음
k_work_submit(work)

Work item을 system workqueue에 제출하는 k_work_submit_to_queue() wrapper입니다. ISR에서도 호출할 수 있습니다.

매개변수

  • work: work item

반환값

  • int: k_work_submit_to_queue()와 동일
k_work_flush(work, sync)

마지막으로 제출된 instance가 끝날 때까지 기다립니다. 기다리는 중 handler가 chained resubmission할 수도 있으며 idle이면 즉시 false를 반환합니다. Workqueue thread가 자기 queue에서 실행 중인 item에 호출하면 동작이 정의되지 않습니다. Caller priority가 queue thread 진행을 막지 않도록 주의합니다.

매개변수

  • work: work item
  • sync: 호출 종료까지 유지되고 caller와 queue thread가 모두 접근할 opaque synchronization object; CONFIG_KERNEL_COHERENCE에서는 coherent memory에 배치

반환값

  • true: 완료를 기다림
  • false: 이미 idle
k_work_cancel(work)

Pending non-delayable item을 queue에서 제거해 실행을 막으려 시도합니다. 이미 handler가 실행 중이면 계속 실행되지만 cancellation 완료 전 resubmission은 거부됩니다. 0이면 cancellation이 완료된 것입니다. ISR에서도 호출할 수 있습니다.

매개변수

  • work: work item

반환값

  • state mask: 현재 남아 있는 k_work_busy_get() 상태
k_work_cancel_sync(work, sync)

k_work_cancel()과 같지만 cancellation이 끝날 때까지 thread를 block합니다. 반환 뒤에는 cancellation 이후 누군가 다시 submit하지 않는 한 idle입니다. Queue thread가 자기 item에 호출하면 undefined입니다.

매개변수

  • work: work item
  • sync: 호출 동안 독점 사용할 persistent synchronization object

반환값

  • true: pending work를 취소했거나 running handler 완료를 기다림
  • false: 취소할 work가 없었음

Workqueue control API

k_work_queue_init(queue)

Workqueue structure를 처음 시작하기 전에 초기화합니다. 같은 structure에는 한 번이면 충분하며 ISR에서도 호출할 수 있습니다.

매개변수

  • queue: 초기화할 queue
k_work_queue_start(queue, stack, stack_size, prio, cfg)

Workqueue thread를 구성하고 시작합니다. 같은 queue에 다시 호출하면 안 됩니다. Queue는 zeroed/BSS memory 또는 k_work_queue_init()으로 초기화돼 있어야 합니다.

매개변수

  • queue: workqueue
  • stack: worker thread stack
  • stack_size: stack byte size
  • prio: initial thread priority
  • cfg: k_work_queue_config 또는 default용 NULL
k_work_queue_thread_get(queue)

Workqueue를 실행하는 thread ID를 얻습니다. Handler가 사용할 kernel object permission을 queue thread에 부여할 때 필요합니다.

매개변수

  • queue: workqueue

반환값

  • k_tid_t: worker thread
k_work_queue_drain(queue, plug)

Queue가 빌 때까지 기다립니다. Drain 중에는 queue thread 자체의 resubmission만 받고 외부 제출은 막습니다. plug가 true면 drain 뒤에도 k_work_queue_unplug()까지 차단을 유지합니다. Delayable item은 deadline 전에는 queue와 결합되지 않으므로 별도로 취소해야 하며, plugged 상태에서 deadline이 끝나면 제출이 조용히 실패합니다.

매개변수

  • queue: workqueue
  • plug: drain 후에도 제출을 막을지 여부

반환값

  • 1: drain 완료를 기다림
  • 0: 기다릴 필요가 없었음
  • negative: wait가 interrupted 또는 실패
k_work_queue_unplug(queue)

이전 drain의 plug를 해제해 새 제출을 받게 합니다. Drain 완료 전 호출하면 drain이 끝난 직후부터 제출할 수 있습니다. ISR에서도 호출할 수 있습니다.

매개변수

  • queue: workqueue

반환값

  • 0: 해제 성공
  • -EALREADY: plug 상태가 아니었음

Delayable work API

k_work_init_delayable(dwork, handler)

Delayable work를 처음 schedule하기 전에 초기화합니다. Idle일 때 다시 호출해 handler를 바꿀 수 있습니다. ISR에서도 호출할 수 있습니다.

매개변수

  • dwork: delayable item
  • handler: 실행 handler
k_work_delayable_from_work(work)

Handler에 전달된 k_work pointer에서 이를 포함한 k_work_delayable 주소를 계산합니다.

매개변수

  • work: handler가 받은 주소

반환값

  • k_work_delayable *: containing delayable object
k_work_delayable_busy_get(dwork)

Delayable item의 live busy-state snapshot을 반환합니다. 필요하면 lock으로 보호합니다.

매개변수

  • dwork: delayable item

반환값

  • bit mask: DELAYED·QUEUED·RUNNING·CANCELING·FLUSHING; 0이면 idle로 보임
k_work_delayable_is_pending(dwork)

Delayable item이 non-idle인지 검사합니다. 결과는 즉시 바뀔 수 있습니다.

매개변수

  • dwork: delayable item

반환값

  • bool: busy mask가 0이 아니면 true
k_work_delayable_expires_get(dwork)

Schedule된 delayable item이 queue에 제출될 absolute tick을 snapshot으로 반환합니다.

매개변수

  • dwork: delayable item

반환값

  • k_ticks_t: timer expiry tick; schedule되지 않았으면 current tick
k_work_delayable_remaining_get(dwork)

Schedule timer가 만료하기까지 남은 tick을 snapshot으로 반환합니다.

매개변수

  • dwork: delayable item

반환값

  • k_ticks_t: 남은 tick; schedule되지 않았으면 0
k_work_schedule_for_queue(queue, dwork, delay)

Idle delayable item을 지정 delay 뒤 queue에 제출합니다. 이미 scheduled 또는 submitted라면 delay가 K_NO_WAIT여도 아무 변경이 없습니다.

매개변수

  • queue: target queue
  • dwork: delayable item
  • delay: 제출 전 대기 시간; K_NO_WAIT면 pending이 아닐 때 direct submit

반환값

  • 0: 이미 scheduled/submitted
  • 1: schedule됨
  • 2: K_NO_WAIT이며 running item이 실행 queue에 다시 queue됨
  • -EBUSY/-EINVAL/-ENODEV: K_NO_WAIT direct submit의 해당 실패
k_work_schedule(dwork, delay)

System workqueue용 k_work_schedule_for_queue() wrapper입니다. 첫 event 뒤 deadline을 유지해야 할 때 사용합니다.

매개변수

  • dwork: delayable item
  • delay: 제출 전 대기

반환값

  • int: k_work_schedule_for_queue()와 동일
k_work_reschedule_for_queue(queue, dwork, delay)

Item이 idle·submitted·running 어떤 상태든 deadline을 새로 설정하고 필요하면 destination queue도 바꿉니다. 이미 queue에 제출된 instance를 unsubmit하지는 않습니다. K_NO_WAIT이면 이전 scheduled submission을 취소한 뒤 direct submit합니다.

매개변수

  • queue: target queue
  • dwork: delayable item
  • delay: 새 delay

반환값

  • 0: K_NO_WAIT이며 이미 queue에 있음
  • 1: 새로 queue되거나 nonzero delay로 schedule됨
  • 2: running item이 실행 queue에 다시 queue됨
  • -EBUSY/-EINVAL/-ENODEV: direct submit 실패
k_work_reschedule(dwork, delay)

System workqueue용 k_work_reschedule_for_queue() wrapper입니다. 마지막 event부터 delay를 다시 재야 할 때 사용합니다.

매개변수

  • dwork: delayable item
  • delay: 새 delay

반환값

  • int: queue variant와 동일
k_work_flush_delayable(dwork, sync)

Schedule돼 있으면 즉시 queue에 제출한 뒤 k_work_flush()처럼 완료까지 기다립니다. Queue thread가 자기 dwork에 호출하면 undefined입니다.

매개변수

  • dwork: delayable item
  • sync: 호출 동안 유지할 synchronization object; coherence 요구를 준수

반환값

  • true: 완료를 기다림
  • false: 이미 idle
k_work_cancel_delayable(dwork)

Schedule 또는 submit된 delayable work를 비동기로 취소합니다. 반환 시 handler가 아직 running일 수 있으며, cancellation 중 reschedule은 가능하지만 submit은 완료될 때까지 막힙니다.

매개변수

  • dwork: delayable item

반환값

  • state mask: 취소 단계를 수행한 뒤 남은 busy state
k_work_cancel_delayable_sync(dwork, sync)

Delayable work가 idle이 될 때까지 기다리는 동기 취소입니다. Queue thread가 자기 dwork에 호출하면 undefined입니다.

매개변수

  • dwork: delayable item
  • sync: 호출 동안 독점하는 persistent synchronization object

반환값

  • true: work가 idle이 아니어서 취소 또는 완료를 기다림
  • false: 이미 idle

User workqueue API

k_work_user_init(work, handler)

Userspace work item을 첫 사용 전에 초기화합니다.

매개변수

  • work: user work item
  • handler: 처리 handler
k_work_user_is_pending(work)

User work item이 queue에 pending인지 snapshot으로 확인합니다. 결과를 사용할 때까지 유지된다는 보장은 caller가 synchronization으로 마련해야 합니다.

매개변수

  • work: user work item

반환값

  • bool: pending이면 true
k_work_user_submit_to_queue(work_q, work)

User-mode workqueue에 item을 제출합니다. Caller resource pool에서 임시 memory를 할당해 worker가 consume한 뒤 해제합니다. Worker는 item memory에 접근할 수 있어야 하고 caller는 work_q의 queue object permission이 필요합니다.

매개변수

  • work_q: user workqueue
  • work: user work item

반환값

  • 0: 성공
  • -EBUSY: 이미 어떤 queue에 있음
  • -ENOMEM: resource pool allocation 실패
k_work_user_queue_start(work_q, stack, stack_size, prio, name)

User mode에서 호출할 수 있고 worker도 user mode로 실행된다는 점 외에는 k_work_queue_start()와 같습니다. Caller는 work_q의 thread·queue object permission이 필요하고 k_thread_create()의 priority 제약을 따릅니다.

매개변수

  • work_q: user queue
  • stack: K_THREAD_STACK_DEFINE() stack
  • stack_size: 원래 constant 또는 K_THREAD_STACK_SIZEOF()
  • prio: worker priority
  • name: 복사할 optional thread name
k_work_user_queue_thread_get(work_q)

User workqueue를 실행하는 user thread ID를 반환합니다. Handler가 쓸 object permission을 부여할 때 사용합니다.

매개변수

  • work_q: user queue

반환값

  • k_tid_t: user worker thread

Triggered work API

k_work_poll_init(work, handler)

Triggered work item을 첫 사용 전에 초기화합니다.

매개변수

  • work: triggered item
  • handler: 처리 handler
k_work_poll_submit_to_queue(work_q, work, events, num_events, timeout)

관찰 event 하나가 signal되거나 timeout될 때 지정 queue에 work를 제출하도록 internal poller를 시작합니다. 아직 event를 기다리는 기존 submission을 다시 제출하면 새 event list로 reschedule하지만 기존 item·queue와 race가 있으므로 외부 synchronization이 필요합니다. Item과 event array는 handler 실행 또는 취소까지 persistent memory에서 유효하고 수정되지 않아야 합니다.

매개변수

  • work_q: target queue
  • work: triggered item
  • events: trigger event array
  • num_events: array size
  • timeout: event가 없어도 제출할 timeout

반환값

  • 0: event 관찰 시작
  • -EINVAL: item이 처리 중이거나 이미 완료
  • -EADDRINUSE: 다른 queue에 pending
k_work_poll_submit(work, events, num_events, timeout)

System workqueue를 대상으로 하는 triggered submission입니다. Event array와 work item은 처리될 때까지 수정하면 안 됩니다.

매개변수

  • work: triggered item
  • events: event array
  • num_events: event 수
  • timeout: 강제 실행 timeout

반환값

  • 0: 관찰 시작
  • -EINVAL: 처리 중 또는 완료
  • -EADDRINUSE: 다른 queue에 pending
k_work_poll_cancel(work)

아직 어떤 event도 submission을 trigger하지 않았을 때만 triggered work 관찰을 취소합니다.

매개변수

  • work: triggered item

반환값

  • 0: 취소 성공
  • -EINVAL: 이미 처리 중이거나 완료

Structure

k_work

Work를 제출하는 데 사용하는 structure이며 include 경로는 kernel.h입니다.

k_work_delayable

지정 delay 뒤 work를 제출하는 structure이며 include 경로는 kernel.h입니다.

k_work_sync

k_work_flush(), k_work_cancel_sync()와 sibling API가 동기 작업 상태를 보관하도록 caller가 제공하는 opaque structure입니다. 호출 종료까지 유지되고 caller와 queue thread가 모두 접근해야 합니다. CONFIG_KERNEL_COHERENCE에서는 arch_mem_coherent() 요구에 맞는 coherent memory에 배치해야 하며 일반 stack은 대개 coherent하지 않아 runtime assertion이 위반을 검출합니다.

k_work_queue_config

k_work_queue_start()에 전달하는 optional configuration structure입니다. Structure와 그 참조 값은 start 이후 보관되지 않습니다.

name

Workqueue thread에 줄 이름입니다. NULL이면 이름을 지정하지 않습니다.

no_yield

기본 false이면 item 사이에 yield해 cooperative thread까지 starvation되지 않게 합니다. true이면 연속 item을 양보 없이 처리할 수 있습니다.

essential

Workqueue thread를 essential thread로 표시할지 제어합니다.

k_work_q

처리할 work를 보관하고 worker thread가 실행하는 workqueue structure이며 include 경로는 kernel.h입니다.

Source

출처

영어 원문에는 문단, 문서 지시문, 표, 코드, 코드 심벌, 경로를 그대로 보존했습니다.