I think you're missing the forest for the trees, here.
The io.Reader/Writer interfaces, and their implementations, are meant to provide a streaming model for reading and writing bytes, which is as efficient as reasonably possible, within the constraints of the core language.
If your goal is to make an io.Reader that respects a context.Context cancelation, then you can just do
type ContextReader struct {
ctx context.Context
r io.Reader
}
func NewContextReader(ctx context.Context, r io.Reader) *ContextReader {
return &ContextReader{
ctx: ctx,
r: r,
}
}
func (cr *ContextReader) Read(p []byte) (int, error) {
if err := cr.ctx.Err(); err != nil {
return 0, err
}
return cr.r.Read(p)
}
No goroutines or mutexes or whatever else required.Extending to a ReadCloser is a simple exercise left to the, er, reader.