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