(__pthread_release): New inline function for releasing
[kopensolaris-gnu/glibc.git] / linuxthreads / spinlock.c
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.                 */
14
15 /* Internal locks */
16
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"
26
27 #if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
28 static void __pthread_acquire(int * spinlock);
29
30 static inline void __pthread_release(int * spinlock)
31 {
32   WRITE_MEMORY_BARRIER();
33   *spinlock = __LT_SPINLOCK_INIT;
34   __asm __volatile ("" : "=m" (*spinlock) : "0" (*spinlock));
35 }
36 #endif
37
38
39 /* The status field of a spinlock is a pointer whose least significant
40    bit is a locked flag.
41
42    Thus the field values have the following meanings:
43
44    status == 0:       spinlock is free
45    status == 1:       spinlock is taken; no thread is waiting on it
46
47    (status & 1) == 1: spinlock is taken and (status & ~1L) is a
48                       pointer to the first waiting thread; other
49                       waiting threads are linked via the p_nextlock
50                       field.
51    (status & 1) == 0: same as above, but spinlock is not taken.
52
53    The waiting list is not sorted by priority order.
54    Actually, we always insert at top of list (sole insertion mode
55    that can be performed without locking).
56    For __pthread_unlock, we perform a linear search in the list
57    to find the highest-priority, oldest waiting thread.
58    This is safe because there are no concurrent __pthread_unlock
59    operations -- only the thread that locked the mutex can unlock it. */
60
61
62 void internal_function __pthread_lock(struct _pthread_fastlock * lock,
63                                       pthread_descr self)
64 {
65 #if defined HAS_COMPARE_AND_SWAP
66   long oldstatus, newstatus;
67   int successful_seizure, spurious_wakeup_count = 0;
68   int spin_count = 0;
69 #endif
70
71 #if defined TEST_FOR_COMPARE_AND_SWAP
72   if (!__pthread_has_cas)
73 #endif
74 #if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
75   {
76     __pthread_acquire(&lock->__spinlock);
77     return;
78   }
79 #endif
80
81 #if defined HAS_COMPARE_AND_SWAP
82 again:
83
84   /* On SMP, try spinning to get the lock. */
85
86   if (__pthread_smp_kernel) {
87     int max_count = lock->__spinlock * 2 + 10;
88
89     for (spin_count = 0; spin_count < max_count; spin_count++) {
90       if (((oldstatus = lock->__status) & 1) == 0) {
91         if(__compare_and_swap(&lock->__status, oldstatus, oldstatus | 1))
92         {
93           if (spin_count)
94             lock->__spinlock += (spin_count - lock->__spinlock) / 8;
95           READ_MEMORY_BARRIER();
96           return;
97         }
98       }
99       __asm __volatile ("" : "=m" (lock->__status) : "0" (lock->__status));
100     }
101
102     lock->__spinlock += (spin_count - lock->__spinlock) / 8;
103   }
104
105   /* No luck, try once more or suspend. */
106
107   do {
108     oldstatus = lock->__status;
109     successful_seizure = 0;
110
111     if ((oldstatus & 1) == 0) {
112       newstatus = oldstatus | 1;
113       successful_seizure = 1;
114     } else {
115       if (self == NULL)
116         self = thread_self();
117       newstatus = (long) self | 1;
118     }
119
120     if (self != NULL) {
121       THREAD_SETMEM(self, p_nextlock, (pthread_descr) (oldstatus & ~1L));
122       /* Make sure the store in p_nextlock completes before performing
123          the compare-and-swap */
124       MEMORY_BARRIER();
125     }
126   } while(! __compare_and_swap(&lock->__status, oldstatus, newstatus));
127
128   /* Suspend with guard against spurious wakeup.
129      This can happen in pthread_cond_timedwait_relative, when the thread
130      wakes up due to timeout and is still on the condvar queue, and then
131      locks the queue to remove itself. At that point it may still be on the
132      queue, and may be resumed by a condition signal. */
133
134   if (!successful_seizure) {
135     for (;;) {
136       suspend(self);
137       if (self->p_nextlock != NULL) {
138         /* Count resumes that don't belong to us. */
139         spurious_wakeup_count++;
140         continue;
141       }
142       break;
143     }
144     goto again;
145   }
146
147   /* Put back any resumes we caught that don't belong to us. */
148   while (spurious_wakeup_count--)
149     restart(self);
150
151   READ_MEMORY_BARRIER();
152 #endif
153 }
154
155 int __pthread_unlock(struct _pthread_fastlock * lock)
156 {
157 #if defined HAS_COMPARE_AND_SWAP
158   long oldstatus;
159   pthread_descr thr, * ptr, * maxptr;
160   int maxprio;
161 #endif
162
163 #if defined TEST_FOR_COMPARE_AND_SWAP
164   if (!__pthread_has_cas)
165 #endif
166 #if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
167   {
168     __pthread_release(&lock->__spinlock);
169     return 0;
170   }
171 #endif
172
173 #if defined HAS_COMPARE_AND_SWAP
174   WRITE_MEMORY_BARRIER();
175
176 again:
177   oldstatus = lock->__status;
178
179   while ((oldstatus = lock->__status) == 1) {
180     if (__compare_and_swap_with_release_semantics(&lock->__status,
181         oldstatus, 0))
182       return 0;
183   }
184
185   /* Find thread in waiting queue with maximal priority */
186   ptr = (pthread_descr *) &lock->__status;
187   thr = (pthread_descr) (oldstatus & ~1L);
188   maxprio = 0;
189   maxptr = ptr;
190
191   /* Before we iterate over the wait queue, we need to execute
192      a read barrier, otherwise we may read stale contents of nodes that may
193      just have been inserted by other processors. One read barrier is enough to
194      ensure we have a stable list; we don't need one for each pointer chase
195      through the list, because we are the owner of the lock; other threads
196      can only add nodes at the front; if a front node is consistent,
197      the ones behind it must also be. */
198
199   READ_MEMORY_BARRIER();
200
201   while (thr != 0) {
202     if (thr->p_priority >= maxprio) {
203       maxptr = ptr;
204       maxprio = thr->p_priority;
205     }
206     ptr = &(thr->p_nextlock);
207     thr = *ptr;
208   }
209
210   /* Remove max prio thread from waiting list. */
211   if (maxptr == (pthread_descr *) &lock->__status) {
212     /* If max prio thread is at head, remove it with compare-and-swap
213        to guard against concurrent lock operation. This removal
214        also has the side effect of marking the lock as released
215        because the new status comes from thr->p_nextlock whose
216        least significant bit is clear. */
217     thr = (pthread_descr) (oldstatus & ~1L);
218     if (! __compare_and_swap_with_release_semantics
219             (&lock->__status, oldstatus, (long)(thr->p_nextlock)))
220       goto again;
221   } else {
222     /* No risk of concurrent access, remove max prio thread normally.
223        But in this case we must also flip the least significant bit
224        of the status to mark the lock as released. */
225     thr = *maxptr;
226     *maxptr = thr->p_nextlock;
227
228     /* Ensure deletion from linked list completes before we
229        release the lock. */
230     WRITE_MEMORY_BARRIER();
231
232     do {
233       oldstatus = lock->__status;
234     } while (!__compare_and_swap_with_release_semantics(&lock->__status,
235              oldstatus, oldstatus & ~1L));
236   }
237
238   /* Wake up the selected waiting thread. Woken thread can check
239      its own p_nextlock field for NULL to detect that it has been removed. No
240      barrier is needed here, since restart() and suspend() take
241      care of memory synchronization. */
242
243   thr->p_nextlock = NULL;
244   restart(thr);
245
246   return 0;
247 #endif
248 }
249
250 /*
251  * Alternate fastlocks do not queue threads directly. Instead, they queue
252  * these wait queue node structures. When a timed wait wakes up due to
253  * a timeout, it can leave its wait node in the queue (because there
254  * is no safe way to remove from the quue). Some other thread will
255  * deallocate the abandoned node.
256  */
257
258
259 struct wait_node {
260   struct wait_node *next;       /* Next node in null terminated linked list */
261   pthread_descr thr;            /* The thread waiting with this node */
262   int abandoned;                /* Atomic flag */
263 };
264
265 static long wait_node_free_list;
266 #if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
267 static int wait_node_free_list_spinlock;
268 #endif
269
270 /* Allocate a new node from the head of the free list using an atomic
271    operation, or else using malloc if that list is empty.  A fundamental
272    assumption here is that we can safely access wait_node_free_list->next.
273    That's because we never free nodes once we allocate them, so a pointer to a
274    node remains valid indefinitely. */
275
276 static struct wait_node *wait_node_alloc(void)
277 {
278 #if defined HAS_COMPARE_AND_SWAP
279   long oldvalue, newvalue;
280 #endif
281
282 #if defined TEST_FOR_COMPARE_AND_SWAP
283   if (!__pthread_has_cas)
284 #endif
285 #if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
286   {
287     struct wait_node *new_node = 0;
288
289     __pthread_acquire(&wait_node_free_list_spinlock);
290     if (wait_node_free_list != 0) {
291       new_node = (struct wait_node *) wait_node_free_list;
292       wait_node_free_list = (long) new_node->next;
293     }
294     WRITE_MEMORY_BARRIER();
295     wait_node_free_list_spinlock = 0;
296
297     if (new_node == 0)
298       return malloc(sizeof *wait_node_alloc());
299
300     return new_node;
301   }
302 #endif
303
304 #if defined HAS_COMPARE_AND_SWAP
305   do {
306     oldvalue = wait_node_free_list;
307
308     if (oldvalue == 0)
309       return malloc(sizeof *wait_node_alloc());
310
311     /* Ensure we don't read stale next link through oldvalue pointer. */
312     READ_MEMORY_BARRIER();
313     newvalue = (long) ((struct wait_node *) oldvalue)->next;
314   } while (! __compare_and_swap(&wait_node_free_list, oldvalue, newvalue));
315
316   return (struct wait_node *) oldvalue;
317 #endif
318 }
319
320 /* Return a node to the head of the free list using an atomic
321    operation. */
322
323 static void wait_node_free(struct wait_node *wn)
324 {
325 #if defined HAS_COMPARE_AND_SWAP
326   long oldvalue, newvalue;
327 #endif
328
329 #if defined TEST_FOR_COMPARE_AND_SWAP
330   if (!__pthread_has_cas)
331 #endif
332 #if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
333   {
334     __pthread_acquire(&wait_node_free_list_spinlock);
335     wn->next = (struct wait_node *) wait_node_free_list;
336     wait_node_free_list = (long) wn;
337     WRITE_MEMORY_BARRIER();
338     wait_node_free_list_spinlock = 0;
339     return;
340   }
341 #endif
342
343 #if defined HAS_COMPARE_AND_SWAP
344   do {
345     oldvalue = wait_node_free_list;
346     wn->next = (struct wait_node *) oldvalue;
347     newvalue = (long) wn;
348     /* Ensure node contents are written before we swap it into the list. */
349     WRITE_MEMORY_BARRIER();
350   } while (! __compare_and_swap(&wait_node_free_list, oldvalue, newvalue));
351 #endif
352 }
353
354 #if defined HAS_COMPARE_AND_SWAP
355
356 /* Remove a wait node from the specified queue.  It is assumed
357    that the removal takes place concurrently with only atomic insertions at the
358    head of the queue. */
359
360 static void wait_node_dequeue(struct wait_node **pp_head,
361                               struct wait_node **pp_node,
362                               struct wait_node *p_node)
363 {
364   /* If the node is being deleted from the head of the
365      list, it must be deleted using atomic compare-and-swap.
366      Otherwise it can be deleted in the straightforward way. */
367
368   if (pp_node == pp_head) {
369     /* We don't need a read barrier between these next two loads,
370        because it is assumed that the caller has already ensured
371        the stability of *p_node with respect to p_node. */
372
373     long oldvalue = (long) p_node;
374     long newvalue = (long) p_node->next;
375
376     if (__compare_and_swap((long *) pp_node, oldvalue, newvalue))
377       return;
378
379     /* Oops! Compare and swap failed, which means the node is
380        no longer first. We delete it using the ordinary method.  But we don't
381        know the identity of the node which now holds the pointer to the node
382        being deleted, so we must search from the beginning. */
383
384     for (pp_node = pp_head; p_node != *pp_node; ) {
385       pp_node = &(*pp_node)->next;
386       READ_MEMORY_BARRIER(); /* Stabilize *pp_node for next iteration. */
387     }
388   }
389
390   *pp_node = p_node->next;
391   return;
392 }
393
394 #endif
395
396 void __pthread_alt_lock(struct _pthread_fastlock * lock,
397                         pthread_descr self)
398 {
399 #if defined HAS_COMPARE_AND_SWAP
400   long oldstatus, newstatus;
401 #endif
402   struct wait_node wait_node;
403
404 #if defined TEST_FOR_COMPARE_AND_SWAP
405   if (!__pthread_has_cas)
406 #endif
407 #if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
408   {
409     int suspend_needed = 0;
410     __pthread_acquire(&lock->__spinlock);
411
412     if (lock->__status == 0)
413       lock->__status = 1;
414     else {
415       if (self == NULL)
416         self = thread_self();
417
418       wait_node.abandoned = 0;
419       wait_node.next = (struct wait_node *) lock->__status;
420       wait_node.thr = self;
421       lock->__status = (long) &wait_node;
422       suspend_needed = 1;
423     }
424
425     __pthread_release(&lock->__spinlock);
426
427     if (suspend_needed)
428       suspend (self);
429     return;
430   }
431 #endif
432
433 #if defined HAS_COMPARE_AND_SWAP
434   do {
435     oldstatus = lock->__status;
436     if (oldstatus == 0) {
437       newstatus = 1;
438     } else {
439       if (self == NULL)
440         self = thread_self();
441       wait_node.thr = self;
442       newstatus = (long) &wait_node;
443     }
444     wait_node.abandoned = 0;
445     wait_node.next = (struct wait_node *) oldstatus;
446     /* Make sure the store in wait_node.next completes before performing
447        the compare-and-swap */
448     MEMORY_BARRIER();
449   } while(! __compare_and_swap(&lock->__status, oldstatus, newstatus));
450
451   /* Suspend. Note that unlike in __pthread_lock, we don't worry
452      here about spurious wakeup. That's because this lock is not
453      used in situations where that can happen; the restart can
454      only come from the previous lock owner. */
455
456   if (oldstatus != 0)
457     suspend(self);
458
459   READ_MEMORY_BARRIER();
460 #endif
461 }
462
463 /* Timed-out lock operation; returns 0 to indicate timeout. */
464
465 int __pthread_alt_timedlock(struct _pthread_fastlock * lock,
466                             pthread_descr self, const struct timespec *abstime)
467 {
468   long oldstatus = 0;
469 #if defined HAS_COMPARE_AND_SWAP
470   long newstatus;
471 #endif
472   struct wait_node *p_wait_node = wait_node_alloc();
473
474   /* Out of memory, just give up and do ordinary lock. */
475   if (p_wait_node == 0) {
476     __pthread_alt_lock(lock, self);
477     return 1;
478   }
479
480 #if defined TEST_FOR_COMPARE_AND_SWAP
481   if (!__pthread_has_cas)
482 #endif
483 #if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
484   {
485     __pthread_acquire(&lock->__spinlock);
486
487     if (lock->__status == 0)
488       lock->__status = 1;
489     else {
490       if (self == NULL)
491         self = thread_self();
492
493       p_wait_node->abandoned = 0;
494       p_wait_node->next = (struct wait_node *) lock->__status;
495       p_wait_node->thr = self;
496       lock->__status = (long) p_wait_node;
497       oldstatus = 1; /* force suspend */
498     }
499
500     __pthread_release(&lock->__spinlock);
501     goto suspend;
502   }
503 #endif
504
505 #if defined HAS_COMPARE_AND_SWAP
506   do {
507     oldstatus = lock->__status;
508     if (oldstatus == 0) {
509       newstatus = 1;
510     } else {
511       if (self == NULL)
512         self = thread_self();
513       p_wait_node->thr = self;
514       newstatus = (long) p_wait_node;
515     }
516     p_wait_node->abandoned = 0;
517     p_wait_node->next = (struct wait_node *) oldstatus;
518     /* Make sure the store in wait_node.next completes before performing
519        the compare-and-swap */
520     MEMORY_BARRIER();
521   } while(! __compare_and_swap(&lock->__status, oldstatus, newstatus));
522 #endif
523
524 #if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
525   suspend:
526 #endif
527
528   /* If we did not get the lock, do a timed suspend. If we wake up due
529      to a timeout, then there is a race; the old lock owner may try
530      to remove us from the queue. This race is resolved by us and the owner
531      doing an atomic testandset() to change the state of the wait node from 0
532      to 1. If we succeed, then it's a timeout and we abandon the node in the
533      queue. If we fail, it means the owner gave us the lock. */
534
535   if (oldstatus != 0) {
536     if (timedsuspend(self, abstime) == 0) {
537       if (!testandset(&p_wait_node->abandoned))
538         return 0; /* Timeout! */
539
540       /* Eat oustanding resume from owner, otherwise wait_node_free() below
541          will race with owner's wait_node_dequeue(). */
542       suspend(self);
543     }
544   }
545
546   wait_node_free(p_wait_node);
547
548   READ_MEMORY_BARRIER();
549
550   return 1; /* Got the lock! */
551 }
552
553 void __pthread_alt_unlock(struct _pthread_fastlock *lock)
554 {
555   struct wait_node *p_node, **pp_node, *p_max_prio, **pp_max_prio;
556   struct wait_node ** const pp_head = (struct wait_node **) &lock->__status;
557   int maxprio;
558
559   WRITE_MEMORY_BARRIER();
560
561 #if defined TEST_FOR_COMPARE_AND_SWAP
562   if (!__pthread_has_cas)
563 #endif
564 #if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
565   {
566     __pthread_acquire(&lock->__spinlock);
567   }
568 #endif
569
570   while (1) {
571
572   /* If no threads are waiting for this lock, try to just
573      atomically release it. */
574 #if defined TEST_FOR_COMPARE_AND_SWAP
575     if (!__pthread_has_cas)
576 #endif
577 #if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
578     {
579       if (lock->__status == 0 || lock->__status == 1) {
580         lock->__status = 0;
581         break;
582       }
583     }
584 #endif
585
586 #if defined TEST_FOR_COMPARE_AND_SWAP
587     else
588 #endif
589
590 #if defined HAS_COMPARE_AND_SWAP
591     {
592       long oldstatus = lock->__status;
593       if (oldstatus == 0 || oldstatus == 1) {
594         if (__compare_and_swap_with_release_semantics (&lock->__status, oldstatus, 0))
595           break;
596         else
597           continue;
598       }
599     }
600 #endif
601
602     /* Process the entire queue of wait nodes. Remove all abandoned
603        wait nodes and put them into the global free queue, and
604        remember the one unabandoned node which refers to the thread
605        having the highest priority. */
606
607     pp_max_prio = pp_node = pp_head;
608     p_max_prio = p_node = *pp_head;
609     maxprio = INT_MIN;
610
611     READ_MEMORY_BARRIER(); /* Prevent access to stale data through p_node */
612
613     while (p_node != (struct wait_node *) 1) {
614       int prio;
615
616       if (p_node->abandoned) {
617         /* Remove abandoned node. */
618 #if defined TEST_FOR_COMPARE_AND_SWAP
619         if (!__pthread_has_cas)
620 #endif
621 #if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
622           *pp_node = p_node->next;
623 #endif
624 #if defined TEST_FOR_COMPARE_AND_SWAP
625         else
626 #endif
627 #if defined HAS_COMPARE_AND_SWAP
628           wait_node_dequeue(pp_head, pp_node, p_node);
629 #endif
630         wait_node_free(p_node);
631         /* Note that the next assignment may take us to the beginning
632            of the queue, to newly inserted nodes, if pp_node == pp_head.
633            In that case we need a memory barrier to stabilize the first of
634            these new nodes. */
635         p_node = *pp_node;
636         if (pp_node == pp_head)
637           READ_MEMORY_BARRIER(); /* No stale reads through p_node */
638         continue;
639       } else if ((prio = p_node->thr->p_priority) >= maxprio) {
640         /* Otherwise remember it if its thread has a higher or equal priority
641            compared to that of any node seen thus far. */
642         maxprio = prio;
643         pp_max_prio = pp_node;
644         p_max_prio = p_node;
645       }
646
647       /* This canno6 jump backward in the list, so no further read
648          barrier is needed. */
649       pp_node = &p_node->next;
650       p_node = *pp_node;
651     }
652
653     /* If all threads abandoned, go back to top */
654     if (maxprio == INT_MIN)
655       continue;
656
657     ASSERT (p_max_prio != (struct wait_node *) 1);
658
659     /* Now we want to to remove the max priority thread's wait node from
660        the list. Before we can do this, we must atomically try to change the
661        node's abandon state from zero to nonzero. If we succeed, that means we
662        have the node that we will wake up. If we failed, then it means the
663        thread timed out and abandoned the node in which case we repeat the
664        whole unlock operation. */
665
666     if (!testandset(&p_max_prio->abandoned)) {
667 #if defined TEST_FOR_COMPARE_AND_SWAP
668       if (!__pthread_has_cas)
669 #endif
670 #if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
671         *pp_max_prio = p_max_prio->next;
672 #endif
673 #if defined TEST_FOR_COMPARE_AND_SWAP
674       else
675 #endif
676 #if defined HAS_COMPARE_AND_SWAP
677         wait_node_dequeue(pp_head, pp_max_prio, p_max_prio);
678 #endif
679       restart(p_max_prio->thr);
680       break;
681     }
682   }
683
684 #if defined TEST_FOR_COMPARE_AND_SWAP
685   if (!__pthread_has_cas)
686 #endif
687 #if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
688   {
689     __pthread_release(&lock->__spinlock);
690   }
691 #endif
692 }
693
694
695 /* Compare-and-swap emulation with a spinlock */
696
697 #ifdef TEST_FOR_COMPARE_AND_SWAP
698 int __pthread_has_cas = 0;
699 #endif
700
701 #if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
702
703 int __pthread_compare_and_swap(long * ptr, long oldval, long newval,
704                                int * spinlock)
705 {
706   int res;
707
708   __pthread_acquire(spinlock);
709
710   if (*ptr == oldval) {
711     *ptr = newval; res = 1;
712   } else {
713     res = 0;
714   }
715
716   __pthread_release(spinlock);
717
718   return res;
719 }
720
721 /* This function is called if the inlined test-and-set
722    in __pthread_compare_and_swap() failed */
723
724 /* The retry strategy is as follows:
725    - We test and set the spinlock MAX_SPIN_COUNT times, calling
726      sched_yield() each time.  This gives ample opportunity for other
727      threads with priority >= our priority to make progress and
728      release the spinlock.
729    - If a thread with priority < our priority owns the spinlock,
730      calling sched_yield() repeatedly is useless, since we're preventing
731      the owning thread from making progress and releasing the spinlock.
732      So, after MAX_SPIN_LOCK attemps, we suspend the calling thread
733      using nanosleep().  This again should give time to the owning thread
734      for releasing the spinlock.
735      Notice that the nanosleep() interval must not be too small,
736      since the kernel does busy-waiting for short intervals in a realtime
737      process (!).  The smallest duration that guarantees thread
738      suspension is currently 2ms.
739    - When nanosleep() returns, we try again, doing MAX_SPIN_COUNT
740      sched_yield(), then sleeping again if needed. */
741
742 static void __pthread_acquire(int * spinlock)
743 {
744   int cnt = 0;
745   struct timespec tm;
746
747   READ_MEMORY_BARRIER();
748
749   while (testandset(spinlock)) {
750     if (cnt < MAX_SPIN_COUNT) {
751       sched_yield();
752       cnt++;
753     } else {
754       tm.tv_sec = 0;
755       tm.tv_nsec = SPIN_SLEEP_DURATION;
756       nanosleep(&tm, NULL);
757       cnt = 0;
758     }
759   }
760 }
761
762 #endif