File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change 1+ // Copyright 2025 FishGoddess. All rights reserved.
2+ // Use of this source code is governed by a MIT style
3+ // license that can be found in the LICENSE file.
4+
5+ package goes
6+
7+ import (
8+ "sync"
9+
10+ "github.com/FishGoddess/goes/pkg/spinlock"
11+ )
12+
13+ type Pool struct {
14+ workers []* worker
15+ index int
16+ closed bool
17+
18+ wg sync.WaitGroup
19+ lock sync.Locker
20+ }
21+
22+ func NewPool (size int , workerLimit int ) * Pool {
23+ if size <= 0 {
24+ panic ("goes: pool size <= 0" )
25+ }
26+
27+ pool := & Pool {
28+ workers : make ([]* worker , 0 , size ),
29+ index : 0 ,
30+ closed : false ,
31+ lock : spinlock .New (),
32+ }
33+
34+ for range size {
35+ worker := newWorker (pool , workerLimit )
36+ pool .workers = append (pool .workers , worker )
37+ }
38+
39+ return pool
40+ }
41+
42+ func (p * Pool ) Go (f func ()) {
43+ p .lock .Lock ()
44+ defer p .lock .Unlock ()
45+
46+ if p .closed {
47+ return
48+ }
49+
50+ worker := p .workers [p .index ]
51+ worker .Accept (f )
52+
53+ p .index ++
54+
55+ if p .index >= len (p .workers ) {
56+ p .index = 0
57+ }
58+ }
59+
60+ func (p * Pool ) Wait () {
61+ p .wg .Wait ()
62+ }
63+
64+ func (p * Pool ) Close () {
65+ p .lock .Lock ()
66+ defer p .lock .Unlock ()
67+
68+ for _ , worker := range p .workers {
69+ worker .Done ()
70+ }
71+
72+ p .wg .Wait ()
73+ p .closed = true
74+ }
Original file line number Diff line number Diff line change 1+ // Copyright 2025 FishGoddess. All rights reserved.
2+ // Use of this source code is governed by a MIT style
3+ // license that can be found in the LICENSE file.
4+
5+ package goes
6+
7+ import (
8+ "sync"
9+ "testing"
10+ "time"
11+ )
12+
13+ // go test -v -cover -run=^TestPool$
14+ func TestPool (t * testing.T ) {
15+ size := 16
16+ workerLimit := 1024
17+ pool := NewPool (size , workerLimit )
18+
19+ var countMap = make (map [int64 ]int , 16 )
20+ var lock sync.Mutex
21+
22+ totalCount := 10 * size
23+ for i := 0 ; i < totalCount ; i ++ {
24+ pool .Go (func () {
25+ now := time .Now ().UnixMilli () / 10
26+
27+ lock .Lock ()
28+ countMap [now ] = countMap [now ] + 1
29+ lock .Unlock ()
30+
31+ time .Sleep (10 * time .Millisecond )
32+ })
33+ }
34+
35+ pool .Close ()
36+ pool .Wait ()
37+
38+ gotTotalCount := 0
39+ for now , count := range countMap {
40+ gotTotalCount = gotTotalCount + count
41+
42+ if count != size {
43+ t .Fatalf ("now %d: count %d != size %d" , now , count , size )
44+ }
45+ }
46+
47+ if gotTotalCount != totalCount {
48+ t .Fatalf ("gotTotalCount %d != totalCount %d" , gotTotalCount , totalCount )
49+ }
50+ }
Original file line number Diff line number Diff line change 1+ // Copyright 2025 FishGoddess. All rights reserved.
2+ // Use of this source code is governed by a MIT style
3+ // license that can be found in the LICENSE file.
4+
5+ package goes
6+
7+ type worker struct {
8+ pool * Pool
9+ tasks chan func ()
10+ }
11+
12+ func newWorker (pool * Pool , limit int ) * worker {
13+ if limit <= 0 {
14+ panic ("goes: worker limit <= 0" )
15+ }
16+
17+ w := & worker {
18+ pool : pool ,
19+ tasks : make (chan func (), limit ),
20+ }
21+
22+ w .work ()
23+ return w
24+ }
25+
26+ func (w * worker ) handle (task func ()) {
27+ if task == nil {
28+ return
29+ }
30+
31+ defer func () {
32+ if r := recover (); r != nil {
33+ panic (r ) // TODO recover from panic
34+ }
35+ }()
36+
37+ task ()
38+ }
39+
40+ func (w * worker ) work () {
41+ w .pool .wg .Add (1 )
42+ go func () {
43+ defer w .pool .wg .Done ()
44+
45+ for task := range w .tasks {
46+ if task == nil {
47+ break
48+ }
49+
50+ w .handle (task )
51+ }
52+
53+ close (w .tasks )
54+ }()
55+ }
56+
57+ func (w * worker ) Accept (task func ()) {
58+ if task == nil {
59+ return
60+ }
61+
62+ w .tasks <- task
63+ }
64+
65+ func (w * worker ) Done () {
66+ w .tasks <- nil
67+ }
Original file line number Diff line number Diff line change 1+ // Copyright 2025 FishGoddess. All rights reserved.
2+ // Use of this source code is governed by a MIT style
3+ // license that can be found in the LICENSE file.
4+
5+ package goes
You can’t perform that action at this time.
0 commit comments