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> |