OSDN Git Service

PR go/48501
[pf3gnuchains/gcc-fork.git] / libgo / runtime / lock_futex.c
1 // Copyright 2011 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 // +build freebsd linux
6
7 #include "runtime.h"
8
9 // This implementation depends on OS-specific implementations of
10 //
11 //      runtime_futexsleep(uint32 *addr, uint32 val, int64 ns)
12 //              Atomically,
13 //                      if(*addr == val) sleep
14 //              Might be woken up spuriously; that's allowed.
15 //              Don't sleep longer than ns; ns < 0 means forever.
16 //
17 //      runtime_futexwakeup(uint32 *addr, uint32 cnt)
18 //              If any procs are sleeping on addr, wake up at most cnt.
19
20 enum
21 {
22         MUTEX_UNLOCKED = 0,
23         MUTEX_LOCKED = 1,
24         MUTEX_SLEEPING = 2,
25
26         ACTIVE_SPIN = 4,
27         ACTIVE_SPIN_CNT = 30,
28         PASSIVE_SPIN = 1,
29 };
30
31 // Possible lock states are MUTEX_UNLOCKED, MUTEX_LOCKED and MUTEX_SLEEPING.
32 // MUTEX_SLEEPING means that there is presumably at least one sleeping thread.
33 // Note that there can be spinning threads during all states - they do not
34 // affect mutex's state.
35 void
36 runtime_lock(Lock *l)
37 {
38         uint32 i, v, wait, spin;
39
40         if(runtime_m()->locks++ < 0)
41                 runtime_throw("runtime_lock: lock count");
42
43         // Speculative grab for lock.
44         v = runtime_xchg(&l->key, MUTEX_LOCKED);
45         if(v == MUTEX_UNLOCKED)
46                 return;
47
48         // wait is either MUTEX_LOCKED or MUTEX_SLEEPING
49         // depending on whether there is a thread sleeping
50         // on this mutex.  If we ever change l->key from
51         // MUTEX_SLEEPING to some other value, we must be
52         // careful to change it back to MUTEX_SLEEPING before
53         // returning, to ensure that the sleeping thread gets
54         // its wakeup call.
55         wait = v;
56
57         // On uniprocessor's, no point spinning.
58         // On multiprocessors, spin for ACTIVE_SPIN attempts.
59         spin = 0;
60         if(runtime_ncpu > 1)
61                 spin = ACTIVE_SPIN;
62
63         for(;;) {
64                 // Try for lock, spinning.
65                 for(i = 0; i < spin; i++) {
66                         while(l->key == MUTEX_UNLOCKED)
67                                 if(runtime_cas(&l->key, MUTEX_UNLOCKED, wait))
68                                         return;
69                         runtime_procyield(ACTIVE_SPIN_CNT);
70                 }
71
72                 // Try for lock, rescheduling.
73                 for(i=0; i < PASSIVE_SPIN; i++) {
74                         while(l->key == MUTEX_UNLOCKED)
75                                 if(runtime_cas(&l->key, MUTEX_UNLOCKED, wait))
76                                         return;
77                         runtime_osyield();
78                 }
79
80                 // Sleep.
81                 v = runtime_xchg(&l->key, MUTEX_SLEEPING);
82                 if(v == MUTEX_UNLOCKED)
83                         return;
84                 wait = MUTEX_SLEEPING;
85                 runtime_futexsleep(&l->key, MUTEX_SLEEPING, -1);
86         }
87 }
88
89 void
90 runtime_unlock(Lock *l)
91 {
92         uint32 v;
93
94         if(--runtime_m()->locks < 0)
95                 runtime_throw("runtime_unlock: lock count");
96
97         v = runtime_xchg(&l->key, MUTEX_UNLOCKED);
98         if(v == MUTEX_UNLOCKED)
99                 runtime_throw("unlock of unlocked lock");
100         if(v == MUTEX_SLEEPING)
101                 runtime_futexwakeup(&l->key, 1);
102 }
103
104 // One-time notifications.
105 void
106 runtime_noteclear(Note *n)
107 {
108         n->key = 0;
109 }
110
111 void
112 runtime_notewakeup(Note *n)
113 {
114         runtime_xchg(&n->key, 1);
115         runtime_futexwakeup(&n->key, 1);
116 }
117
118 void
119 runtime_notesleep(Note *n)
120 {
121         while(runtime_atomicload(&n->key) == 0)
122                 runtime_futexsleep(&n->key, 0, -1);
123 }
124
125 void
126 runtime_notetsleep(Note *n, int64 ns)
127 {
128         int64 deadline, now;
129
130         if(ns < 0) {
131                 runtime_notesleep(n);
132                 return;
133         }
134
135         if(runtime_atomicload(&n->key) != 0)
136                 return;
137
138         deadline = runtime_nanotime() + ns;
139         for(;;) {
140                 runtime_futexsleep(&n->key, 0, ns);
141                 if(runtime_atomicload(&n->key) != 0)
142                         return;
143                 now = runtime_nanotime();
144                 if(now >= deadline)
145                         return;
146                 ns = deadline - now;
147         }
148 }