github.com/guyezi/gofrontend@v0.0.0-20200228202240-7a62a49e62c0/libgo/go/runtime/testdata/testprog/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  package main
     6  
     7  import (
     8  	"os"
     9  	"runtime"
    10  	"time"
    11  )
    12  
    13  var mainTID int
    14  
    15  func init() {
    16  	registerInit("LockOSThreadMain", func() {
    17  		// init is guaranteed to run on the main thread.
    18  		mainTID = gettid()
    19  	})
    20  	register("LockOSThreadMain", LockOSThreadMain)
    21  
    22  	registerInit("LockOSThreadAlt", func() {
    23  		// Lock the OS thread now so main runs on the main thread.
    24  		runtime.LockOSThread()
    25  	})
    26  	register("LockOSThreadAlt", LockOSThreadAlt)
    27  
    28  	registerInit("LockOSThreadAvoidsStatePropagation", func() {
    29  		// Lock the OS thread now so main runs on the main thread.
    30  		runtime.LockOSThread()
    31  	})
    32  	register("LockOSThreadAvoidsStatePropagation", LockOSThreadAvoidsStatePropagation)
    33  }
    34  
    35  func LockOSThreadMain() {
    36  	// gettid only works on Linux, so on other platforms this just
    37  	// checks that the runtime doesn't do anything terrible.
    38  
    39  	// This requires GOMAXPROCS=1 from the beginning to reliably
    40  	// start a goroutine on the main thread.
    41  	if runtime.GOMAXPROCS(-1) != 1 {
    42  		println("requires GOMAXPROCS=1")
    43  		os.Exit(1)
    44  	}
    45  
    46  	ready := make(chan bool, 1)
    47  	go func() {
    48  		// Because GOMAXPROCS=1, this *should* be on the main
    49  		// thread. Stay there.
    50  		runtime.LockOSThread()
    51  		if mainTID != 0 && gettid() != mainTID {
    52  			println("failed to start goroutine on main thread")
    53  			os.Exit(1)
    54  		}
    55  		// Exit with the thread locked, which should exit the
    56  		// main thread.
    57  		ready <- true
    58  	}()
    59  	<-ready
    60  	time.Sleep(1 * time.Millisecond)
    61  	// Check that this goroutine is still running on a different
    62  	// thread.
    63  	if mainTID != 0 && gettid() == mainTID {
    64  		println("goroutine migrated to locked thread")
    65  		os.Exit(1)
    66  	}
    67  	println("OK")
    68  }
    69  
    70  func LockOSThreadAlt() {
    71  	// This is running locked to the main OS thread.
    72  
    73  	var subTID int
    74  	ready := make(chan bool, 1)
    75  	go func() {
    76  		// This goroutine must be running on a new thread.
    77  		runtime.LockOSThread()
    78  		subTID = gettid()
    79  		ready <- true
    80  		// Exit with the thread locked.
    81  	}()
    82  	<-ready
    83  	runtime.UnlockOSThread()
    84  	for i := 0; i < 100; i++ {
    85  		time.Sleep(1 * time.Millisecond)
    86  		// Check that this goroutine is running on a different thread.
    87  		if subTID != 0 && gettid() == subTID {
    88  			println("locked thread reused")
    89  			os.Exit(1)
    90  		}
    91  		exists, supported := tidExists(subTID)
    92  		if !supported || !exists {
    93  			goto ok
    94  		}
    95  	}
    96  	println("sub thread", subTID, "still running")
    97  	return
    98  ok:
    99  	println("OK")
   100  }
   101  
   102  func LockOSThreadAvoidsStatePropagation() {
   103  	// This test is similar to LockOSThreadAlt in that it will detect if a thread
   104  	// which should have died is still running. However, rather than do this with
   105  	// thread IDs, it does this by unsharing state on that thread. This way, it
   106  	// also detects whether new threads were cloned from the dead thread, and not
   107  	// from a clean thread. Cloning from a locked thread is undesirable since
   108  	// cloned threads will inherit potentially unwanted OS state.
   109  	//
   110  	// unshareFs, getcwd, and chdir("/tmp") are only guaranteed to work on
   111  	// Linux, so on other platforms this just checks that the runtime doesn't
   112  	// do anything terrible.
   113  	//
   114  	// This is running locked to the main OS thread.
   115  
   116  	// GOMAXPROCS=1 makes this fail much more reliably if a tainted thread is
   117  	// cloned from.
   118  	if runtime.GOMAXPROCS(-1) != 1 {
   119  		println("requires GOMAXPROCS=1")
   120  		os.Exit(1)
   121  	}
   122  
   123  	if err := chdir("/"); err != nil {
   124  		println("failed to chdir:", err.Error())
   125  		os.Exit(1)
   126  	}
   127  	// On systems other than Linux, cwd == "".
   128  	cwd, err := getcwd()
   129  	if err != nil {
   130  		println("failed to get cwd:", err.Error())
   131  		os.Exit(1)
   132  	}
   133  	if cwd != "" && cwd != "/" {
   134  		println("unexpected cwd", cwd, " wanted /")
   135  		os.Exit(1)
   136  	}
   137  
   138  	ready := make(chan bool, 1)
   139  	go func() {
   140  		// This goroutine must be running on a new thread.
   141  		runtime.LockOSThread()
   142  
   143  		// Unshare details about the FS, like the CWD, with
   144  		// the rest of the process on this thread.
   145  		// On systems other than Linux, this is a no-op.
   146  		if err := unshareFs(); err != nil {
   147  			if err == errNotPermitted {
   148  				println("unshare not permitted")
   149  				os.Exit(0)
   150  			}
   151  			println("failed to unshare fs:", err.Error())
   152  			os.Exit(1)
   153  		}
   154  		// Chdir to somewhere else on this thread.
   155  		// On systems other than Linux, this is a no-op.
   156  		if err := chdir("/tmp"); err != nil {
   157  			println("failed to chdir:", err.Error())
   158  			os.Exit(1)
   159  		}
   160  
   161  		// The state on this thread is now considered "tainted", but it
   162  		// should no longer be observable in any other context.
   163  
   164  		ready <- true
   165  		// Exit with the thread locked.
   166  	}()
   167  	<-ready
   168  
   169  	// Spawn yet another goroutine and lock it. Since GOMAXPROCS=1, if
   170  	// for some reason state from the (hopefully dead) locked thread above
   171  	// propagated into a newly created thread (via clone), or that thread
   172  	// is actually being re-used, then we should get scheduled on such a
   173  	// thread with high likelihood.
   174  	done := make(chan bool)
   175  	go func() {
   176  		runtime.LockOSThread()
   177  
   178  		// Get the CWD and check if this is the same as the main thread's
   179  		// CWD. Every thread should share the same CWD.
   180  		// On systems other than Linux, wd == "".
   181  		wd, err := getcwd()
   182  		if err != nil {
   183  			println("failed to get cwd:", err.Error())
   184  			os.Exit(1)
   185  		}
   186  		if wd != cwd {
   187  			println("bad state from old thread propagated after it should have died")
   188  			os.Exit(1)
   189  		}
   190  		<-done
   191  
   192  		runtime.UnlockOSThread()
   193  	}()
   194  	done <- true
   195  	runtime.UnlockOSThread()
   196  	println("OK")
   197  }