OSDN Git Service

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