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