github.com/xushiwei/go@v0.0.0-20130601165731-2b9d83f45bc9/src/pkg/runtime/sema.goc (about) 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. 4 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. 11 // 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. 16 // 17 // See Mullender and Cox, ``Semaphores in Plan 9,'' 18 // http://swtch.com/semaphore.pdf 19 20 package sync 21 #include "runtime.h" 22 #include "arch_GOARCH.h" 23 24 typedef struct Sema Sema; 25 struct Sema 26 { 27 uint32 volatile* addr; 28 G* g; 29 int64 releasetime; 30 Sema* prev; 31 Sema* next; 32 }; 33 34 typedef struct SemaRoot SemaRoot; 35 struct SemaRoot 36 { 37 Lock; 38 Sema* head; 39 Sema* tail; 40 // Number of waiters. Read w/o the lock. 41 uint32 volatile nwait; 42 }; 43 44 // Prime to not correlate with any user patterns. 45 #define SEMTABLESZ 251 46 47 struct semtable 48 { 49 SemaRoot; 50 uint8 pad[CacheLineSize-sizeof(SemaRoot)]; 51 }; 52 #pragma dataflag 16 /* mark semtable as 'no pointers', hiding from garbage collector */ 53 static struct semtable semtable[SEMTABLESZ]; 54 55 static SemaRoot* 56 semroot(uint32 *addr) 57 { 58 return &semtable[((uintptr)addr >> 3) % SEMTABLESZ]; 59 } 60 61 static void 62 semqueue(SemaRoot *root, uint32 volatile *addr, Sema *s) 63 { 64 s->g = g; 65 s->addr = addr; 66 s->next = nil; 67 s->prev = root->tail; 68 if(root->tail) 69 root->tail->next = s; 70 else 71 root->head = s; 72 root->tail = s; 73 } 74 75 static void 76 semdequeue(SemaRoot *root, Sema *s) 77 { 78 if(s->next) 79 s->next->prev = s->prev; 80 else 81 root->tail = s->prev; 82 if(s->prev) 83 s->prev->next = s->next; 84 else 85 root->head = s->next; 86 s->prev = nil; 87 s->next = nil; 88 } 89 90 static int32 91 cansemacquire(uint32 *addr) 92 { 93 uint32 v; 94 95 while((v = runtime·atomicload(addr)) > 0) 96 if(runtime·cas(addr, v, v-1)) 97 return 1; 98 return 0; 99 } 100 101 static void 102 semacquireimpl(uint32 volatile *addr, int32 profile) 103 { 104 Sema s; // Needs to be allocated on stack, otherwise garbage collector could deallocate it 105 SemaRoot *root; 106 int64 t0; 107 108 // Easy case. 109 if(cansemacquire(addr)) 110 return; 111 112 // Harder case: 113 // increment waiter count 114 // try cansemacquire one more time, return if succeeded 115 // enqueue itself as a waiter 116 // sleep 117 // (waiter descriptor is dequeued by signaler) 118 root = semroot(addr); 119 t0 = 0; 120 s.releasetime = 0; 121 if(profile && runtime·blockprofilerate > 0) { 122 t0 = runtime·cputicks(); 123 s.releasetime = -1; 124 } 125 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 } 145 } 146 147 void 148 runtime·semacquire(uint32 volatile *addr) 149 { 150 semacquireimpl(addr, 0); 151 } 152 153 void 154 runtime·semrelease(uint32 volatile *addr) 155 { 156 Sema *s; 157 SemaRoot *root; 158 159 root = semroot(addr); 160 runtime·xadd(addr, 1); 161 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; 167 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 } 189 } 190 191 func runtime_Semacquire(addr *uint32) { 192 semacquireimpl(addr, 1); 193 } 194 195 func runtime_Semrelease(addr *uint32) { 196 runtime·semrelease(addr); 197 }