mirror of https://github.com/golang/go.git
add mutex.Mutex
R=r DELTA=349 (348 added, 0 deleted, 1 changed) OCL=20380 CL=20472
This commit is contained in:
parent
3e8faa640f
commit
bf3dd3f0ef
|
|
@ -3,7 +3,7 @@
|
|||
// license that can be found in the LICENSE file.
|
||||
|
||||
|
||||
package SYS // rename to avoid redeclaration
|
||||
package SYS // rename to avoid redeclaration errors
|
||||
|
||||
export func mal(int32) *any;
|
||||
export func breakpoint();
|
||||
|
|
@ -92,3 +92,5 @@ export func exit(int);
|
|||
|
||||
export func symdat() (symtab *[]byte, pclntab *[]byte);
|
||||
|
||||
export func semacquire(sema *int32);
|
||||
export func semrelease(sema *int32);
|
||||
|
|
|
|||
|
|
@ -71,5 +71,7 @@ char *sysimport =
|
|||
"export func sys.stringtorune (? string, ? int) (? int, ? int)\n"
|
||||
"export func sys.exit (? int)\n"
|
||||
"export func sys.symdat () (symtab *[]uint8, pclntab *[]uint8)\n"
|
||||
"export func sys.semacquire (sema *int32)\n"
|
||||
"export func sys.semrelease (sema *int32)\n"
|
||||
"\n"
|
||||
"$$\n";
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ DIRS=\
|
|||
reflect\
|
||||
regexp\
|
||||
strconv\
|
||||
sync\
|
||||
tabwriter\
|
||||
time\
|
||||
|
||||
|
|
@ -95,4 +96,5 @@ reflect.dirinstall: strconv.dirinstall
|
|||
strconv.dirinstall: os.dirinstall utf8.install
|
||||
tabwriter.dirinstall: os.dirinstall io.dirinstall container/array.dirinstall
|
||||
time.dirinstall: once.install os.dirinstall
|
||||
sync.dirinstall:
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,56 @@
|
|||
# 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.
|
||||
|
||||
# DO NOT EDIT. Automatically generated by gobuild.
|
||||
# gobuild -m >Makefile
|
||||
O=6
|
||||
GC=$(O)g
|
||||
CC=$(O)c -w
|
||||
AS=$(O)a
|
||||
AR=$(O)ar
|
||||
|
||||
default: packages
|
||||
|
||||
clean:
|
||||
rm -f *.$O *.a $O.out
|
||||
|
||||
test: packages
|
||||
gotest
|
||||
|
||||
coverage: packages
|
||||
gotest
|
||||
6cov -g `pwd` | grep -v '_test\.go:'
|
||||
|
||||
%.$O: %.go
|
||||
$(GC) $*.go
|
||||
|
||||
%.$O: %.c
|
||||
$(CC) $*.c
|
||||
|
||||
%.$O: %.s
|
||||
$(AS) $*.s
|
||||
|
||||
O1=\
|
||||
asm_$(GOARCH).$O\
|
||||
mutex.$O\
|
||||
|
||||
sync.a: a1
|
||||
|
||||
a1: $(O1)
|
||||
$(AR) grc sync.a asm_$(GOARCH).$O mutex.$O
|
||||
rm -f $(O1)
|
||||
|
||||
newpkg: clean
|
||||
$(AR) grc sync.a
|
||||
|
||||
$(O1): newpkg
|
||||
|
||||
nuke: clean
|
||||
rm -f $(GOROOT)/pkg/sync.a
|
||||
|
||||
packages: sync.a
|
||||
|
||||
install: packages
|
||||
cp sync.a $(GOROOT)/pkg/sync.a
|
||||
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
// 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.
|
||||
|
||||
// func cas(val *int32, old, new int32) bool
|
||||
// Atomically:
|
||||
// if *val == old {
|
||||
// *val = new;
|
||||
// return true;
|
||||
// }else
|
||||
// return false;
|
||||
TEXT sync·cas(SB), 7, $0
|
||||
MOVQ 8(SP), BX
|
||||
MOVL 16(SP), AX
|
||||
MOVL 20(SP), CX
|
||||
LOCK
|
||||
CMPXCHGL CX, 0(BX)
|
||||
JZ ok
|
||||
MOVL $0, 24(SP)
|
||||
RET
|
||||
ok:
|
||||
MOVL $1, 24(SP)
|
||||
RET
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
// 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.
|
||||
|
||||
package sync
|
||||
|
||||
package func cas(val *int32, old, new int32) bool
|
||||
|
||||
export type Mutex struct {
|
||||
key int32;
|
||||
sema int32;
|
||||
}
|
||||
|
||||
func xadd(val *int32, delta int32) (new int32) {
|
||||
for {
|
||||
v := *val;
|
||||
if cas(val, v, v+delta) {
|
||||
return v+delta;
|
||||
}
|
||||
}
|
||||
panic("unreached")
|
||||
}
|
||||
|
||||
func (m *Mutex) Lock() {
|
||||
if xadd(&m.key, 1) == 1 {
|
||||
// changed from 0 to 1; we hold lock
|
||||
return;
|
||||
}
|
||||
sys.semacquire(&m.sema);
|
||||
}
|
||||
|
||||
func (m *Mutex) Unlock() {
|
||||
if xadd(&m.key, -1) == 0 {
|
||||
// changed from 1 to 0; no contention
|
||||
return;
|
||||
}
|
||||
sys.semrelease(&m.sema);
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
// 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.
|
||||
|
||||
// GOMAXPROCS=10 gotest
|
||||
|
||||
package sync
|
||||
|
||||
import (
|
||||
"sync";
|
||||
"testing"
|
||||
)
|
||||
|
||||
func HammerSemaphore(s *int32, cdone *chan bool) {
|
||||
for i := 0; i < 1000; i++ {
|
||||
sys.semacquire(s);
|
||||
sys.semrelease(s);
|
||||
}
|
||||
cdone <- true;
|
||||
}
|
||||
|
||||
export func TestSemaphore(t *testing.T) {
|
||||
s := new(int32);
|
||||
*s = 1;
|
||||
c := new(chan bool);
|
||||
for i := 0; i < 10; i++ {
|
||||
go HammerSemaphore(s, c);
|
||||
}
|
||||
for i := 0; i < 10; i++ {
|
||||
<-c;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func HammerMutex(m *Mutex, cdone *chan bool) {
|
||||
for i := 0; i < 1000; i++ {
|
||||
m.Lock();
|
||||
m.Unlock();
|
||||
}
|
||||
cdone <- true;
|
||||
}
|
||||
|
||||
export func TestMutex(t *testing.T) {
|
||||
m := new(Mutex);
|
||||
c := new(chan bool);
|
||||
for i := 0; i < 10; i++ {
|
||||
go HammerMutex(m, c);
|
||||
}
|
||||
for i := 0; i < 10; i++ {
|
||||
<-c;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -34,6 +34,12 @@ maketest \
|
|||
|
||||
(xcd lib; make test) || exit $?
|
||||
|
||||
(xcd lib/sync;
|
||||
make clean;
|
||||
time make
|
||||
GOMAXPROCS=10 make test
|
||||
) || exit $?
|
||||
|
||||
(xcd ../usr/gri/pretty
|
||||
make clean
|
||||
time make
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ LIBOFILES=\
|
|||
print.$O\
|
||||
rune.$O\
|
||||
proc.$O\
|
||||
sema.$O\
|
||||
stack.$O\
|
||||
string.$O\
|
||||
symtab.$O\
|
||||
|
|
|
|||
|
|
@ -0,0 +1,178 @@
|
|||
// 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
|
||||
|
||||
#include "runtime.h"
|
||||
|
||||
typedef struct Sema Sema;
|
||||
struct Sema
|
||||
{
|
||||
uint32 *addr;
|
||||
G *g;
|
||||
Sema *prev;
|
||||
Sema *next;
|
||||
};
|
||||
|
||||
// TODO: For now, a linked list; maybe a hash table of linked lists later.
|
||||
static Sema *semfirst, *semlast;
|
||||
static Lock semlock;
|
||||
|
||||
static void
|
||||
semqueue(uint32 *addr, Sema *s)
|
||||
{
|
||||
s->addr = addr;
|
||||
s->g = nil;
|
||||
|
||||
lock(&semlock);
|
||||
s->prev = semlast;
|
||||
s->next = nil;
|
||||
if(semlast)
|
||||
semlast->next = s;
|
||||
else
|
||||
semfirst = s;
|
||||
semlast = s;
|
||||
unlock(&semlock);
|
||||
}
|
||||
|
||||
static void
|
||||
semdequeue(Sema *s)
|
||||
{
|
||||
lock(&semlock);
|
||||
if(s->next)
|
||||
s->next->prev = s->prev;
|
||||
else
|
||||
semlast = s->prev;
|
||||
if(s->prev)
|
||||
s->prev->next = s->next;
|
||||
else
|
||||
semfirst = s->next;
|
||||
s->prev = nil;
|
||||
s->next = nil;
|
||||
unlock(&semlock);
|
||||
}
|
||||
|
||||
static void
|
||||
semwakeup(uint32 *addr)
|
||||
{
|
||||
Sema *s;
|
||||
|
||||
lock(&semlock);
|
||||
for(s=semfirst; s; s=s->next) {
|
||||
if(s->addr == addr && s->g) {
|
||||
ready(s->g);
|
||||
s->g = nil;
|
||||
break;
|
||||
}
|
||||
}
|
||||
unlock(&semlock);
|
||||
}
|
||||
|
||||
// Step 1 of sleep: make ourselves available for wakeup.
|
||||
// TODO(rsc): Maybe we can write a version without
|
||||
// locks by using cas on s->g. Maybe not: I need to
|
||||
// think more about whether it would be correct.
|
||||
static void
|
||||
semsleep1(Sema *s)
|
||||
{
|
||||
lock(&semlock);
|
||||
s->g = g;
|
||||
unlock(&semlock);
|
||||
}
|
||||
|
||||
// Decided not to go through with it: undo step 1.
|
||||
static void
|
||||
semsleepundo1(Sema *s)
|
||||
{
|
||||
lock(&semlock);
|
||||
if(s->g != nil) {
|
||||
s->g = nil; // back ourselves out
|
||||
} else {
|
||||
// If s->g == nil already, semwakeup
|
||||
// already readied us. Since we never stopped
|
||||
// running, readying us just set g->readyonstop.
|
||||
// Clear it.
|
||||
if(g->readyonstop == 0)
|
||||
*(int32*)0x555 = 555;
|
||||
g->readyonstop = 0;
|
||||
}
|
||||
unlock(&semlock);
|
||||
}
|
||||
|
||||
// Step 2: wait for the wakeup.
|
||||
static void
|
||||
semsleep2(Sema *s)
|
||||
{
|
||||
USED(s);
|
||||
g->status = Gwaiting;
|
||||
sys·gosched();
|
||||
}
|
||||
|
||||
static int32
|
||||
cansemacquire(uint32 *addr)
|
||||
{
|
||||
uint32 v;
|
||||
|
||||
while((v = *addr) > 0)
|
||||
if(cas(addr, v, v-1))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// func sys.semacquire(addr *uint32)
|
||||
// For now has no return value.
|
||||
// Might return an ok (not interrupted) bool in the future?
|
||||
void
|
||||
sys·semacquire(uint32 *addr)
|
||||
{
|
||||
Sema s;
|
||||
|
||||
// Easy case.
|
||||
if(cansemacquire(addr))
|
||||
return;
|
||||
|
||||
// Harder case:
|
||||
// queue
|
||||
// try semacquire one more time, sleep if failed
|
||||
// dequeue
|
||||
// wake up one more guy to avoid races (TODO(rsc): maybe unnecessary?)
|
||||
semqueue(addr, &s);
|
||||
for(;;) {
|
||||
semsleep1(&s);
|
||||
if(cansemacquire(addr)) {
|
||||
semsleepundo1(&s);
|
||||
break;
|
||||
}
|
||||
semsleep2(&s);
|
||||
}
|
||||
semdequeue(&s);
|
||||
semwakeup(addr);
|
||||
}
|
||||
|
||||
// func sys.semrelease(addr *uint32)
|
||||
void
|
||||
sys·semrelease(uint32 *addr)
|
||||
{
|
||||
uint32 v;
|
||||
|
||||
for(;;) {
|
||||
v = *addr;
|
||||
if(cas(addr, v, v+1))
|
||||
break;
|
||||
}
|
||||
semwakeup(addr);
|
||||
}
|
||||
Loading…
Reference in New Issue