2013-02-11 Sebastian Huber <sebastian.huber@embedded-brains.de>
[official-gcc.git] / libgo / runtime / sema.goc
blob4622f6c8a61ad54fa8fcbbf82823bf36e703508a
1 // Copyright 2009 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.
5 // Semaphore implementation exposed to Go.
6 // Intended use is provide a sleep and wakeup
7 // primitive that can be used in the contended case
8 // of other synchronization primitives.
9 // Thus it targets the same goal as Linux's futex,
10 // but it has much simpler semantics.
12 // That is, don't think of these as semaphores.
13 // Think of them as a way to implement sleep and wakeup
14 // such that every sleep is paired with a single wakeup,
15 // even if, due to races, the wakeup happens before the sleep.
17 // See Mullender and Cox, ``Semaphores in Plan 9,''
18 // http://swtch.com/semaphore.pdf
20 package sync
21 #include "runtime.h"
22 #include "arch.h"
24 typedef struct Sema Sema;
25 struct Sema
27         uint32 volatile*        addr;
28         G*      g;
29         int64   releasetime;
30         Sema*   prev;
31         Sema*   next;
34 typedef struct SemaRoot SemaRoot;
35 struct SemaRoot
37         Lock;
38         Sema*   head;
39         Sema*   tail;
40         // Number of waiters. Read w/o the lock.
41         uint32 volatile nwait;
44 // Prime to not correlate with any user patterns.
45 #define SEMTABLESZ 251
47 union semtable
49         SemaRoot;
50         uint8 pad[CacheLineSize];
52 static union semtable semtable[SEMTABLESZ];
54 static SemaRoot*
55 semroot(uint32 volatile *addr)
57         return &semtable[((uintptr)addr >> 3) % SEMTABLESZ];
60 static void
61 semqueue(SemaRoot *root, uint32 volatile *addr, Sema *s)
63         s->g = runtime_g();
64         s->addr = addr;
65         s->next = nil;
66         s->prev = root->tail;
67         if(root->tail)
68                 root->tail->next = s;
69         else
70                 root->head = s;
71         root->tail = s;
74 static void
75 semdequeue(SemaRoot *root, Sema *s)
77         if(s->next)
78                 s->next->prev = s->prev;
79         else
80                 root->tail = s->prev;
81         if(s->prev)
82                 s->prev->next = s->next;
83         else
84                 root->head = s->next;
85         s->prev = nil;
86         s->next = nil;
89 static int32
90 cansemacquire(uint32 volatile *addr)
92         uint32 v;
94         while((v = runtime_atomicload(addr)) > 0)
95                 if(runtime_cas(addr, v, v-1))
96                         return 1;
97         return 0;
100 static void
101 semacquireimpl(uint32 volatile *addr, int32 profile)
103         Sema s; // Needs to be allocated on stack, otherwise garbage collector could deallocate it
104         SemaRoot *root;
105         int64 t0;
106         
107         // Easy case.
108         if(cansemacquire(addr))
109                 return;
111         // Harder case:
112         //      increment waiter count
113         //      try cansemacquire one more time, return if succeeded
114         //      enqueue itself as a waiter
115         //      sleep
116         //      (waiter descriptor is dequeued by signaler)
117         root = semroot(addr);
118         t0 = 0;
119         s.releasetime = 0;
120         if(profile && runtime_blockprofilerate > 0) {
121                 t0 = runtime_cputicks();
122                 s.releasetime = -1;
123         }
124         for(;;) {
126                 runtime_lock(root);
127                 // Add ourselves to nwait to disable "easy case" in semrelease.
128                 runtime_xadd(&root->nwait, 1);
129                 // Check cansemacquire to avoid missed wakeup.
130                 if(cansemacquire(addr)) {
131                         runtime_xadd(&root->nwait, -1);
132                         runtime_unlock(root);
133                         return;
134                 }
135                 // Any semrelease after the cansemacquire knows we're waiting
136                 // (we set nwait above), so go to sleep.
137                 semqueue(root, addr, &s);
138                 runtime_park(runtime_unlock, root, "semacquire");
139                 if(cansemacquire(addr)) {
140                         if(t0)
141                                 runtime_blockevent(s.releasetime - t0, 3);
142                         return;
143                 }
144         }
147 void
148 runtime_semacquire(uint32 volatile *addr)
150         semacquireimpl(addr, 0);
153 void
154 runtime_semrelease(uint32 volatile *addr)
156         Sema *s;
157         SemaRoot *root;
159         root = semroot(addr);
160         runtime_xadd(addr, 1);
162         // Easy case: no waiters?
163         // This check must happen after the xadd, to avoid a missed wakeup
164         // (see loop in semacquire).
165         if(runtime_atomicload(&root->nwait) == 0)
166                 return;
168         // Harder case: search for a waiter and wake it.
169         runtime_lock(root);
170         if(runtime_atomicload(&root->nwait) == 0) {
171                 // The count is already consumed by another goroutine,
172                 // so no need to wake up another goroutine.
173                 runtime_unlock(root);
174                 return;
175         }
176         for(s = root->head; s; s = s->next) {
177                 if(s->addr == addr) {
178                         runtime_xadd(&root->nwait, -1);
179                         semdequeue(root, s);
180                         break;
181                 }
182         }
183         runtime_unlock(root);
184         if(s) {
185                 if(s->releasetime)
186                         s->releasetime = runtime_cputicks();
187                 runtime_ready(s->g);
188         }
191 func runtime_Semacquire(addr *uint32) {
192         semacquireimpl(addr, 1);
195 func runtime_Semrelease(addr *uint32) {
196         runtime_semrelease(addr);