Home | History | Annotate | Download | only in testprogcgo
      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 }
    112