Rev 73 | Rev 82 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed
| Rev 73 | Rev 81 | ||
|---|---|---|---|
| Line 464... | Line 464... | ||
| 464 | <primary>synchronization</primary> |
464 | <primary>synchronization</primary> |
| 465 | 465 | ||
| 466 | <secondary>- futex</secondary> |
466 | <secondary>- futex</secondary> |
| 467 | </indexterm> |
467 | </indexterm> |
| 468 | 468 | ||
| - | 469 | <para>In a multithreaded environment, userspace threads need an |
|
| - | 470 | efficient way to synchronize. HelenOS borrows an idea from Linux<xref |
|
| - | 471 | linkend="futex" /> to implement lightweight userspace synchronization |
|
| - | 472 | and mutual exclusion primitive called futex. The key idea behind futexes |
|
| - | 473 | is that non-contended synchronization is very fast and takes place only |
|
| - | 474 | in userspace without kernel's intervention. When more threads contend |
|
| - | 475 | for a futex, only one of them wins; other threads go to sleep via a |
|
| - | 476 | dedicated syscall.</para> |
|
| - | 477 | ||
| - | 478 | <para>The userspace part of the futex is a mere integer variable, a |
|
| - | 479 | counter, that can be atomically incremented or decremented. The kernel |
|
| - | 480 | part is rather more complicated. For each userspace futex counter, there |
|
| - | 481 | is a kernel structure describing the futex. This structure |
|
| - | 482 | contains:</para> |
|
| - | 483 | ||
| - | 484 | <itemizedlist> |
|
| - | 485 | <listitem> |
|
| - | 486 | <para>number of references,</para> |
|
| - | 487 | </listitem> |
|
| - | 488 | ||
| - | 489 | <listitem> |
|
| - | 490 | <para>physical address of the userspace futex counter,</para> |
|
| - | 491 | </listitem> |
|
| - | 492 | ||
| - | 493 | <listitem> |
|
| - | 494 | <para>hash table link and</para> |
|
| - | 495 | </listitem> |
|
| - | 496 | ||
| - | 497 | <listitem> |
|
| - | 498 | <para>a wait queue.</para> |
|
| - | 499 | </listitem> |
|
| - | 500 | </itemizedlist> |
|
| - | 501 | ||
| - | 502 | <para>The reference count helps to find out when the futex is no longer |
|
| - | 503 | needed and can be deallocated. The physical address is used as a key for |
|
| - | 504 | the global futex hash table. Note that the kernel has to use physical |
|
| - | 505 | address to identify the futex beacause one futex can be used for |
|
| - | 506 | synchronization among different address spaces and can have different |
|
| - | 507 | virtual addresses in each of them. Finally, the kernel futex structure |
|
| - | 508 | includes a wait queue. The wait queue is used to put threads that didn't |
|
| - | 509 | win the futex to sleep until the winner wakes one of them up.</para> |
|
| - | 510 | ||
| - | 511 | <para>A futex should be initialized by setting its userspace counter to |
|
| - | 512 | one before it is used. When locking the futex via userspace library |
|
| - | 513 | function <code>futex_down_timeout</code>, the library code atomically |
|
| - | 514 | decrements the futex counter and tests if it droped below zero. If it |
|
| - | 515 | did, then the futex is locked by another thread and the library uses the |
|
| - | 516 | <constant>SYS_FUTEX_SLEEP</constant> syscall to put the thread asleep. |
|
| - | 517 | If the counter decreased to 0, then there was no contention and the |
|
| - | 518 | thread can enter the critical section protected by the futex. When the |
|
| - | 519 | thread later leaves that critical section, it, using library function |
|
| - | 520 | <code>futex_up</code>, atomically increments the counter. If the counter |
|
| - | 521 | value increased to one, then there again was no contention and no action |
|
| - | 522 | needs to be done. However, if it increased to zero or even a smaller |
|
| - | 523 | number, then there are sleeping threads waiting for the futex to become |
|
| - | 524 | available. In that case, the library has to use the |
|
| - | 525 | <constant>SYS_FUTEX_WAKEUP</constant> syscall to wake one sleeping |
|
| 469 | <para></para> |
526 | thread.</para> |
| - | 527 | ||
| - | 528 | <para>So far, futexes are very elegant in that they don't interfere with |
|
| - | 529 | the kernel when there is no contention for them. Another nice aspect of |
|
| - | 530 | futexes is that they don't need to be registered anywhere prior to the |
|
| - | 531 | first kernel intervention.</para> |
|
| - | 532 | ||
| - | 533 | <para>Both futex related syscalls, <constant>SYS_FUTEX_SLEEP</constant> |
|
| - | 534 | and <constant>SYS_FUTEX_WAKEUP</constant>, respectivelly, are mere |
|
| - | 535 | wrappers for <code>waitq_sleep_timeout</code> and |
|
| - | 536 | <code>waitq_sleep_wakeup</code>, respectively, with some housekeeping |
|
| - | 537 | functionality added. Both syscalls need to translate the userspace |
|
| - | 538 | virtual address of the futex counter to physical address in order to |
|
| - | 539 | support synchronization accross shared memory. Once the physical address |
|
| - | 540 | is known, the kernel checks whether the futexes are already known to it |
|
| - | 541 | by searching the global futex hash table for an item with the physical |
|
| - | 542 | address of the futex counter as a key. When the search is successful, it |
|
| - | 543 | returns an address of the kernel futex structure associated with the |
|
| - | 544 | counter. If the hash table does not contain the key, the kernel creates |
|
| - | 545 | it and inserts it into the hash table. At the same time, the the current |
|
| - | 546 | task's B+tree of known futexes is searched in order to find out if the |
|
| - | 547 | task already uses the futex. If it does, no action is taken. Otherwise |
|
| - | 548 | the reference count of the futex is incremented, provided that the futex |
|
| - | 549 | already existed.</para> |
|
| 470 | </section> |
550 | </section> |
| 471 | </section> |
551 | </section> |
| 472 | </chapter> |
552 | </chapter> |
| 473 | 553 | ||