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