Skip to content

Commit f558428

Browse files
committed
all: add mainthread.Go
1 parent 07478f5 commit f558428

File tree

2 files changed

+74
-25
lines changed

2 files changed

+74
-25
lines changed

mainthread.go

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
// Copyright 2020 The golang.design Initiative Authors.
1+
// Copyright 2021 The golang.design Initiative Authors.
22
// All rights reserved. Use of this source code is governed
3-
// by a GNU GPLv3 license that can be found in the LICENSE file.
3+
// by a MIT license that can be found in the LICENSE file.
4+
//
5+
// Written by Changkun Ou <changkun.de>
46

57
package mainthread // import "golang.design/x/mainthread"
68

@@ -9,21 +11,14 @@ import (
911
"sync"
1012
)
1113

12-
var funcQ = make(chan funcData, runtime.GOMAXPROCS(0))
13-
1414
func init() {
1515
runtime.LockOSThread()
1616
}
1717

18-
type funcData struct {
19-
fn func()
20-
done chan struct{}
21-
}
22-
23-
// Init initializes the functionality for running arbitrary subsequent
24-
// functions on a main system thread.
18+
// Init initializes the functionality of running arbitrary subsequent
19+
// functions be called on the main system thread.
2520
//
26-
// Init must be called in the main package.
21+
// Init must be called in the main.main function.
2722
func Init(main func()) {
2823
done := donePool.Get().(chan struct{})
2924
defer donePool.Put(done)
@@ -38,12 +33,14 @@ func Init(main func()) {
3833
for {
3934
select {
4035
case f := <-funcQ:
41-
func() {
42-
defer func() {
43-
f.done <- struct{}{}
44-
}()
36+
if f.fn != nil {
4537
f.fn()
46-
}()
38+
if f.done != nil {
39+
f.done <- struct{}{}
40+
}
41+
} else if f.fnv != nil {
42+
f.ret <- f.fnv()
43+
}
4744
case <-done:
4845
return
4946
}
@@ -59,6 +56,24 @@ func Call(f func()) {
5956
<-done
6057
}
6158

62-
var donePool = sync.Pool{
63-
New: func() interface{} { return make(chan struct{}) },
59+
// Go schedules f to be called on the main thread.
60+
func Go(f func()) {
61+
funcQ <- funcData{fn: f}
62+
}
63+
64+
var (
65+
funcQ = make(chan funcData, runtime.GOMAXPROCS(0))
66+
donePool = sync.Pool{New: func() interface{} {
67+
return make(chan struct{})
68+
}}
69+
retPool = sync.Pool{New: func() interface{} {
70+
return make(chan interface{})
71+
}}
72+
)
73+
74+
type funcData struct {
75+
fn func()
76+
done chan struct{}
77+
fnv func() interface{}
78+
ret chan interface{}
6479
}

mainthread_test.go

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,22 @@
1-
// Copyright 2020 The golang.design Initiative Authors.
1+
// Copyright 2021 The golang.design Initiative Authors.
22
// All rights reserved. Use of this source code is governed
3-
// by a GNU GPLv3 license that can be found in the LICENSE file.
3+
// by a MIT license that can be found in the LICENSE file.
4+
//
5+
// Written by Changkun Ou <changkun.de>
6+
7+
//go:build linux
8+
// +build linux
49

510
package mainthread_test
611

712
import (
13+
"context"
814
"fmt"
915
"os"
1016
"sync"
1117
"sync/atomic"
1218
"testing"
19+
"time"
1320

1421
"golang.design/x/mainthread"
1522
"golang.org/x/sys/unix"
@@ -22,11 +29,12 @@ func init() {
2229
}
2330

2431
func TestMain(m *testing.M) {
25-
mainthread.Init(func() {
26-
os.Exit(m.Run())
27-
})
32+
mainthread.Init(func() { os.Exit(m.Run()) })
2833
}
2934

35+
// TestMainThread is not designed to be executed on the main thread.
36+
// This test tests the a call from this function that is invoked by
37+
// mainthread.Call is either executed on the main thread or not.
3038
func TestMainThread(t *testing.T) {
3139
var (
3240
nummain uint64
@@ -39,11 +47,14 @@ func TestMainThread(t *testing.T) {
3947
go func() {
4048
defer wg.Done()
4149
mainthread.Call(func() {
50+
// Code inside this function is expecting to be executed
51+
// on the mainthread, this means the thread id should be
52+
// euqal to the initial process id.
4253
tid := unix.Gettid()
4354
if tid == initTid {
4455
return
4556
}
46-
t.Logf("call is not executed on the main thread, want %d, got %d", initTid, tid)
57+
t.Errorf("call is not executed on the main thread, want %d, got %d", initTid, tid)
4758
})
4859
}()
4960
go func() {
@@ -60,6 +71,29 @@ func TestMainThread(t *testing.T) {
6071
}
6172
}
6273

74+
func TestGo(t *testing.T) {
75+
done := make(chan struct{})
76+
mainthread.Go(func() {
77+
time.Sleep(time.Second)
78+
done <- struct{}{}
79+
})
80+
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond)
81+
defer cancel()
82+
select {
83+
case <-ctx.Done():
84+
case <-done:
85+
t.Fatalf("mainthread.Go is not executing in parallel")
86+
}
87+
88+
ctxx, cancell := context.WithTimeout(context.Background(), time.Second)
89+
defer cancell()
90+
select {
91+
case <-ctxx.Done():
92+
t.Fatalf("mainthread.Go never schedules the function")
93+
case <-done:
94+
}
95+
}
96+
6397
func BenchmarkCall(b *testing.B) {
6498
f := func() {}
6599
mainthread.Init(func() {

0 commit comments

Comments
 (0)