RFC: Microkernel tasks pending on nanokernel objects

Mitsis, Peter <Peter.Mitsis@...>

The following is proposed to permit microkernel tasks to pend on nanokernel

When the nanokernel's background task must wait for a nanokernel object
(N.O.) it polls. The polling is done at a frequency of no more than once
per interrupt. Thus, if the timer is the only source of interrupts then
the polling is done once no more than once per tick.

This is fine for the nanokernel, but in a microkernel where there are
multiple tasks, it is very inefficient.

To allow multiple microkernel tasks to pend on N.O.'s, let there be a
second queue per N.O. and let it be used strictly for pending tasks.

NOTE: This second queue would only apply to N.O.'s in a microkernel system.
Since there is only one task in a nanokernel system, there is no need for
this second queue.

| Nanokernel Object |
| |
Fiber1 Task1
| |
Fiber2 Task2
| |
--- Task3
- |

Fig 1: A N.O. with both waiting fibers and tasks

Proposed N.O. Rules:
1. When a task must wait on a N.O., it is added to the task wait queue.
2. When a fiber must wait on a N.O., it is added to the fiber wait queue.
3. When someone posts to the N.O. ...
A. If there is a waiting fiber, let it claim it immediately.
B. If there are no waiting fibers, mark it is availble and wake all
pending tasks.
4. An awakened task must check for timeout before trying to claim the N.O.
If it can not claim it, then it adds itself to the task wait queue.

Explanation of Rules:
Preference is to be given to fibers just as in a nanokernel system.

By waking all the tasks waiting on that N.O. ...
1. The waiting tasks are automatically sorted by the scheduler by priority.
2. The meaning of a task's timeout parameter remains unchanged from the
3. Timeouts are handled neatly with minimal new code.

Additional Changes
In a microkernel system, the nanokernel would also require the ability to
notify the kernel of tasks that are to be set to a wait state as well as
tasks that are to be cleared of their wait state. As the nanokernel deals
with thread control structures (TCS) and the microkernel deals with k_task
structures. A mechanism is required to convert a nanokernel TCS pointer to
a k_task pointer. This can be done easily by ...
1. Adding a new field to the TCS such as ...
void *uk_task_ptr; /* ptr to the microkernel k_task */
2. Modifying _new_thread() to accept the microkernel k_task pointer as a
parameter. If it is a fiber that it is being created, use NULL.

To mark a task as waiting on a nanokernel object, a new TF_xxx bit will
be defined.
#define TF_NANO 0x00000400

Known Limitations and Problems
This will not be done for the nanokernel stacks (nano_stack.c).

Since many tasks may be actively waiting on a N.O., it may take an
indeterminate amount of time to find the next task to run.
This is non-real time behavior!

For example:
Imagine 100 tasks of high priority that are actively waiting on a
nanokernel FIFO. That is, there are 100 tasks that are ready to execute
but they are waiting for data from that FIFO. Each one of those 100
tasks would have to put themselves back onto the FIFO's task wait queue
before a low priority task would get to run. That low priority task
might be the idle task, but it could just as easily be something else.