initial import

This commit is contained in:
kevin
2020-07-26 17:09:05 +08:00
commit 7e3a369a8f
647 changed files with 54754 additions and 0 deletions

View File

@@ -0,0 +1,37 @@
package threading
import "sync"
type RoutineGroup struct {
waitGroup sync.WaitGroup
}
func NewRoutineGroup() *RoutineGroup {
return new(RoutineGroup)
}
// Don't reference the variables from outside,
// because outside variables can be changed by other goroutines
func (g *RoutineGroup) Run(fn func()) {
g.waitGroup.Add(1)
go func() {
defer g.waitGroup.Done()
fn()
}()
}
// Don't reference the variables from outside,
// because outside variables can be changed by other goroutines
func (g *RoutineGroup) RunSafe(fn func()) {
g.waitGroup.Add(1)
GoSafe(func() {
defer g.waitGroup.Done()
fn()
})
}
func (g *RoutineGroup) Wait() {
g.waitGroup.Wait()
}

View File

@@ -0,0 +1,45 @@
package threading
import (
"io/ioutil"
"log"
"sync"
"sync/atomic"
"testing"
"github.com/stretchr/testify/assert"
)
func TestRoutineGroupRun(t *testing.T) {
var count int32
group := NewRoutineGroup()
for i := 0; i < 3; i++ {
group.Run(func() {
atomic.AddInt32(&count, 1)
})
}
group.Wait()
assert.Equal(t, int32(3), count)
}
func TestRoutingGroupRunSafe(t *testing.T) {
log.SetOutput(ioutil.Discard)
var count int32
group := NewRoutineGroup()
var once sync.Once
for i := 0; i < 3; i++ {
group.RunSafe(func() {
once.Do(func() {
panic("")
})
atomic.AddInt32(&count, 1)
})
}
group.Wait()
assert.Equal(t, int32(2), count)
}

View File

@@ -0,0 +1,31 @@
package threading
import (
"bytes"
"runtime"
"strconv"
"zero/core/rescue"
)
func GoSafe(fn func()) {
go RunSafe(fn)
}
// Only for debug, never use it in production
func RoutineId() uint64 {
b := make([]byte, 64)
b = b[:runtime.Stack(b, false)]
b = bytes.TrimPrefix(b, []byte("goroutine "))
b = b[:bytes.IndexByte(b, ' ')]
// if error, just return 0
n, _ := strconv.ParseUint(string(b), 10, 64)
return n
}
func RunSafe(fn func()) {
defer rescue.Recover()
fn()
}

View File

@@ -0,0 +1,37 @@
package threading
import (
"io/ioutil"
"log"
"testing"
"zero/core/lang"
"github.com/stretchr/testify/assert"
)
func TestRoutineId(t *testing.T) {
assert.True(t, RoutineId() > 0)
}
func TestRunSafe(t *testing.T) {
log.SetOutput(ioutil.Discard)
i := 0
defer func() {
assert.Equal(t, 1, i)
}()
ch := make(chan lang.PlaceholderType)
go RunSafe(func() {
defer func() {
ch <- lang.Placeholder
}()
panic("panic")
})
<-ch
i++
}

View File

@@ -0,0 +1,28 @@
package threading
import (
"zero/core/lang"
"zero/core/rescue"
)
type TaskRunner struct {
limitChan chan lang.PlaceholderType
}
func NewTaskRunner(concurrency int) *TaskRunner {
return &TaskRunner{
limitChan: make(chan lang.PlaceholderType, concurrency),
}
}
func (rp *TaskRunner) Schedule(task func()) {
rp.limitChan <- lang.Placeholder
go func() {
defer rescue.Recover(func() {
<-rp.limitChan
})
task()
}()
}

View File

@@ -0,0 +1,37 @@
package threading
import (
"runtime"
"sync"
"sync/atomic"
"testing"
"github.com/stretchr/testify/assert"
)
func TestRoutinePool(t *testing.T) {
times := 100
pool := NewTaskRunner(runtime.NumCPU())
var counter int32
var waitGroup sync.WaitGroup
for i := 0; i < times; i++ {
waitGroup.Add(1)
pool.Schedule(func() {
atomic.AddInt32(&counter, 1)
waitGroup.Done()
})
}
waitGroup.Wait()
assert.Equal(t, times, int(counter))
}
func BenchmarkRoutinePool(b *testing.B) {
queue := NewTaskRunner(runtime.NumCPU())
for i := 0; i < b.N; i++ {
queue.Schedule(func() {
})
}
}

View File

@@ -0,0 +1,21 @@
package threading
type WorkerGroup struct {
job func()
workers int
}
func NewWorkerGroup(job func(), workers int) WorkerGroup {
return WorkerGroup{
job: job,
workers: workers,
}
}
func (wg WorkerGroup) Start() {
group := NewRoutineGroup()
for i := 0; i < wg.workers; i++ {
group.RunSafe(wg.job)
}
group.Wait()
}

View File

@@ -0,0 +1,28 @@
package threading
import (
"fmt"
"runtime"
"sync"
"testing"
"zero/core/lang"
"github.com/stretchr/testify/assert"
)
func TestWorkerGroup(t *testing.T) {
m := make(map[string]lang.PlaceholderType)
var lock sync.Mutex
var wg sync.WaitGroup
wg.Add(runtime.NumCPU())
group := NewWorkerGroup(func() {
lock.Lock()
m[fmt.Sprint(RoutineId())] = lang.Placeholder
lock.Unlock()
wg.Done()
}, runtime.NumCPU())
go group.Start()
wg.Wait()
assert.Equal(t, runtime.NumCPU(), len(m))
}