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 |