Last active
January 8, 2018 23:39
-
-
Save zombiezen/23cffea630e6345a22aa6d6fc005b593 to your computer and use it in GitHub Desktop.
Writing while respecting Context.Done
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// writeCtx writes bytes to a writer while making a best effort to | |
// respect the Done signal of the Context. However, once any bytes have | |
// been written to w, writeCtx will ignore the Done signal to avoid | |
// partial writes. | |
func writeCtx(ctx context.Context, w io.Writer, b []byte) (int, error) { | |
select { | |
case <-ctx.Done(): | |
// Early cancel. | |
return 0, ctx.Err() | |
default: | |
} | |
// Check for timeout support. | |
wd, ok := w.(interface { | |
SetWriteDeadline(time.Time) error | |
}) | |
if !ok { | |
return w.Write(b) | |
} | |
if err := wd.SetWriteDeadline(time.Now()); err != nil { | |
return w.Write(b) | |
} | |
// Start separate goroutine to wait on Context.Done. | |
if d, ok := ctx.Deadline(); ok { | |
wd.SetWriteDeadline(d) | |
} else { | |
wd.SetWriteDeadline(time.Time{}) | |
} | |
writeDone := make(chan struct{}) | |
listenDone := make(chan struct{}) | |
go func() { | |
defer close(listenDone) | |
select { | |
case <-ctx.Done(): | |
wd.SetWriteDeadline(time.Now()) // interrupt write | |
case <-writeDone: | |
} | |
}() | |
n, err := w.Write(b) | |
close(writeDone) | |
<-listenDone | |
if n == 0 || !isTimeout(err) { | |
return n, err | |
} | |
// Data has been written. Block until finished, since partial writes | |
// are guaranteed protocol violations. | |
wd.SetWriteDeadline(time.Time{}) | |
nn, err := w.Write(b[n:]) | |
return n + nn, err | |
} | |
func isTimeout(e error) bool { | |
te, ok := e.(interface { | |
Timeout() bool | |
}) | |
return ok && te.Timeout() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment