class SchedulerQueueState

Defined at line 763 of file ../../zircon/kernel/include/kernel/scheduler_state.h

SchedulerQueueState tracks the association with a scheduler and run queue. To

accommodate the different ways a thread can be accessed (i.e. starting from a

thread vs. starting from a run queue), specific locking rules apply to this

member of Thread (i.e. Thread::scheduler_queue_state_).

There are two locks that apply to interactions with the queue state:

1. Thread::lock_ (or the thread's lock): Protects most of a thread's data

members, including the scheduling state (i.e. Thread::scheduling_state_).

Thread::scheduling_state_::curr_cpu_ indicates which CPU and scheduler

the thread is currently associated with.

2. Scheduler::queue_lock_ (or the queue lock): Protects a scheduler's data

members, including the run queue and associated bookkeeping. This lock

also protects the thread's scheduler queue state member while the thread

is associated with a particular scheduler.

When both a thread's lock and a scheduler's queue lock must be held at

the same time, commonly needed during rescheduling and PI operations,

the defined lock order is to acquire the thread's lock before the queue

lock. Fortunately, most operations start from a thread and proceed to

interact with a scheduler (e.g. blocking, unblocking, PI operations),

naturally conforming to the required lock order. However, several

operations start from a scheduler (e.g. finding the next thread to run,

stealing a thread from another CPU), and require some lock juggling to

complete their operations.

Operations that start from a scheduler typically dequeue a thread from a

run queue while holding the queue lock protecting that run queue.

Locking the thread to complete the operation involves releasing the

currently held queue lock, acquiring the thread's lock, and then

acquiring either the same queue lock or a different queue lock.

For example, selecting the next thread to run during a reschedule

involves the following lock juggling sequence:

1. With the local queue lock held, dequeue the next thread to run.

2. Release the queue lock.

3. Acquire the next thread's lock.

4. Re-acquire the local queue lock and continue the reschedule.

Stealing a thread from another CPU involves a similar lock juggling

sequence:

1. With the source queue lock held, dequeue the thread to steal and

(mostly) disassociate it with the source scheduler.

2. Release the source queue lock.

3. Acquire the stolen thread's lock.

4. Acquire the local queue lock, associate the thread with the local

scheduler, and continue the reschedule.

In both cases, since the thread is no longer in a queue after step 1,

it cannot be selected or stolen by another CPU after the queue lock is

released. However, because the thread is not yet locked between steps 2 and

3, an operation starting at the thread and holding the thread's lock could

interleave with the locking sequence. If such an interleaved operation

involves updating the thread's run queue position and/or the associated

scheduler's bookkeeping, care must be taken to avoid double-dequeuing the

thread, updating the wrong scheduler's bookkeeping, or rescheduling the

wrong CPU.

SchedulerQueueState::Disposition is an enumeration of the valid combinations

of a thread's scheduler queue states that may be used in conjunction with the

thread state (i.e. Thread::state()) to determine how the thread and the

associated scheduler can be updated.

The following state combinations may be observed by operations that start at

a thread and hold both the thread's lock and the queue lock:

1. state=RUNNING, disposition=Associated:

Observable when a thread is running. Because the currently running

thread's lock is held over a reschedule, there are no observable

transitional states to deal with in PI and affinity change operations.

Holding the thread lock delays rescheduling and transitioning from RUNNING

to any other state. The thread's scheduling state and scheduler

bookkeeping may be updated by a PI operation or affinity change. The CPU

performing the update is obligated to reschedule the CPU the thread is

running on when the update is complete.

2. state=READY, disposition=Enqueued:

Observable when a thread is waiting in the run queue and is not in the

process of rescheduling.

Holding the queue lock delays the thread from transitioning to RUNNING,

being stolen, or migrated by another CPU. The thread's scheduling state,

scheduler bookkeeping, and scheduler queue state may be updated by a PI

operation or affinity change. The CPU performing the update is obligated

to reschedule the CPU the thread is associated with if the change might

affect the currently running thread.

3. state=READY, disposition=Associated:

Observable when a thread is rescheduling and transitioning from READY to

RUNNING and the lock juggling of the rescheduling CPU races with an

update.

During the reschedule, when the next thread has been dequeued it is

expected to be the next thread to run on the CPU. To complete the

transition, the scheduler releases the queue lock (allowing this

observation from another CPU), acquires the thread lock, and re-acquires

the queue lock.

Holding the thread lock on another CPU delays the completion of the

transition, allowing the effective profile, dynamic parameters, and

scheduler bookkeeping or affinity to be updated.

Note: An update of the thread's effective profile and dynamic parameters

can invalidate the thread as the correct/best choice to run. The CPU

performing the update is obligated to reschedule the CPU the thread is

running on, either unconditionally (current behavior) or conditionally if

another thread should run instead (future optimization).

Likewise, a change to the thread's affinity mask may invalidate the CPU

the thread is about to run on as a viable option. The CPU performing the

update is obligated to reschedule the CPU to cause it to migrate the

thread to viable target.

4. state=READY, disposition=Stolen:

Observable when a thread is being stolen and the lock juggling of

the stealing CPU races with an update from another CPU.

When the thread is being stolen, it is dequeued and removed from the

previous scheduler's bookkeeping. To complete the steal, the source

queue lock is released (allowing this observation from another CPU),

the thread lock is acquired, and the destination queue lock is

acquired. Since the stolen thread will become the currently running

thread on the stealing CPU, rescheduling is unnecessary and can be

omitted in a future optimization.

Holding the thread lock on another CPU delays the steal, allowing the

effective profile and dynamic parameters or affinity mask to be updated.

In this state, the thread is not associated with any scheduler

bookkeeping, but the current CPU of thread is stale. However, holding the

queue lock of the stale CPU allows the CPU performing the update to make a

coherent observation of the stolen_by member, which can be used to

determine if an affinity mask change has invalidated the stealing CPU as a

viable option. The updating CPU is obligated to reschedule the stealing

CPU if it became an invalid option due to the update.

5. state=INITIAL,BLOCKED*,SLEEPING,SUSPENDED,DEATH,

disposition=Unassociated

Observable only by the CPU performing a reschedule that transitions the

current thread from RUNNING to one of the non-runnable states. Since a

reschedule of the current thread occurs with the thread lock held, and

transitioning to a non-runnable state clears the current CPU for the

thread, no other CPU can observe this state while holding the thread's

lock and a queue lock.

Public Methods

void SchedulerQueueState ()

Defined at line 765 of file ../../zircon/kernel/include/kernel/scheduler_state.h

void ~SchedulerQueueState ()

Defined at line 766 of file ../../zircon/kernel/include/kernel/scheduler_state.h

void SchedulerQueueState (const SchedulerQueueState & )

Defined at line 768 of file ../../zircon/kernel/include/kernel/scheduler_state.h

SchedulerQueueState & operator= (const SchedulerQueueState & )

Defined at line 769 of file ../../zircon/kernel/include/kernel/scheduler_state.h

void SchedulerQueueState (SchedulerQueueState && )

Defined at line 770 of file ../../zircon/kernel/include/kernel/scheduler_state.h

SchedulerQueueState & operator= (SchedulerQueueState && )

Defined at line 771 of file ../../zircon/kernel/include/kernel/scheduler_state.h

bool OnInsert ()

Sets the thread state to active (i.e. associated with a specific CPU's

scheduler and bookkeeping).

Returns true if the thread was not previously active.

Defined at line 795 of file ../../zircon/kernel/include/kernel/scheduler_state.h

bool OnRemove (cpu_num_t stolen_by)

Sets the thread state to inactive (i.e. not associated with a CPU's

scheduler or bookkeeping). If the thread is being stolen from another CPU's

run queue, stolen_by must be the CPU id of the stealing CPU, otherwise it

must be INVALID_CPU.

Returns true if the task was previously active.

Defined at line 808 of file ../../zircon/kernel/include/kernel/scheduler_state.h

fbl::WAVLTreeNodeState<Thread *> & run_queue_node ()

Returns the run queue node.

Defined at line 816 of file ../../zircon/kernel/include/kernel/scheduler_state.h

const fbl::WAVLTreeNodeState<Thread *> & run_queue_node ()

Defined at line 817 of file ../../zircon/kernel/include/kernel/scheduler_state.h

bool in_queue ()

Returns true if the thread is currently enqueued in a run queue.

Defined at line 820 of file ../../zircon/kernel/include/kernel/scheduler_state.h

cpu_num_t stolen_by ()

Returns the CPU id of the CPU currently stealing the thread.

Defined at line 823 of file ../../zircon/kernel/include/kernel/scheduler_state.h

bool active ()

Returns true if the thread is currently associated with a scheduler.

Defined at line 826 of file ../../zircon/kernel/include/kernel/scheduler_state.h

Disposition disposition ()

Returns the disposition of this scheduler queue state. Asserts on invalid

combinations.

Defined at line 830 of file ../../zircon/kernel/include/kernel/scheduler_state.h

Enumerations

enum Disposition
Name Value
Unassociated 0
Associated 1
Enqueued 2
Stolen 3

The disposition is a concise representation of the valid combinations of

states of the members of this class. It is used in conjunction with the

thread state to determine which operations are valid on a thread and its

associated scheduler, if any.

Defined at line 777 of file ../../zircon/kernel/include/kernel/scheduler_state.h