OSDN Git Service

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