Skip to content

Commit 668cb17

Browse files
committed
init module
1 parent 3196b56 commit 668cb17

20 files changed

+836
-1
lines changed

.travis.yml

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
language: go
2+
go:
3+
- "1.13.x"
4+
- "1.14.x"
5+
- "1.15.x"
6+
- "1.16.x"
7+
8+
script:
9+
- export GOMAXPROCS=4
10+
- export GORACE=halt_on_error=1
11+
- go test -race -v ./...

README.md

+17-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,17 @@
1-
# routine
1+
# routine
2+
3+
# API
4+
5+
## `Goid()`
6+
7+
`Goid()` helps you get the unique `goid` of the current `goroutine` easily.
8+
9+
## `AllGoids()`
10+
11+
`AllGoids()` helps you find all `goid` of alive `goroutine`s easily.
12+
13+
## `LocalStorage`
14+
15+
# License
16+
17+
MIT

g/g.go

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2018 Huan Du. All rights reserved.
2+
// Licensed under the MIT license that can be found in the LICENSE file.
3+
4+
// Package g exposes goroutine struct g to user space.
5+
package g
6+
7+
import (
8+
"unsafe"
9+
)
10+
11+
func getg() unsafe.Pointer
12+
13+
// G returns current g (the goroutine struct) to user space.
14+
func G() unsafe.Pointer {
15+
return getg()
16+
}

g/g_test.go

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright 2018 Huan Du. All rights reserved.
2+
// Licensed under the MIT license that can be found in the LICENSE file.
3+
4+
package g
5+
6+
import (
7+
"testing"
8+
)
9+
10+
func TestG(t *testing.T) {
11+
gp1 := G()
12+
13+
if gp1 == nil {
14+
t.Fatalf("fail to get G.")
15+
}
16+
17+
t.Run("G in another goroutine", func(t *testing.T) {
18+
gp2 := G()
19+
20+
if gp2 == nil {
21+
t.Fatalf("fail to get G.")
22+
}
23+
24+
if gp2 == gp1 {
25+
t.Fatalf("every living G must be different. [gp1:%p] [gp2:%p]", gp1, gp2)
26+
}
27+
})
28+
}

g/getg_386.s

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Copyright 2018 Huan Du. All rights reserved.
2+
// Licensed under the MIT license that can be found in the LICENSE file.
3+
4+
#include "go_asm.h"
5+
#include "go_tls.h"
6+
#include "textflag.h"
7+
8+
TEXT ·getg(SB), NOSPLIT, $0-4
9+
get_tls(CX)
10+
MOVL g(CX), AX
11+
MOVL AX, ret+0(FP)
12+
RET

g/getg_amd64.s

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Copyright 2018 Huan Du. All rights reserved.
2+
// Licensed under the MIT license that can be found in the LICENSE file.
3+
4+
#include "go_asm.h"
5+
#include "go_tls.h"
6+
#include "textflag.h"
7+
8+
TEXT ·getg(SB), NOSPLIT, $0-8
9+
get_tls(CX)
10+
MOVQ g(CX), AX
11+
MOVQ AX, ret+0(FP)
12+
RET

g/getg_amd64p32.s

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// Copyright 2018 Huan Du. All rights reserved.
2+
// Licensed under the MIT license that can be found in the LICENSE file.
3+
4+
#include "getg_amd64.s"

g/getg_arm.s

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Copyright 2018 Huan Du. All rights reserved.
2+
// Licensed under the MIT license that can be found in the LICENSE file.
3+
4+
#include "go_asm.h"
5+
#include "textflag.h"
6+
7+
TEXT ·getg(SB), NOSPLIT, $0-4
8+
MOVW g, R8
9+
MOVW R8, ret+0(FP)
10+
RET

g/getg_arm64.s

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Copyright 2018 Huan Du. All rights reserved.
2+
// Licensed under the MIT license that can be found in the LICENSE file.
3+
4+
#include "go_asm.h"
5+
#include "textflag.h"
6+
7+
TEXT ·getg(SB), NOSPLIT, $0-4
8+
MOVW g, R8
9+
MOVW R8, ret+0(FP)
10+
RET

g/go_tls.h

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright 2014 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+
#ifdef GOARCH_arm
6+
#define LR R14
7+
#endif
8+
9+
#ifdef GOARCH_amd64
10+
#define get_tls(r) MOVQ TLS, r
11+
#define g(r) 0(r)(TLS*1)
12+
#endif
13+
14+
#ifdef GOARCH_amd64p32
15+
#define get_tls(r) MOVL TLS, r
16+
#define g(r) 0(r)(TLS*1)
17+
#endif
18+
19+
#ifdef GOARCH_386
20+
#define get_tls(r) MOVL TLS, r
21+
#define g(r) 0(r)(TLS*1)
22+
#endif

go.mod

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module github.com/go-eden/routine
2+
3+
go 1.16
4+
5+
require github.com/stretchr/testify v1.7.0

routine.s

Whitespace-only changes.

routine_api.go

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package routine
2+
3+
import "fmt"
4+
5+
// LocalStorage provides goroutine-local variables.
6+
type LocalStorage interface {
7+
8+
// Get returns the value in the current goroutine's local storage, if it was set before.
9+
Get() (value interface{})
10+
11+
// Set copy the value into the current goroutine's local storage, and return the old value.
12+
Set(value interface{}) (oldValue interface{})
13+
14+
// Del delete the value from the current goroutine's local storage, and return it.
15+
Del() (oldValue interface{})
16+
17+
// Clear delete values from all goroutine's local storages.
18+
Clear()
19+
}
20+
21+
// ImmutableContext represents all local storages of one goroutine.
22+
type ImmutableContext struct {
23+
gid int64
24+
values map[uintptr]interface{}
25+
}
26+
27+
// Go start an new goroutine, and copy all local storages from current goroutine.
28+
func Go(f func()) {
29+
ic := BackupContext()
30+
go func() {
31+
InheritContext(ic)
32+
f()
33+
}()
34+
}
35+
36+
// BackupContext copy all local storages into an ImmutableContext instance.
37+
func BackupContext() *ImmutableContext {
38+
s := loadCurrentStore()
39+
data := make(map[uintptr]interface{}, len(s.values))
40+
for k, v := range s.values {
41+
data[k] = v
42+
}
43+
return &ImmutableContext{gid: s.gid, values: data}
44+
}
45+
46+
// InheritContext load the specified ImmutableContext instance into the local storage of current goroutine.
47+
func InheritContext(ic *ImmutableContext) {
48+
if ic == nil || ic.values == nil {
49+
return
50+
}
51+
s := loadCurrentStore()
52+
for k, v := range ic.values {
53+
s.values[k] = v
54+
}
55+
}
56+
57+
// NewLocalStorage create and return an new LocalStorage instance.
58+
func NewLocalStorage() LocalStorage {
59+
t := new(storage)
60+
t.Clear()
61+
return t
62+
}
63+
64+
// Goid return the current goroutine's unique id.
65+
// It will try get gid by native cgo/asm for better performance,
66+
// and could parse gid from stack for failover supporting.
67+
func Goid() (id int64) {
68+
var succ bool
69+
if id, succ = getGoidByNative(); !succ {
70+
// no need to warning
71+
id = getGoidByStack()
72+
}
73+
return
74+
}
75+
76+
// AllGoids return all goroutine's goid in the current golang process.
77+
// It will try load all goid from runtime natively for better performance,
78+
// and fallover to runtime.Stack, which is realy inefficient.
79+
func AllGoids() (ids []int64) {
80+
var err error
81+
if ids, err = getAllGoidByNative(); err != nil {
82+
fmt.Println("[WARNING] cannot get all goid from runtime natively, now fallover to stack info, this will be very inefficient!!!")
83+
ids = getAllGoidByStack()
84+
}
85+
return
86+
}

routine_api_test.go

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package routine
2+
3+
import (
4+
"github.com/stretchr/testify/assert"
5+
"testing"
6+
"time"
7+
)
8+
9+
func TestGoid(t *testing.T) {
10+
t.Log(Goid())
11+
}
12+
13+
func TestAllGoid(t *testing.T) {
14+
const num = 10
15+
for i := 0; i < num; i++ {
16+
go func() {
17+
time.Sleep(time.Second)
18+
}()
19+
}
20+
time.Sleep(time.Millisecond)
21+
22+
ids := AllGoids()
23+
t.Log("all gids: ", len(ids), ids)
24+
}
25+
26+
func TestGoStorage(t *testing.T) {
27+
var variable = "hello world"
28+
stg := NewLocalStorage()
29+
stg.Set(variable)
30+
Go(func() {
31+
v := stg.Get()
32+
assert.True(t, v != nil && v.(string) == variable)
33+
})
34+
time.Sleep(time.Millisecond)
35+
stg.Clear()
36+
}
37+
38+
// BenchmarkGoid-12 278801190 4.586 ns/op 0 B/op 0 allocs/op
39+
func BenchmarkGoid(b *testing.B) {
40+
b.ReportAllocs()
41+
b.ResetTimer()
42+
for i := 0; i < b.N; i++ {
43+
_ = Goid()
44+
}
45+
}
46+
47+
// BenchmarkAllGoid-12 5949680 228.3 ns/op 896 B/op 1 allocs/op
48+
func BenchmarkAllGoid(b *testing.B) {
49+
const num = 16
50+
for i := 0; i < num; i++ {
51+
go func() {
52+
time.Sleep(time.Second)
53+
}()
54+
}
55+
56+
b.ReportAllocs()
57+
b.ResetTimer()
58+
for i := 0; i < b.N; i++ {
59+
_ = AllGoids()
60+
}
61+
}

0 commit comments

Comments
 (0)