Source file src/runtime/testdata/testprogcgo/lockosthread.go

     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  
     8  package main
     9  
    10  import (
    11  	"os"
    12  	"runtime"
    13  	"sync/atomic"
    14  	"time"
    15  	"unsafe"
    16  )
    17  
    18  /*
    19  #include <pthread.h>
    20  #include <stdint.h>
    21  
    22  extern uint32_t threadExited;
    23  
    24  void setExited(void *x);
    25  */
    26  import "C"
    27  
    28  var mainThread C.pthread_t
    29  
    30  func 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  
    44  func 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  
    78  func 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  

View as plain text