Update.
[glibc.git] / linuxthreads / spinlock.c
blob102b1be5f720c50f752417d61d01eebed5901d91
1 /* Linuxthreads - a simple clone()-based implementation of Posix */
2 /* threads for Linux. */
3 /* Copyright (C) 1998 Xavier Leroy (Xavier.Leroy@inria.fr) */
4 /* */
5 /* This program is free software; you can redistribute it and/or */
6 /* modify it under the terms of the GNU Library General Public License */
7 /* as published by the Free Software Foundation; either version 2 */
8 /* of the License, or (at your option) any later version. */
9 /* */
10 /* This program is distributed in the hope that it will be useful, */
11 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
12 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
13 /* GNU Library General Public License for more details. */
15 /* Internal locks */
17 #include <errno.h>
18 #include <sched.h>
19 #include <time.h>
20 #include <stdlib.h>
21 #include <limits.h>
22 #include "pthread.h"
23 #include "internals.h"
24 #include "spinlock.h"
25 #include "restart.h"
27 #if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
28 static void __pthread_acquire(int * spinlock);
29 #endif
32 /* The status field of a spinlock is a pointer whose least significant
33 bit is a locked flag.
35 Thus the field values have the following meanings:
37 status == 0: spinlock is free
38 status == 1: spinlock is taken; no thread is waiting on it
40 (status & 1) == 1: spinlock is taken and (status & ~1L) is a
41 pointer to the first waiting thread; other
42 waiting threads are linked via the p_nextlock
43 field.
44 (status & 1) == 0: same as above, but spinlock is not taken.
46 The waiting list is not sorted by priority order.
47 Actually, we always insert at top of list (sole insertion mode
48 that can be performed without locking).
49 For __pthread_unlock, we perform a linear search in the list
50 to find the highest-priority, oldest waiting thread.
51 This is safe because there are no concurrent __pthread_unlock
52 operations -- only the thread that locked the mutex can unlock it. */
55 void internal_function __pthread_lock(struct _pthread_fastlock * lock,
56 pthread_descr self)
58 #if defined HAS_COMPARE_AND_SWAP
59 long oldstatus, newstatus;
60 int successful_seizure, spurious_wakeup_count = 0;
61 int spin_count = 0;
62 #endif
64 #if defined TEST_FOR_COMPARE_AND_SWAP
65 if (!__pthread_has_cas)
66 #endif
67 #if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
69 __pthread_acquire(&lock->__spinlock);
70 return;
72 #endif
74 #if defined HAS_COMPARE_AND_SWAP
75 again:
77 /* On SMP, try spinning to get the lock. */
79 if (__pthread_smp_kernel) {
80 int max_count = lock->__spinlock * 2 + 10;
82 for (spin_count = 0; spin_count < max_count; spin_count++) {
83 if (((oldstatus = lock->__status) & 1) == 0) {
84 if(__compare_and_swap(&lock->__status, oldstatus, oldstatus | 1))
86 if (spin_count)
87 lock->__spinlock += (spin_count - lock->__spinlock) / 8;
88 return;
93 lock->__spinlock += (spin_count - lock->__spinlock) / 8;
96 /* No luck, try once more or suspend. */
98 do {
99 oldstatus = lock->__status;
100 successful_seizure = 0;
102 if ((oldstatus & 1) == 0) {
103 newstatus = oldstatus | 1;
104 successful_seizure = 1;
105 } else {
106 if (self == NULL)
107 self = thread_self();
108 newstatus = (long) self | 1;
111 if (self != NULL) {
112 THREAD_SETMEM(self, p_nextlock, (pthread_descr) (oldstatus & ~1L));
113 /* Make sure the store in p_nextlock completes before performing
114 the compare-and-swap */
115 MEMORY_BARRIER();
117 } while(! __compare_and_swap(&lock->__status, oldstatus, newstatus));
119 /* Suspend with guard against spurious wakeup.
120 This can happen in pthread_cond_timedwait_relative, when the thread
121 wakes up due to timeout and is still on the condvar queue, and then
122 locks the queue to remove itself. At that point it may still be on the
123 queue, and may be resumed by a condition signal. */
125 if (!successful_seizure) {
126 for (;;) {
127 suspend(self);
128 if (self->p_nextlock != NULL) {
129 /* Count resumes that don't belong to us. */
130 spurious_wakeup_count++;
131 continue;
133 break;
135 goto again;
138 /* Put back any resumes we caught that don't belong to us. */
139 while (spurious_wakeup_count--)
140 restart(self);
141 #endif
144 int __pthread_unlock(struct _pthread_fastlock * lock)
146 #if defined HAS_COMPARE_AND_SWAP
147 long oldstatus;
148 pthread_descr thr, * ptr, * maxptr;
149 int maxprio;
150 #endif
152 #if defined TEST_FOR_COMPARE_AND_SWAP
153 if (!__pthread_has_cas)
154 #endif
155 #if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
157 WRITE_MEMORY_BARRIER();
158 lock->__spinlock = 0;
159 return 0;
161 #endif
163 #if defined HAS_COMPARE_AND_SWAP
164 again:
165 oldstatus = lock->__status;
167 while ((oldstatus = lock->__status) == 1) {
168 if (__compare_and_swap_with_release_semantics(&lock->__status,
169 oldstatus, 0))
170 return 0;
173 /* Find thread in waiting queue with maximal priority */
174 ptr = (pthread_descr *) &lock->__status;
175 thr = (pthread_descr) (oldstatus & ~1L);
176 maxprio = 0;
177 maxptr = ptr;
178 while (thr != 0) {
179 if (thr->p_priority >= maxprio) {
180 maxptr = ptr;
181 maxprio = thr->p_priority;
183 ptr = &(thr->p_nextlock);
184 /* Prevent reordering of the load of lock->__status above and the
185 load of *ptr below, as well as reordering of *ptr between
186 several iterations of the while loop. Some processors (e.g.
187 multiprocessor Alphas) could perform such reordering even though
188 the loads are dependent. */
189 READ_MEMORY_BARRIER();
190 thr = *ptr;
192 /* Prevent reordering of the load of lock->__status above and
193 thr->p_nextlock below */
194 READ_MEMORY_BARRIER();
195 /* Remove max prio thread from waiting list. */
196 if (maxptr == (pthread_descr *) &lock->__status) {
197 /* If max prio thread is at head, remove it with compare-and-swap
198 to guard against concurrent lock operation. This removal
199 also has the side effect of marking the lock as released
200 because the new status comes from thr->p_nextlock whose
201 least significant bit is clear. */
202 thr = (pthread_descr) (oldstatus & ~1L);
203 if (! __compare_and_swap_with_release_semantics
204 (&lock->__status, oldstatus, (long)(thr->p_nextlock)))
205 goto again;
206 } else {
207 /* No risk of concurrent access, remove max prio thread normally.
208 But in this case we must also flip the least significant bit
209 of the status to mark the lock as released. */
210 thr = *maxptr;
211 *maxptr = thr->p_nextlock;
213 do {
214 oldstatus = lock->__status;
215 } while (!__compare_and_swap_with_release_semantics(&lock->__status,
216 oldstatus, oldstatus & ~1L));
218 /* Prevent reordering of store to *maxptr above and store to thr->p_nextlock
219 below */
220 WRITE_MEMORY_BARRIER();
221 /* Wake up the selected waiting thread */
222 thr->p_nextlock = NULL;
223 restart(thr);
225 return 0;
226 #endif
230 * Alternate fastlocks do not queue threads directly. Instead, they queue
231 * these wait queue node structures. When a timed wait wakes up due to
232 * a timeout, it can leave its wait node in the queue (because there
233 * is no safe way to remove from the quue). Some other thread will
234 * deallocate the abandoned node.
238 struct wait_node {
239 struct wait_node *next; /* Next node in null terminated linked list */
240 pthread_descr thr; /* The thread waiting with this node */
241 int abandoned; /* Atomic flag */
244 static long wait_node_free_list;
245 #if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
246 static int wait_node_free_list_spinlock;
247 #endif
249 /* Allocate a new node from the head of the free list using an atomic
250 operation, or else using malloc if that list is empty. A fundamental
251 assumption here is that we can safely access wait_node_free_list->next.
252 That's because we never free nodes once we allocate them, so a pointer to a
253 node remains valid indefinitely. */
255 static struct wait_node *wait_node_alloc(void)
257 #if defined HAS_COMPARE_AND_SWAP
258 long oldvalue, newvalue;
259 #endif
261 #if defined TEST_FOR_COMPARE_AND_SWAP
262 if (!__pthread_has_cas)
263 #endif
264 #if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
266 struct wait_node *new_node = 0;
268 __pthread_acquire(&wait_node_free_list_spinlock);
269 if (wait_node_free_list != 0) {
270 new_node = (struct wait_node *) wait_node_free_list;
271 wait_node_free_list = (long) new_node->next;
273 WRITE_MEMORY_BARRIER();
274 wait_node_free_list_spinlock = 0;
276 if (new_node == 0)
277 return malloc(sizeof *wait_node_alloc());
279 return new_node;
281 #endif
283 #if defined HAS_COMPARE_AND_SWAP
284 do {
285 oldvalue = wait_node_free_list;
287 if (oldvalue == 0)
288 return malloc(sizeof *wait_node_alloc());
290 newvalue = (long) ((struct wait_node *) oldvalue)->next;
291 WRITE_MEMORY_BARRIER();
292 } while (! __compare_and_swap(&wait_node_free_list, oldvalue, newvalue));
294 return (struct wait_node *) oldvalue;
295 #endif
298 /* Return a node to the head of the free list using an atomic
299 operation. */
301 static void wait_node_free(struct wait_node *wn)
303 #if defined HAS_COMPARE_AND_SWAP
304 long oldvalue, newvalue;
305 #endif
307 #if defined TEST_FOR_COMPARE_AND_SWAP
308 if (!__pthread_has_cas)
309 #endif
310 #if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
312 __pthread_acquire(&wait_node_free_list_spinlock);
313 wn->next = (struct wait_node *) wait_node_free_list;
314 wait_node_free_list = (long) wn;
315 WRITE_MEMORY_BARRIER();
316 wait_node_free_list_spinlock = 0;
317 return;
319 #endif
321 #if defined HAS_COMPARE_AND_SWAP
322 do {
323 oldvalue = wait_node_free_list;
324 wn->next = (struct wait_node *) oldvalue;
325 newvalue = (long) wn;
326 WRITE_MEMORY_BARRIER();
327 } while (! __compare_and_swap(&wait_node_free_list, oldvalue, newvalue));
328 #endif
331 #if defined HAS_COMPARE_AND_SWAP
333 /* Remove a wait node from the specified queue. It is assumed
334 that the removal takes place concurrently with only atomic insertions at the
335 head of the queue. */
337 static void wait_node_dequeue(struct wait_node **pp_head,
338 struct wait_node **pp_node,
339 struct wait_node *p_node)
341 /* If the node is being deleted from the head of the
342 list, it must be deleted using atomic compare-and-swap.
343 Otherwise it can be deleted in the straightforward way. */
345 if (pp_node == pp_head) {
346 long oldvalue = (long) p_node;
347 long newvalue = (long) p_node->next;
349 if (__compare_and_swap((long *) pp_node, oldvalue, newvalue))
350 return;
352 /* Oops! Compare and swap failed, which means the node is
353 no longer first. We delete it using the ordinary method. But we don't
354 know the identity of the node which now holds the pointer to the node
355 being deleted, so we must search from the beginning. */
357 for (pp_node = pp_head; *pp_node != p_node; pp_node = &(*pp_node)->next)
358 ; /* null body */
361 *pp_node = p_node->next;
362 return;
365 #endif
367 void __pthread_alt_lock(struct _pthread_fastlock * lock,
368 pthread_descr self)
370 #if defined HAS_COMPARE_AND_SWAP
371 long oldstatus, newstatus;
372 #endif
373 struct wait_node wait_node;
375 #if defined TEST_FOR_COMPARE_AND_SWAP
376 if (!__pthread_has_cas)
377 #endif
378 #if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
380 int suspend_needed = 0;
381 __pthread_acquire(&lock->__spinlock);
383 if (lock->__status == 0)
384 lock->__status = 1;
385 else {
386 if (self == NULL)
387 self = thread_self();
389 wait_node.abandoned = 0;
390 wait_node.next = (struct wait_node *) lock->__status;
391 wait_node.thr = self;
392 lock->__status = (long) &wait_node;
393 suspend_needed = 1;
396 WRITE_MEMORY_BARRIER();
397 lock->__spinlock = 0;
399 if (suspend_needed)
400 suspend (self);
401 return;
403 #endif
405 #if defined HAS_COMPARE_AND_SWAP
406 do {
407 oldstatus = lock->__status;
408 if (oldstatus == 0) {
409 newstatus = 1;
410 } else {
411 if (self == NULL)
412 self = thread_self();
413 wait_node.thr = self;
414 newstatus = (long) &wait_node;
416 wait_node.abandoned = 0;
417 wait_node.next = (struct wait_node *) oldstatus;
418 /* Make sure the store in wait_node.next completes before performing
419 the compare-and-swap */
420 MEMORY_BARRIER();
421 } while(! __compare_and_swap(&lock->__status, oldstatus, newstatus));
423 /* Suspend. Note that unlike in __pthread_lock, we don't worry
424 here about spurious wakeup. That's because this lock is not
425 used in situations where that can happen; the restart can
426 only come from the previous lock owner. */
428 if (oldstatus != 0)
429 suspend(self);
430 #endif
433 /* Timed-out lock operation; returns 0 to indicate timeout. */
435 int __pthread_alt_timedlock(struct _pthread_fastlock * lock,
436 pthread_descr self, const struct timespec *abstime)
438 long oldstatus = 0;
439 #if defined HAS_COMPARE_AND_SWAP
440 long newstatus;
441 #endif
442 struct wait_node *p_wait_node = wait_node_alloc();
444 /* Out of memory, just give up and do ordinary lock. */
445 if (p_wait_node == 0) {
446 __pthread_alt_lock(lock, self);
447 return 1;
450 #if defined TEST_FOR_COMPARE_AND_SWAP
451 if (!__pthread_has_cas)
452 #endif
453 #if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
455 __pthread_acquire(&lock->__spinlock);
457 if (lock->__status == 0)
458 lock->__status = 1;
459 else {
460 if (self == NULL)
461 self = thread_self();
463 p_wait_node->abandoned = 0;
464 p_wait_node->next = (struct wait_node *) lock->__status;
465 p_wait_node->thr = self;
466 lock->__status = (long) p_wait_node;
467 oldstatus = 1; /* force suspend */
470 WRITE_MEMORY_BARRIER();
471 lock->__spinlock = 0;
472 goto suspend;
474 #endif
476 #if defined HAS_COMPARE_AND_SWAP
477 do {
478 oldstatus = lock->__status;
479 if (oldstatus == 0) {
480 newstatus = 1;
481 } else {
482 if (self == NULL)
483 self = thread_self();
484 p_wait_node->thr = self;
485 newstatus = (long) p_wait_node;
487 p_wait_node->abandoned = 0;
488 p_wait_node->next = (struct wait_node *) oldstatus;
489 /* Make sure the store in wait_node.next completes before performing
490 the compare-and-swap */
491 MEMORY_BARRIER();
492 } while(! __compare_and_swap(&lock->__status, oldstatus, newstatus));
493 #endif
495 #if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
496 suspend:
497 #endif
499 /* If we did not get the lock, do a timed suspend. If we wake up due
500 to a timeout, then there is a race; the old lock owner may try
501 to remove us from the queue. This race is resolved by us and the owner
502 doing an atomic testandset() to change the state of the wait node from 0
503 to 1. If we succeed, then it's a timeout and we abandon the node in the
504 queue. If we fail, it means the owner gave us the lock. */
506 if (oldstatus != 0) {
507 if (timedsuspend(self, abstime) == 0) {
508 if (!testandset(&p_wait_node->abandoned))
509 return 0; /* Timeout! */
511 /* Eat oustanding resume from owner, otherwise wait_node_free() below
512 will race with owner's wait_node_dequeue(). */
513 suspend(self);
517 wait_node_free(p_wait_node);
519 return 1; /* Got the lock! */
522 void __pthread_alt_unlock(struct _pthread_fastlock *lock)
524 struct wait_node *p_node, **pp_node, *p_max_prio, **pp_max_prio;
525 struct wait_node ** const pp_head = (struct wait_node **) &lock->__status;
526 int maxprio;
528 #if defined TEST_FOR_COMPARE_AND_SWAP
529 if (!__pthread_has_cas)
530 #endif
531 #if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
533 __pthread_acquire(&lock->__spinlock);
535 #endif
537 while (1) {
539 /* If no threads are waiting for this lock, try to just
540 atomically release it. */
541 #if defined TEST_FOR_COMPARE_AND_SWAP
542 if (!__pthread_has_cas)
543 #endif
544 #if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
546 if (lock->__status == 0 || lock->__status == 1) {
547 lock->__status = 0;
548 break;
551 #endif
553 #if defined TEST_FOR_COMPARE_AND_SWAP
554 else
555 #endif
557 #if defined HAS_COMPARE_AND_SWAP
559 long oldstatus = lock->__status;
560 if (oldstatus == 0 || oldstatus == 1) {
561 if (__compare_and_swap_with_release_semantics (&lock->__status, oldstatus, 0))
562 break;
563 else
564 continue;
567 #endif
569 /* Process the entire queue of wait nodes. Remove all abandoned
570 wait nodes and put them into the global free queue, and
571 remember the one unabandoned node which refers to the thread
572 having the highest priority. */
574 pp_max_prio = pp_node = pp_head;
575 p_max_prio = p_node = *pp_head;
576 maxprio = INT_MIN;
578 while (p_node != (struct wait_node *) 1) {
579 int prio;
581 if (p_node->abandoned) {
582 /* Remove abandoned node. */
583 #if defined TEST_FOR_COMPARE_AND_SWAP
584 if (!__pthread_has_cas)
585 #endif
586 #if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
587 *pp_node = p_node->next;
588 #endif
589 #if defined TEST_FOR_COMPARE_AND_SWAP
590 else
591 #endif
592 #if defined HAS_COMPARE_AND_SWAP
593 wait_node_dequeue(pp_head, pp_node, p_node);
594 #endif
595 wait_node_free(p_node);
596 READ_MEMORY_BARRIER();
597 p_node = *pp_node;
598 continue;
599 } else if ((prio = p_node->thr->p_priority) >= maxprio) {
600 /* Otherwise remember it if its thread has a higher or equal priority
601 compared to that of any node seen thus far. */
602 maxprio = prio;
603 pp_max_prio = pp_node;
604 p_max_prio = p_node;
607 pp_node = &p_node->next;
608 READ_MEMORY_BARRIER();
609 p_node = *pp_node;
612 READ_MEMORY_BARRIER();
614 /* If all threads abandoned, go back to top */
615 if (maxprio == INT_MIN)
616 continue;
618 ASSERT (p_max_prio != (struct wait_node *) 1);
620 /* Now we want to to remove the max priority thread's wait node from
621 the list. Before we can do this, we must atomically try to change the
622 node's abandon state from zero to nonzero. If we succeed, that means we
623 have the node that we will wake up. If we failed, then it means the
624 thread timed out and abandoned the node in which case we repeat the
625 whole unlock operation. */
627 if (!testandset(&p_max_prio->abandoned)) {
628 #if defined TEST_FOR_COMPARE_AND_SWAP
629 if (!__pthread_has_cas)
630 #endif
631 #if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
632 *pp_max_prio = p_max_prio->next;
633 #endif
634 #if defined TEST_FOR_COMPARE_AND_SWAP
635 else
636 #endif
637 #if defined HAS_COMPARE_AND_SWAP
638 wait_node_dequeue(pp_head, pp_max_prio, p_max_prio);
639 #endif
640 WRITE_MEMORY_BARRIER();
641 restart(p_max_prio->thr);
642 break;
646 #if defined TEST_FOR_COMPARE_AND_SWAP
647 if (!__pthread_has_cas)
648 #endif
649 #if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
651 WRITE_MEMORY_BARRIER();
652 lock->__spinlock = 0;
654 #endif
658 /* Compare-and-swap emulation with a spinlock */
660 #ifdef TEST_FOR_COMPARE_AND_SWAP
661 int __pthread_has_cas = 0;
662 #endif
664 #if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
666 int __pthread_compare_and_swap(long * ptr, long oldval, long newval,
667 int * spinlock)
669 int res;
670 if (testandset(spinlock)) __pthread_acquire(spinlock);
671 if (*ptr == oldval) {
672 *ptr = newval; res = 1;
673 } else {
674 res = 0;
676 /* Prevent reordering of store to *ptr above and store to *spinlock below */
677 WRITE_MEMORY_BARRIER();
678 *spinlock = 0;
679 return res;
682 /* This function is called if the inlined test-and-set
683 in __pthread_compare_and_swap() failed */
685 /* The retry strategy is as follows:
686 - We test and set the spinlock MAX_SPIN_COUNT times, calling
687 sched_yield() each time. This gives ample opportunity for other
688 threads with priority >= our priority to make progress and
689 release the spinlock.
690 - If a thread with priority < our priority owns the spinlock,
691 calling sched_yield() repeatedly is useless, since we're preventing
692 the owning thread from making progress and releasing the spinlock.
693 So, after MAX_SPIN_LOCK attemps, we suspend the calling thread
694 using nanosleep(). This again should give time to the owning thread
695 for releasing the spinlock.
696 Notice that the nanosleep() interval must not be too small,
697 since the kernel does busy-waiting for short intervals in a realtime
698 process (!). The smallest duration that guarantees thread
699 suspension is currently 2ms.
700 - When nanosleep() returns, we try again, doing MAX_SPIN_COUNT
701 sched_yield(), then sleeping again if needed. */
703 static void __pthread_acquire(int * spinlock)
705 int cnt = 0;
706 struct timespec tm;
708 while (testandset(spinlock)) {
709 if (cnt < MAX_SPIN_COUNT) {
710 sched_yield();
711 cnt++;
712 } else {
713 tm.tv_sec = 0;
714 tm.tv_nsec = SPIN_SLEEP_DURATION;
715 nanosleep(&tm, NULL);
716 cnt = 0;
721 #endif