From a6950fe0f9c23fe681109db746038092b5a04728 Mon Sep 17 00:00:00 2001 From: Dmitriy Vyukov Date: Sun, 24 Aug 2014 12:41:23 +0400 Subject: [PATCH] runtime: convert synchronous semaphores to Go LGTM=rsc R=golang-codereviews, khr, rsc CC=golang-codereviews, rlh https://golang.org/cl/130340043 --- src/pkg/runtime/sema.go | 117 +++++++++++++++++++++++++++++++++++++++ src/pkg/runtime/sema.goc | 90 ------------------------------ src/pkg/runtime/thunk.s | 21 +++++++ src/pkg/sync/runtime.go | 8 ++- 4 files changed, 144 insertions(+), 92 deletions(-) create mode 100644 src/pkg/runtime/sema.go create mode 100644 src/pkg/runtime/thunk.s diff --git a/src/pkg/runtime/sema.go b/src/pkg/runtime/sema.go new file mode 100644 index 00000000000..ac855f9d8c6 --- /dev/null +++ b/src/pkg/runtime/sema.go @@ -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") + } +} diff --git a/src/pkg/runtime/sema.goc b/src/pkg/runtime/sema.goc index 7b1f8f2ed3e..7724566a1ee 100644 --- a/src/pkg/runtime/sema.goc +++ b/src/pkg/runtime/sema.goc @@ -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); -} diff --git a/src/pkg/runtime/thunk.s b/src/pkg/runtime/thunk.s new file mode 100644 index 00000000000..2a48f5ff237 --- /dev/null +++ b/src/pkg/runtime/thunk.s @@ -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) diff --git a/src/pkg/sync/runtime.go b/src/pkg/sync/runtime.go index 3bf47ea52aa..3b866303a96 100644 --- a/src/pkg/sync/runtime.go +++ b/src/pkg/sync/runtime.go @@ -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)