// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"runtime"
	"runtime/debug"
	"sync/atomic"
)

func init() {
	register("AsyncPreempt", AsyncPreempt)
}

func AsyncPreempt() {
	// Run with just 1 GOMAXPROCS so the runtime is required to
	// use scheduler preemption.
	runtime.GOMAXPROCS(1)
	// Disable GC so we have complete control of what we're testing.
	debug.SetGCPercent(-1)
	// Out of an abundance of caution, also make sure that there are
	// no GCs actively in progress. The sweep phase of a GC cycle
	// for instance tries to preempt Ps at the very beginning.
	runtime.GC()

	// Start a goroutine with no sync safe-points.
	var ready, ready2 uint32
	go func() {
		for {
			atomic.StoreUint32(&ready, 1)
			dummy()
			dummy()
		}
	}()
	// Also start one with a frameless function.
	// This is an especially interesting case for
	// LR machines.
	go func() {
		atomic.AddUint32(&ready2, 1)
		frameless()
	}()
	// Also test empty infinite loop.
	go func() {
		atomic.AddUint32(&ready2, 1)
		for {
		}
	}()

	// Wait for the goroutine to stop passing through sync
	// safe-points.
	for atomic.LoadUint32(&ready) == 0 || atomic.LoadUint32(&ready2) < 2 {
		runtime.Gosched()
	}

	// Run a GC, which will have to stop the goroutine for STW and
	// for stack scanning. If this doesn't work, the test will
	// deadlock and timeout.
	runtime.GC()

	println("OK")
}

//go:noinline
func frameless() {
	for i := int64(0); i < 1<<62; i++ {
		out += i * i * i * i * i * 12345
	}
}

var out int64

//go:noinline
func dummy() {}
