github.com/yukk001/go1.10.8@v0.0.0-20190813125351-6df2d3982e20/src/runtime/testdata/testprogcgo/lockosthread.go (about)

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