1
0
mirror of https://github.com/golang/go synced 2024-11-19 02:04:42 -07:00

runtime: convert synchronous semaphores to Go

LGTM=rsc
R=golang-codereviews, khr, rsc
CC=golang-codereviews, rlh
https://golang.org/cl/130340043
This commit is contained in:
Dmitriy Vyukov 2014-08-24 12:41:23 +04:00
parent 9a1e142bbc
commit a6950fe0f9
4 changed files with 144 additions and 92 deletions

117
src/pkg/runtime/sema.go Normal file
View File

@ -0,0 +1,117 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Semaphore implementation exposed to Go.
// Intended use is provide a sleep and wakeup
// primitive that can be used in the contended case
// of other synchronization primitives.
// Thus it targets the same goal as Linux's futex,
// but it has much simpler semantics.
//
// That is, don't think of these as semaphores.
// Think of them as a way to implement sleep and wakeup
// such that every sleep is paired with a single wakeup,
// even if, due to races, the wakeup happens before the sleep.
//
// See Mullender and Cox, ``Semaphores in Plan 9,''
// http://swtch.com/semaphore.pdf
package runtime
import "unsafe"
// Synchronous semaphore for sync.Cond.
type syncSema struct {
lock lock
head *sudog
tail *sudog
}
// Syncsemacquire waits for a pairing syncsemrelease on the same semaphore s.
func syncsemacquire(s *syncSema) {
golock(&s.lock)
if s.head != nil && s.head.nrelease > 0 {
// Have pending release, consume it.
var wake *sudog
s.head.nrelease--
if s.head.nrelease == 0 {
wake = s.head
s.head = wake.link
if s.head == nil {
s.tail = nil
}
}
gounlock(&s.lock)
if wake != nil {
goready(wake.g)
}
} else {
// Enqueue itself.
w := acquireSudog()
w.g = getg()
w.nrelease = -1
w.link = nil
w.releasetime = 0
t0 := int64(0)
if blockprofilerate > 0 {
t0 = gocputicks()
w.releasetime = -1
}
if s.tail == nil {
s.head = w
} else {
s.tail.link = w
}
s.tail = w
goparkunlock(&s.lock, "semacquire")
if t0 != 0 {
goblockevent(int64(w.releasetime)-t0, 2)
}
releaseSudog(w)
}
}
// Syncsemrelease waits for n pairing syncsemacquire on the same semaphore s.
func syncsemrelease(s *syncSema, n uint32) {
golock(&s.lock)
for n > 0 && s.head != nil && s.head.nrelease < 0 {
// Have pending acquire, satisfy it.
wake := s.head
s.head = wake.link
if s.head == nil {
s.tail = nil
}
if wake.releasetime != 0 {
// TODO: Remove use of unsafe here.
releasetimep := (*int64)(unsafe.Pointer(&wake.releasetime))
*releasetimep = gocputicks()
}
goready(wake.g)
n--
}
if n > 0 {
// enqueue itself
w := acquireSudog()
w.g = getg()
w.nrelease = int32(n)
w.link = nil
w.releasetime = 0
if s.tail == nil {
s.head = w
} else {
s.tail.link = w
}
s.tail = w
goparkunlock(&s.lock, "semarelease")
} else {
gounlock(&s.lock)
}
}
func syncsemcheck(sz uintptr) {
if sz != unsafe.Sizeof(syncSema{}) {
print("runtime: bad syncSema size - sync=", sz, " runtime=", unsafe.Sizeof(syncSema{}), "\n")
gothrow("bad syncSema size")
}
}

View File

@ -202,93 +202,3 @@ func runtime_Semacquire(addr *uint32) {
func runtime_Semrelease(addr *uint32) {
runtime·semrelease(addr);
}
typedef struct SyncSema SyncSema;
struct SyncSema
{
Lock lock;
SemaWaiter* head;
SemaWaiter* tail;
};
func runtime_Syncsemcheck(size uintptr) {
if(size != sizeof(SyncSema)) {
runtime·printf("bad SyncSema size: sync:%D runtime:%D\n", (int64)size, (int64)sizeof(SyncSema));
runtime·throw("bad SyncSema size");
}
}
// Syncsemacquire waits for a pairing Syncsemrelease on the same semaphore s.
func runtime_Syncsemacquire(s *SyncSema) {
SemaWaiter w, *wake;
int64 t0;
w.g = g;
w.nrelease = -1;
w.next = nil;
w.releasetime = 0;
t0 = 0;
if(runtime·blockprofilerate > 0) {
t0 = runtime·cputicks();
w.releasetime = -1;
}
runtime·lock(&s->lock);
if(s->head && s->head->nrelease > 0) {
// have pending release, consume it
wake = nil;
s->head->nrelease--;
if(s->head->nrelease == 0) {
wake = s->head;
s->head = wake->next;
if(s->head == nil)
s->tail = nil;
}
runtime·unlock(&s->lock);
if(wake)
runtime·ready(wake->g);
} else {
// enqueue itself
if(s->tail == nil)
s->head = &w;
else
s->tail->next = &w;
s->tail = &w;
runtime·parkunlock(&s->lock, runtime·gostringnocopy((byte*)"semacquire"));
if(t0)
runtime·blockevent(w.releasetime - t0, 2);
}
}
// Syncsemrelease waits for n pairing Syncsemacquire on the same semaphore s.
func runtime_Syncsemrelease(s *SyncSema, n uint32) {
SemaWaiter w, *wake;
w.g = g;
w.nrelease = (int32)n;
w.next = nil;
w.releasetime = 0;
runtime·lock(&s->lock);
while(w.nrelease > 0 && s->head && s->head->nrelease < 0) {
// have pending acquire, satisfy it
wake = s->head;
s->head = wake->next;
if(s->head == nil)
s->tail = nil;
if(wake->releasetime)
wake->releasetime = runtime·cputicks();
runtime·ready(wake->g);
w.nrelease--;
}
if(w.nrelease > 0) {
// enqueue itself
if(s->tail == nil)
s->head = &w;
else
s->tail->next = &w;
s->tail = &w;
runtime·parkunlock(&s->lock, runtime·gostringnocopy((byte*)"semarelease"));
} else
runtime·unlock(&s->lock);
}

21
src/pkg/runtime/thunk.s Normal file
View File

@ -0,0 +1,21 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file exposes various internal runtime functions to other packages in std lib.
#include "zasm_GOOS_GOARCH.h"
#include "../../cmd/ld/textflag.h"
#ifdef GOARCH_arm
#define JMP B
#endif
TEXT sync·runtime_Syncsemacquire(SB),NOSPLIT,$0-0
JMP runtime·syncsemacquire(SB)
TEXT sync·runtime_Syncsemrelease(SB),NOSPLIT,$0-0
JMP runtime·syncsemrelease(SB)
TEXT sync·runtime_Syncsemcheck(SB),NOSPLIT,$0-0
JMP runtime·syncsemcheck(SB)

View File

@ -19,8 +19,12 @@ func runtime_Semacquire(s *uint32)
// library and should not be used directly.
func runtime_Semrelease(s *uint32)
// Opaque representation of SyncSema in runtime/sema.goc.
type syncSema [3]uintptr
// Approximation of syncSema in runtime/sema.go.
type syncSema struct {
lock uintptr
head unsafe.Pointer
tail unsafe.Pointer
}
// Syncsemacquire waits for a pairing Syncsemrelease on the same semaphore s.
func runtime_Syncsemacquire(s *syncSema)