• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2017 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//go:build !plan9 && !windows
6// +build !plan9,!windows
7
8package main
9
10import (
11	"os"
12	"runtime"
13	"sync/atomic"
14	"time"
15	"unsafe"
16)
17
18/*
19#include <pthread.h>
20#include <stdint.h>
21
22extern uint32_t threadExited;
23
24void setExited(void *x);
25*/
26import "C"
27
28var mainThread C.pthread_t
29
30func init() {
31	registerInit("LockOSThreadMain", func() {
32		// init is guaranteed to run on the main thread.
33		mainThread = C.pthread_self()
34	})
35	register("LockOSThreadMain", LockOSThreadMain)
36
37	registerInit("LockOSThreadAlt", func() {
38		// Lock the OS thread now so main runs on the main thread.
39		runtime.LockOSThread()
40	})
41	register("LockOSThreadAlt", LockOSThreadAlt)
42}
43
44func LockOSThreadMain() {
45	// This requires GOMAXPROCS=1 from the beginning to reliably
46	// start a goroutine on the main thread.
47	if runtime.GOMAXPROCS(-1) != 1 {
48		println("requires GOMAXPROCS=1")
49		os.Exit(1)
50	}
51
52	ready := make(chan bool, 1)
53	go func() {
54		// Because GOMAXPROCS=1, this *should* be on the main
55		// thread. Stay there.
56		runtime.LockOSThread()
57		self := C.pthread_self()
58		if C.pthread_equal(mainThread, self) == 0 {
59			println("failed to start goroutine on main thread")
60			os.Exit(1)
61		}
62		// Exit with the thread locked, which should exit the
63		// main thread.
64		ready <- true
65	}()
66	<-ready
67	time.Sleep(1 * time.Millisecond)
68	// Check that this goroutine is still running on a different
69	// thread.
70	self := C.pthread_self()
71	if C.pthread_equal(mainThread, self) != 0 {
72		println("goroutine migrated to locked thread")
73		os.Exit(1)
74	}
75	println("OK")
76}
77
78func LockOSThreadAlt() {
79	// This is running locked to the main OS thread.
80
81	var subThread C.pthread_t
82	ready := make(chan bool, 1)
83	C.threadExited = 0
84	go func() {
85		// This goroutine must be running on a new thread.
86		runtime.LockOSThread()
87		subThread = C.pthread_self()
88		// Register a pthread destructor so we can tell this
89		// thread has exited.
90		var key C.pthread_key_t
91		C.pthread_key_create(&key, (*[0]byte)(unsafe.Pointer(C.setExited)))
92		C.pthread_setspecific(key, unsafe.Pointer(new(int)))
93		ready <- true
94		// Exit with the thread locked.
95	}()
96	<-ready
97	for {
98		time.Sleep(1 * time.Millisecond)
99		// Check that this goroutine is running on a different thread.
100		self := C.pthread_self()
101		if C.pthread_equal(subThread, self) != 0 {
102			println("locked thread reused")
103			os.Exit(1)
104		}
105		if atomic.LoadUint32((*uint32)(&C.threadExited)) != 0 {
106			println("OK")
107			return
108		}
109	}
110}
111