79,27 → 79,27 |
of synchronous context switches, because it is a natural vehicle not |
only for changes in control flow, but also for switching between two |
kernel stacks. Two functions figure in a synchronous context switch |
implementation: <code>context_save</code> and |
<code>context_restore</code>. Note that these two functions break the |
implementation: <code>context_save()</code> and |
<code>context_restore()</code>. Note that these two functions break the |
natural perception of the linear C code execution flow starting at |
function's entry point and ending on one of the function's exit |
points.</para> |
|
<para>When the <code>context_save</code> function is called, the |
<para>When the <code>context_save()</code> function is called, the |
synchronous context is saved in a memory structure passed to it. After |
executing <code>context_save</code>, the caller is returned 1 as a |
executing <code>context_save()</code>, the caller is returned 1 as a |
return value. The execution of instructions continues as normally until |
<code>context_restore</code> is called. For the caller, it seems like |
<code>context_restore()</code> is called. For the caller, it seems like |
the call never returns<footnote> |
<para>Which might be a source of problems with variable liveliness |
after <code>context_restore</code>.</para> |
after <code>context_restore()</code>.</para> |
</footnote>. Nevertheless, a synchronous register context, which is |
saved in a memory structure passed to <code>context_restore,</code> is |
saved in a memory structure passed to <code>context_restore()</code>, is |
restored, thus transfering the control flow to the place of occurrence |
of the corresponding call to <code>context_save</code>. From the |
of the corresponding call to <code>context_save()</code>. From the |
perspective of the caller of the corresponding |
<code>context_save</code>, it looks as though a return from |
<code>context_save</code>. However, this time a return value of 0 is |
<code>context_save()</code>, it looks like a return from |
<code>context_save()</code>. However, this time a return value of 0 is |
returned.</para> |
</section> |
</section> |
136,7 → 136,7 |
<constant>Sleeping</constant> state by going to sleep or enters the |
<constant>Exiting</constant> state when it reaches termination. When the |
thread exits, its kernel structure usually stays in memory, until the |
thread is detached by another thread using <code>thread_detach</code> |
thread is detached by another thread using <code>thread_detach()</code> |
function. Terminated but undetached threads are in the |
<constant>Undead</constant> state. When the thread is detached or |
detaches itself during its life, it is destroyed in the |
207,7 → 207,7 |
<title>Scheduler Operation</title> |
|
<para>The scheduler is invoked either explicitly when a thread calls the |
<code>scheduler</code> function (e.g. goes to sleep or merely wants to |
<code>scheduler()</code> function (e.g. goes to sleep or merely wants to |
relinquish the processor for a while) or implicitly on a periodic basis |
when the generic clock interrupt preempts the current thread. After its |
invocation, the scheduler saves the synchronous register context of the |
225,9 → 225,9 |
queues from lower towards higher indices and looks for a thread. If the |
visited run queue is empty, it simply searches the next run queue. If it |
is known in advance that there are no ready threads waiting for |
execution, <code>find_best_thread</code> interruptibly halts the |
execution, <code>find_best_thread()</code> interruptibly halts the |
processor or busy waits until some threads arrive. This process repeats |
until <code>find_best_thread</code> succeeds.</para> |
until <code>find_best_thread()</code> succeeds.</para> |
|
<para>After the best thread is chosen, the scheduler switches to the |
thread's task and memory management context. Finally, the saved |