Commit c216c3aa authored by Austin Clements's avatar Austin Clements

runtime: fix race between unminit and Windows profile loop

Currently, the Windows profile loop isn't robust against racing with
unminit. For example,

T1 is running profileloop1, T2 is another thread
T1: thread := atomic.Loaduintptr(&T2.thread)
T2: calls unminit, which does CloseHandle(T2.thread)
T1: attempts to suspends T2

In this case the SuspendThread will fail, but currently we ignore this
failure and forge ahead, which will cause further failures and
probably bad profile data.

Handle this race by defending against SuspendThread failing. If
SuspendThread succeeds, then we know the thread is no longer going
anywhere.

Change-Id: I4726553239b17f05ca07a0cf7df49631e0cb550d
Reviewed-on: https://go-review.googlesource.com/c/129685
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: 's avatarAlex Brainman <alex.brainman@gmail.com>
parent f6fea0f3
...@@ -858,14 +858,14 @@ func profileloop() ...@@ -858,14 +858,14 @@ func profileloop()
var profiletimer uintptr var profiletimer uintptr
func profilem(mp *m) { func profilem(mp *m, thread uintptr) {
var r *context var r *context
rbuf := make([]byte, unsafe.Sizeof(*r)+15) rbuf := make([]byte, unsafe.Sizeof(*r)+15)
// align Context to 16 bytes // align Context to 16 bytes
r = (*context)(unsafe.Pointer((uintptr(unsafe.Pointer(&rbuf[15]))) &^ 15)) r = (*context)(unsafe.Pointer((uintptr(unsafe.Pointer(&rbuf[15]))) &^ 15))
r.contextflags = _CONTEXT_CONTROL r.contextflags = _CONTEXT_CONTROL
stdcall2(_GetThreadContext, mp.thread, uintptr(unsafe.Pointer(r))) stdcall2(_GetThreadContext, thread, uintptr(unsafe.Pointer(r)))
var gp *g var gp *g
switch GOARCH { switch GOARCH {
...@@ -906,9 +906,16 @@ func profileloop1(param uintptr) uint32 { ...@@ -906,9 +906,16 @@ func profileloop1(param uintptr) uint32 {
if thread == 0 || mp.profilehz == 0 || mp.blocked { if thread == 0 || mp.profilehz == 0 || mp.blocked {
continue continue
} }
stdcall1(_SuspendThread, thread) // mp may exit between the load above and the
// SuspendThread, so be careful.
if int32(stdcall1(_SuspendThread, thread)) == -1 {
// The thread no longer exists.
continue
}
if mp.profilehz != 0 && !mp.blocked { if mp.profilehz != 0 && !mp.blocked {
profilem(mp) // Pass the thread handle in case mp
// was in the process of shutting down.
profilem(mp, thread)
} }
stdcall1(_ResumeThread, thread) stdcall1(_ResumeThread, thread)
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment