// also drive Client behaviour. Not safe for concurrent use. There are Torrent, File and Piece
// constructors for this.
type Reader interface {
+ // Set the context for reads. When done, reads should get cancelled so they don't get stuck
+ // waiting for data.
+ SetContext(context.Context)
// Read/Seek and not ReadAt because we want to return data as soon as it's available, and
// because we want a single read head.
io.ReadSeekCloser
// Function to dynamically calculate readahead. If nil, readahead is static.
readaheadFunc ReadaheadFunc
+ // This is not protected by a lock because you should be coordinating setting this. If you want
+ // different contexts, you should have different Readers.
+ ctx context.Context
+
// Required when modifying pos and readahead.
mu sync.Locker
responsive bool
}
+func (r *reader) SetContext(ctx context.Context) {
+ r.ctx = ctx
+}
+
var _ io.ReadSeekCloser = (*reader)(nil)
func (r *reader) SetResponsive() {
}
func (r *reader) Read(b []byte) (n int, err error) {
- return r.ReadContext(context.Background(), b)
+ return r.read(b)
}
+func (r *reader) read(b []byte) (n int, err error) {
+ return r.readContext(r.ctx, b)
+}
+
+// Deprecated: Use SetContext and Read. TODO: I've realised this breaks the ability to pass through
+// optional interfaces like io.WriterTo and io.ReaderFrom. Go sux. Context should be provided
+// somewhere else.
func (r *reader) ReadContext(ctx context.Context, b []byte) (n int, err error) {
+ r.ctx = ctx
+ return r.Read(b)
+}
+
+// We still pass ctx here, although it's a reader field now.
+func (r *reader) readContext(ctx context.Context, b []byte) (n int, err error) {
if len(b) > 0 {
r.reading = true
// TODO: Rework reader piece priorities so we don't have to push updates in to the Client
_, err = r.ReadContext(ctx, make([]byte, 1))
require.EqualValues(t, context.DeadlineExceeded, err)
}
+
+func TestReaderSetContextAndRead(t *testing.T) {
+ cl, err := NewClient(TestingConfig(t))
+ require.NoError(t, err)
+ defer cl.Close()
+ tt, err := cl.AddTorrent(testutil.GreetingMetaInfo())
+ require.NoError(t, err)
+ defer tt.Drop()
+ ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(time.Millisecond))
+ defer cancel()
+ r := tt.Files()[0].NewReader()
+ defer r.Close()
+ r.SetContext(ctx)
+ _, err = r.Read(make([]byte, 1))
+ require.EqualValues(t, context.DeadlineExceeded, err)
+}