func profileSample(clk clock.Clock, w io.Writer, r io.Reader, res time.Duration) (io.Writer, io.Reader, func() SamplingProfile) { samplingWriter := &samplingTimeWriter{w: w} samplingReader := &samplingTimeReader{r: r} start := clk.Now() done := make(chan struct{}) samples := SamplingProfile{} go func() { ticker := clk.Ticker(res) defer ticker.Stop() for { select { case <-ticker.C: isWriting := atomic.LoadUint32(&samplingWriter.state) == stateBlocked isReading := atomic.LoadUint32(&samplingReader.state) == stateBlocked if isWriting { samples.Writing++ } else { samples.NotWriting++ } if isReading { samples.Reading++ } else { samples.NotReading++ } case <-done: return } } }() return samplingWriter, samplingReader, func() SamplingProfile { close(done) total := clk.Now().Sub(start) samples.TimeProfile = TimeProfile{ Total: total, WaitRead: time.Duration(float64(samples.Reading) / float64(samples.Reading+samples.NotReading) * float64(total)), WaitWrite: time.Duration(float64(samples.Writing) / float64(samples.Writing+samples.NotWriting) * float64(total)), } return samples } }
// GetAlignedTicker returns a ticker so that, let's say interval is a second // then it will tick at every whole second, or if it's 60s than it's every whole // minute. Note that in my testing this is about .0001 to 0.0002 seconds off due // to scheduling etc. func GetAlignedTicker(c clock.Clock, period time.Duration) *clock.Ticker { unix := c.Now().UnixNano() diff := time.Duration(period - (time.Duration(unix) % period)) return c.Ticker(diff) }