You may use runtime.LockOSThread()
to wire the calling goroutine to its current OS thread. This will ensure that no other goroutines will be scheduled to this thread, so your goroutine will run and not get interrupted or put on hold. No other goroutines will interfere when thread is locked.
After this, you just need a loop until the given seconds have passed. You must call runtime.UnlockOSThread()
to "release" the thread and make it available for other goroutines for execution, best done as a defer
statement.
See this example:
func runUntil(end time.Time) {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
for time.Now().Before(end) {
}
}
To make it wait for 2 seconds, it could look like this:
start := time.Now()
end := start.Add(time.Second * 2)
runUntil(end)
fmt.Println("Verify:", time.Now().Sub(start))
This prints for example:
Verify: 2.0004556s
Of course you can specify less than a second too, e.g. to wait for 100 ms:
start := time.Now()
runUntil(start.Add(time.Millisecond * 100))
fmt.Println("Verify:", time.Now().Sub(start))
Output:
Verify: 100.1278ms
You may use a different version of this function if that suits you better, one that takes the amount of time to "wait" as a value of time.Duration
:
func wait(d time.Duration) {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
for end := time.Now().Add(d); time.Now().Before(end); {
}
}
Using this:
start = time.Now()
wait(time.Millisecond * 200)
fmt.Println("Verify:", time.Now().Sub(start))
Output:
Verify: 200.1546ms
Note: Note that the loops in the above functions will use CPU relentlessly as there is no sleep or blocking IO in them, they will just query the current system time and compare it to the deadline.
What if the attacker increases system load by multiple concurrent attempts?
The Go runtime limits the system threads that can simultaneously execute goroutines. This is controlled by runtime.GOMAXPROCS()
, so this is already a limitation. It defaults to the number of available CPU cores, and you can change it anytime. This also poses a bottleneck though, as by using runtime.LockOSThread()
, if the number of locked threads equals to GOMAXPROCS
at any given time, that would block execution of other goroutines until a thread is unlocked.
See related questions:
Number of threads used by Go runtime
Why does it not create many threads when many goroutines are blocked in writing file in golang?