Skip to content

FakeClock.Sleep calling FakeClock.Step may be unexpected #309

Open
@SOF3

Description

@SOF3

What happened:

Consider the following code (playground):

package main

import (
	"time"

	"k8s.io/utils/clock"
	clocktesting "k8s.io/utils/clock/testing"
)

func main() {
	clk := clocktesting.NewFakeClock(time.Now())
	go func(clk clock.Clock) {
		clk.Sleep(time.Second * 2)
		println("after 2 fakeclock seconds")
	}(clk)

	time.Sleep(time.Millisecond) // used to emulate race condition
	clk.Sleep(time.Second)
	println("after 1 fakeclock second")
}

The code above prints

after 2 fakeclock seconds
after 1 fakeclock second

When clk is changed to clock.RealClock{} (playground), the println lines are in correct order (1 -> 2).

What you expected to happen:

Intuitively, one would expect clk.Sleep(duration) to be identical to <-clk.After(duration). However, (*FakeClock).Sleep actually calls clk.Step instead, which immediately modifies the clock.

Suggested fix:

It would be a huge BC break to many testing packages if (*FakeClock).Sleep is changed to be passive. Documentation improvement would suffice, although deprecating Sleep would be great since Sleep doesn't always do what users intend. (imo, clk.Step is usually only intended in the main control flow of the unit test rather than other goroutines, so calling clk.Sleep from non-testing code is often unintended).

Alternatively, consider adding a ref-counted clock, where Sleep and After have identical behavior and the clock is only stepped when all refs are sleeping.

Environment:
k8s.io/utils: fe8a2dd

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions