Contexts in Go are generally used for convenience in request cancellation, but they're not required , and they're not the only way to do it. Under the hood, a context is just a channel that's closed on cancellation. The way it was done before contexts was pretty much the same: func CancellableOp(done chan error /* , args... */) { for { // ... // cancellable code: select { case Some compare context "virus" to async vi…
Context should go away for Go 2 (2017)
51–60 of 176 posts
Re: Context should go away for Go 2 (2017)
#52Contexts in Go are generally used for convenience in request cancellation, but they're not required , and they're not the only way to do it. Under the hood, a context is just a channel that's closed on cancellation. The way it was done before contexts was pretty much the same: func CancellableOp(done chan error /* , args... */) { for { // ... // cancellable code: select { case Some compare context "virus" to async vi…
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
reader := ioContextReader(ctx, r)
...
ctx, cancel := context.WithTimeout(ctx, 1*time.Second)
ctx = context.WithValue(ctx, "hello", "world")
...
func(ctx context.Context) {
reader.Read() // does not time out after one second, does not contain hello/world.
...
}(ctx)Re: Context should go away for Go 2 (2017)
#53Earlier quoted context omitted.
The author gave a pretty good reasoning why is it a bad idea, in the same section. However, for the demonstration purposes I think the they should have included their vision on how the request scoped data should be passed. As I understand they propose to pass the data explicitly, like a struct with fields for all possible request-scoped data. I personally don't like context for value passing either, as it is easy to…
> As I understand they propose to pass the data explicitly, like a struct with fields for all possible request-scoped data. So basically context.Context, except it can't propagate through third party libraries?
It won't propagate to the third-party libraries, yes. But then again, why don't they just provide an explicit way of passing values instead of hiding them in the context?
Re: Context should go away for Go 2 (2017)
#54Contexts in Go are generally used for convenience in request cancellation, but they're not required , and they're not the only way to do it. Under the hood, a context is just a channel that's closed on cancellation. The way it was done before contexts was pretty much the same: func CancellableOp(done chan error /* , args... */) { for { // ... // cancellable code: select { case Some compare context "virus" to async vi…
Consider this: ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) reader := ioContextReader(ctx, r) ... ctx, cancel := context.WithTimeout(ctx, 1*time.Second) ctx = context.WithValue(ctx, "hello", "world") ... func(ctx context.Context) { reader.Read() // does not time out after one second, does not contain hello/world. ... }(ctx)
1) You're calling Read() directly and don't need to use functions that strictly accept io.Reader - then just implement ReadContext:
func (rc ioContextReader) ReadContext(ctx context.Context, p []byte) (n int, err error) {
done := make(chan struct{})
go func() {
n, err = rc.Reader.Read(p)
close(done)
}()
select {
case
Otherwise, just wrap the ioContextReader with another ioContextReader: reader = ioContextReader(ctx, r)Re: Context should go away for Go 2 (2017)
#55Contexts in Go are generally used for convenience in request cancellation, but they're not required , and they're not the only way to do it. Under the hood, a context is just a channel that's closed on cancellation. The way it was done before contexts was pretty much the same: func CancellableOp(done chan error /* , args... */) { for { // ... // cancellable code: select { case Some compare context "virus" to async vi…
This works, but goes against convention in that (from the context package docs) you shouldn’t “store Contexts inside a struct type; instead, pass a Context explicitly to each function that needs it.”
Re: Context should go away for Go 2 (2017)
#56This article is from 2017! As others have already mentioned, there won't be a Go 2. Besides, I really don't want another verbose method for cancellation; error handling is already bad enough.
I thought go 2 was considered harmful
Re: Context should go away for Go 2 (2017)
#57Contexts in Go are generally used for convenience in request cancellation, but they're not required , and they're not the only way to do it. Under the hood, a context is just a channel that's closed on cancellation. The way it was done before contexts was pretty much the same: func CancellableOp(done chan error /* , args... */) { for { // ... // cancellable code: select { case Some compare context "virus" to async vi…
You're spawning a goroutine per Read call? This is pretty bonkers inefficient, to start, and a super weird approach in any case...
type ioContextReadCloser struct {
io.ReadCloser
ctx context.Context
ch chan *readReq
}
type readReq struct {
p []byte
n *int
err *error
m sync.Mutex
}
func NewIoContextReadCloser(ctx context.Context, rc io.ReadCloser) *ioContextReadCloser {
rcc := &ioContextReadCloser{
ReadCloser: rc,
ctx: ctx,
ch: make(chan *readReq),
}
go rcc.readLoop()
return rcc
}
func (rcc *ioContextReadCloser) readLoop() {
for {
select {
case
Again, this is not to say this is the right way, only that it is possible and does not require any shenanigans that e.g. Python needs when dealing with when mixing sync & async, or even different async libraries.Re: Context should go away for Go 2 (2017)
#58Earlier quoted context omitted.
I already talked about it above. Main problems with passing dependencies in function argument lists: 1) it pollutes the code and makes refactoring harder (a small change in one place must be propagated to all call sites in the dependency tree which recursively accept user ID/tenant ID and similar info) 2) it violates various architectural principles, for example, from the point of view of our business logic, there's…
I believe this problem isn't solvable under our current paradigm of programming, which I call "working directly on plaintext, single-source-of-truth codebase". Tenant ID, cancellations, loggers, error handling are all examples of cross-cutting concerns. Depending on what any given function does, and what you (the programmer) are interested in at a given moment, any of them could be critical information or pure noise.…
However, there's a lot of manual labour to stuff everything into a monad, and then extract it and pattern match when your libraries don't match your choice of control flow monad(s)!
This is where I'd prefer if compilers could come in.
Imagine being in the bowels of a DB lib, and realising that the function you just write might be well positioned to terminate the TCP connection that it's using to talk to the database with. Oh no: now you have to update the signature and every single call-site for its parent, and its parent, and...
Instead, it would be neat if the compiler could treat things you deem cross-cutting as a graph traversal problem instead; call a cancelable method and all callers are automatically cancelable. Decisions about whether to spawn a cancelable subtree, to 'protect' some execution or set a deadline is then written on an opt-in basis per function; all functions compose. The compiler can visualise the tree of cancellation (or hierachical loggers, or OT spans, or actors, or green fibers, or ...) and it can enforce the global invariant that the entry-point captures SIGINT (or sets up logging, or sets up a tracer, or ...).
So imagine the infrastructure of a monad transformer, but available per-function on an opt-in basis. If you write your function to have a cleanup on cancellation, or write logs around any asynchronous barrier, the fiddly details of stuffing the monad is done by the compiler and optionally visualised and explained in the IDE. Your code doesn't have to opt-in, so you can make each function very clean.
Re: Context should go away for Go 2 (2017)
#59This is about an explicit argument of type "Context". I'm not a Go user, and at first I thought it was about something else: an implicit context variable that allows you to pass stuff deep down the call stack, without intermediate functions knowing about it. React has "Context", SwiftUI has "@Environment", Emacs LISP has dynamic scope (so I heard). C# has AsyncLocal, Node.JS AsyncLocalStorage. This is one of those id…
Furthermore in Go threads are spun up at process start, not at request time, so thread-local has a leak risk or cleanup cost. Contexts are all releasable after their processing ends.
I've grown to be a huge fan of Go for servers and context is one reason. That said, I agree with a lot of the critique and would love to see an in-language solution, but thread-local ain't it.
Re: Context should go away for Go 2 (2017)
#60> If you use ctx.Value in my (non-existent) company, you’re fired This is such a bad take. ctx.Value is incredibly useful for passing around context of api calls. We use it a lot, especially for logging such context values as locales, ids, client info, etc. We then use these context values when calling other services as headers so they gain the context around the original call too. Loggers in all services pluck out v…
Let me try to take the other side: `ctx.Value` is an `any -> any` kv store that does not come with any documentation, type checking for which key and value should be available. It's quick and dirty, but in a large code base, it can be quite tricky to check if you are passing too many values down the chain, or too little, and handle the failure cases. What if you just use a custom struct with all the fields you may ne…
The docs https://pkg.go.dev/context#Context suggest a way to make it type-safe (use an unexported key type and provide getter/setter). Seems fine to me.
> What if you just use a custom struct with all the fields you may need to be defined inside?
Can't seamlessly cross module boundaries.