Add ReadTimeout to perf/ring buffer#523
Conversation
ti-mo
left a comment
There was a problem hiding this comment.
As a concept, I think taking context.Context is fine (this is Go after all), but for such a low-level API, it doesn't make much sense performance-wise. context.WithTimeout makes several allocations, takes out a lock and needs to be called for every call to ReadTimeout(). A simple value for setting a timeout would be just fine here. 👍
Overall I think this is a good addition. Implementing cancellation is not needed IMO, either read with timeouts or close the reader if you're done with it.
|
@ti-mo Addressed all your feedback if you want to give it another look. |
|
|
||
| // ReadTimeout is Read but will time out and return os.ErrDeadlineExceeded | ||
| // if the timeout expires. | ||
| func (pr *Reader) ReadTimeout(timeout time.Duration) (Record, error) { |
There was a problem hiding this comment.
Should this be a deadline vs. a time out?
There was a problem hiding this comment.
Do you mean taking a time.Time and computing the millisecond timeout value from that? If so, that would make it hard/impossible to end up with a timeout value of 0 which is useful for polling.
There was a problem hiding this comment.
Not sure I understand the use case of polling to be honest, so I'd be OK to forego that for now.
As a user I'd expect the timeout to specify the time until the next Record. However, what it really does is bound how long we block in epoll. Those are two distinct, since we might in the future generate a spurious wakeup (e.g. to implement suspension without map shenanigans). As a user I don't (and shouldn't care) that epoll was woken. So taking a deadline here is simpler to implement and easier to understand I think.
There was a problem hiding this comment.
So taking a deadline here is simpler to implement
Would you mind branching and showing me? A deadline seems to be more complicated to me, since the underlying epoll API supports the timeout, so we simply pass it on.
There was a problem hiding this comment.
Why not stick with a timeout? For network i/o, expressing timeouts as deadlines makes sense e.g. to bound a send/recv to the same window, but I'm not sure if that would be very useful here.
So taking a deadline here is simpler to implement and easier to understand I think.
Hmm, epoll doesn't have the equivalent of (net.Conn).SetReadDeadline iirc. With your suggestion, would the caller pass in a time.Time using time.Now().Add(time.Millisecond * 100), which the library code then has to reverse using t.Sub(time.Now()) to obtain a relative timeout value to pass to epoll_pwait? That's going to be less accurate than passing a time.Duration and won't allow the caller to busypoll.
There was a problem hiding this comment.
We can implement both, I'm just trying to say that ReadTimeout() would be implemented in terms of ReadDeadline(time.Now().Add(to)).
There was a problem hiding this comment.
Since msec timeout is the native format taken by epoll_wait, why not the other way around? :)
There was a problem hiding this comment.
I'm trying to say: the deadline or timeout parameter should indicate "the time i'm willing to wait until Read returns a record", not "the time I'm willing to wait for the next epoll notification". The current implementation does the latter.
Why is this relevant? For example, ringbuf can be woken from epoll but not produce a Record, because items in the ring may be marked as "discarded". The same can happen here if we end up changing the implementation. To avoid this the natural implementation is for Poller to take a deadline until which it may block.
If we add both timeout and deadline methods, how about SetTimeout and SetDeadline like for net.Conn etc.?
There was a problem hiding this comment.
(sorry for joining late here)
Why is this relevant? For example,
ringbufcan be woken from epoll but not produce a Record,
Wanted to add the other use case ringbuf has: read from raw ring without epoll. Should this be handled as part of the Set* as a special case or we want it to be completely separate ReadRaw() call or something?
There was a problem hiding this comment.
@mythi I probably need a more fully fleshed out example, maybe open a discussion about it?
|
@brycekahle do you want me to do another round of review? |
I think we probably need to figure out the API design issue first. |
|
Closing for now. Feel free to reopen. |
Adds the function
ReadTimeoutto allow non-indefinite waits when reading from a perf buffer.I'm not in love with the
ErrNoRecordssentinel error, but I wanted to see what you folks thought. Maybeio.EOFor exportingerrEORinstead?Alternatively, I could make it more Go-ish, changing the function signature to
ReadContext(ctx context.Context), but it would need to handle manual cancellation too.