OSDN Git Service

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