Subversion Repositories HelenOS-doc

Rev

Rev 45 | Rev 57 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 45 Rev 48
Line 173... Line 173...
173
 
173
 
174
      <para>The interesting point about wait queues is that the number of
174
      <para>The interesting point about wait queues is that the number of
175
      missed wakeups is equal to the number of threads that will not block in
175
      missed wakeups is equal to the number of threads that will not block in
176
      <emphasis>watiq_sleep_timeout</emphasis> and would immediately succeed
176
      <emphasis>watiq_sleep_timeout</emphasis> and would immediately succeed
177
      instead. On the other hand, semaphores are synchronization primitives
177
      instead. On the other hand, semaphores are synchronization primitives
178
      that will let predefined amount of threads into its critical section and
178
      that will let predefined amount of threads into their critical section
179
      block any other threads above this count. However, these two cases are
179
      and block any other threads above this count. However, these two cases
180
      exactly the same. Semaphores in HelenOS are therefore implemented as
180
      are exactly the same. Semaphores in HelenOS are therefore implemented as
181
      wait queues with a single semantic change: their wait queue is
181
      wait queues with a single semantic change: their wait queue is
182
      initialized to have so many missed wakeups as is the number of threads
182
      initialized to have so many missed wakeups as is the number of threads
183
      that the semphore intends to let into its critical section
183
      that the semphore intends to let into its critical section
184
      simultaneously.</para>
184
      simultaneously.</para>
185
 
185
 
Line 290... Line 290...
290
    </section>
290
    </section>
291
 
291
 
292
    <section>
292
    <section>
293
      <title>Condition variables</title>
293
      <title>Condition variables</title>
294
 
294
 
-
 
295
      <para>Condition variables can be used for waiting until a condition
-
 
296
      becomes true. In this respect, they are similar to wait queues. But
-
 
297
      contrary to wait queues, condition variables have different semantics
-
 
298
      that allows events to be lost when there is no thread waiting for them.
-
 
299
      In order to support this, condition variables don't use direct hand-off
-
 
300
      and operate in a way similar to the example below. A thread waiting for
-
 
301
      the condition becoming true does the following:</para>
-
 
302
 
-
 
303
      <para><programlisting language="C"><function>mutex_lock</function>(<varname>mtx</varname>);
-
 
304
while (!<varname>condition</varname>)
-
 
305
        <function>condvar_wait_timeout</function>(<varname>cv</varname>, <varname>mtx</varname>);
-
 
306
/* <remark>the condition is true, do something</remark> */
-
 
307
<function>mutex_unlock</function>(<varname>mtx</varname>);</programlisting></para>
-
 
308
 
-
 
309
      <para>A thread that causes the condition become true signals this event
-
 
310
      like this:</para>
-
 
311
 
-
 
312
      <para><programlisting><function>mutex_lock</function>(<varname>mtx</varname>);
-
 
313
<varname>condition</varname> = <constant>true</constant>;
-
 
314
<function>condvar_signal</function>(<varname>cv</varname>);  /* <remark>condvar_broadcast(cv);</remark> */
-
 
315
<function>mutex_unlock</function>(<varname>mtx</varname>);</programlisting></para>
-
 
316
 
-
 
317
      <para>The wait operation, <emphasis>condvar_wait_timeout</emphasis>,
-
 
318
      always puts the calling thread to sleep. The thread then sleeps until
-
 
319
      another thread invokes <emphasis>condvar_broadcast</emphasis> on the
-
 
320
      same condition variable or until it is woken up by
-
 
321
      <emphasis>condvar_signal</emphasis>. The
-
 
322
      <emphasis>condvar_signal</emphasis> operation unblocks the first thread
-
 
323
      blocking on the condition variable while the
-
 
324
      <emphasis>condvar_broadcast</emphasis> operation unblocks all threads
-
 
325
      blocking there. If there are no blocking threads, these two operations
-
 
326
      have no efect.</para>
-
 
327
 
-
 
328
      <para>Note that the threads must synchronize over a dedicated mutex. To
-
 
329
      prevent race condition between <emphasis>condvar_wait_timeout</emphasis>
-
 
330
      and <emphasis>condvar_signal</emphasis> or
-
 
331
      <emphasis>condvar_broadcast</emphasis>, the mutex is passed to
-
 
332
      <emphasis>condvar_wait_timeout</emphasis> which then atomically puts the
-
 
333
      calling thread asleep and unlocks the mutex. When the thread eventually
-
 
334
      wakes up, <emphasis>condvar_wait</emphasis> regains the mutex and
-
 
335
      returns.</para>
-
 
336
 
-
 
337
      <para>Also note, that there is no conditional operation for condition
-
 
338
      variables. Such an operation would make no sence since condition
-
 
339
      variables are defined to forget events for which there is no waiting
-
 
340
      thread and because <emphasis>condvar_wait</emphasis> must always go to
-
 
341
      sleep. The operation with timeout is supported as usually.</para>
-
 
342
 
-
 
343
      <para>In HelenOS, condition variables are based on wait queues. As it is
-
 
344
      already mentioned above, wait queues remember missed events while
-
 
345
      condition variables must not do so. This is reasoned by the fact that
-
 
346
      condition variables are designed for scenarios in which an event might
-
 
347
      occur very many times without being picked up by any waiting thread. On
-
 
348
      the other hand, wait queues would remember any event that had not been
-
 
349
      picked up by a call to <emphasis>waitq_sleep_timeout</emphasis>.
-
 
350
      Therefore, if wait queues were used directly and without any changes to
-
 
351
      implement condition variables, the missed_wakeup counter would hurt
-
 
352
      performance of the implementation: the <code>while</code> loop in
-
 
353
      <emphasis>condvar_wait_timeout</emphasis> would effectively do busy
-
 
354
      waiting until all missed wakeups were discarded.</para>
-
 
355
 
-
 
356
      <para>The requirement on the wait operation to atomically put the caller
-
 
357
      to sleep and release the mutex poses an interesting problem on
-
 
358
      <emphasis>condvar_wait_timeout</emphasis>. More precisely, the thread
-
 
359
      should sleep in the condvar's wait queue prior to releasing the mutex,
-
 
360
      but it must not hold the mutex when it is sleeping.</para>
-
 
361
 
-
 
362
      <para>Problems described in the two previous paragraphs are addressed in
-
 
363
      HelenOS by dividing the <emphasis>waitq_sleep_timeout</emphasis>
-
 
364
      function into three pieces:</para>
-
 
365
 
-
 
366
      <orderedlist>
-
 
367
        <listitem>
-
 
368
          <para><emphasis>waitq_sleep_prepare</emphasis> prepares the thread
-
 
369
          to go to sleep by, among other things, locking the wait
-
 
370
          queue;</para>
-
 
371
        </listitem>
-
 
372
 
-
 
373
        <listitem>
-
 
374
          <para><emphasis>waitq_sleep_timeout_unsafe</emphasis> implements the
-
 
375
          core blocking logic;</para>
-
 
376
        </listitem>
-
 
377
 
-
 
378
        <listitem>
-
 
379
          <para><emphasis>waitq_sleep_finish</emphasis> performs cleanup after
-
 
380
          <emphasis>waitq_sleep_timeout_unsafe</emphasis>; after this call,
-
 
381
          the wait queue spinlock is guaranteed to be unlocked by the
-
 
382
          caller</para>
-
 
383
        </listitem>
-
 
384
      </orderedlist>
-
 
385
 
-
 
386
      <para>The stock <emphasis>waitq_sleep_timeout</emphasis> is then a mere
-
 
387
      wrapper that calls these three functions. It is provided for convenience
-
 
388
      in cases where the caller doesn't require such a low level control.
-
 
389
      However, the implementation of <emphasis>condvar_wait_timeout</emphasis>
-
 
390
      does need this finer-grained control because it has to interleave calls
-
 
391
      to these functions by other actions. It carries its operations out in
-
 
392
      the following order:</para>
-
 
393
 
-
 
394
      <orderedlist>
-
 
395
        <listitem>
-
 
396
          <para>calls <emphasis>waitq_sleep_prepare</emphasis> in order to
-
 
397
          lock the condition variable's wait queue,</para>
-
 
398
        </listitem>
-
 
399
 
-
 
400
        <listitem>
295
      <para>Condvars explanation</para>
401
          <para>releases the mutex,</para>
-
 
402
        </listitem>
-
 
403
 
-
 
404
        <listitem>
-
 
405
          <para>clears the counter of missed wakeups,</para>
-
 
406
        </listitem>
-
 
407
 
-
 
408
        <listitem>
-
 
409
          <para>calls <emphasis>waitq_sleep_timeout_unsafe</emphasis>,</para>
-
 
410
        </listitem>
-
 
411
 
-
 
412
        <listitem>
-
 
413
          <para>retakes the mutex,</para>
-
 
414
        </listitem>
-
 
415
 
-
 
416
        <listitem>
-
 
417
          <para>calls <emphasis>waitq_sleep_finish</emphasis>.</para>
-
 
418
        </listitem>
-
 
419
      </orderedlist>
296
    </section>
420
    </section>
297
  </section>
421
  </section>
298
 
422
 
299
  <section>
423
  <section>
300
    <title>Userspace synchronization</title>
424
    <title>Userspace synchronization</title>