Live data from Hacker News

Goroutines and APIs

blog.gnoack.org

21–30 of 31 posts

Re: Goroutines and APIs

#21

Earlier quoted context omitted.

Right. So: * Don't require users of your API to start [additional] goroutines to use your API correctly.

I guess what I mean to say is: don't make your users write go yourpackage.YourApiCall() in order to use the API correctly. That's just a guess, though.

The article says almost exactly the opposite, and I agree.

Your API should present all of the things it does as synchronous functions/methods. If callers want to run them asynchronously, they can easily provide their own goroutine to do so, including whatever lifecycle management makes sense for them.

The concrete example was

    // Do this
    func (t *type) Run(ctx context.Context) { ... }

    // Not this    
    func (t *type) Start()  { ... }
    func (t *type) Cancel() { ... }
This is generally good advice which stops a whole class of extremely common and very tricky to debug orchestration bugs outright.

Re: Goroutines and APIs

#22

I can't read the article but hopefully it does not ironically present advice on writing webservers. There is really little excuse for a web server to buckle under pressure these days - hackernews sends a spike of traffic but not enough to kill even a moderately spec'ed server.

Often these kinds of problems are caused by system misconfigurations, not web servers buckling.

(Source: Am web server developer)

Re: Goroutines and APIs

#23

Some operations can use green threads, while others need kernel threads. Last I tried (~2015) Go would spawn unlimited kernel threads, so function callers had to know whether a call required a kernel thread or not, and rate-limit appropriately. Is this still the case? How does modern Go handle operations which require kernel threads?

Which operations need kernel threads? There is runtime.LockOSThread if you need underlying thread affinity, but I'm not aware of any other mechanism to interact with the underlying threads of execution.

For example os.Stat()

Re: Goroutines and APIs

#24

Earlier quoted context omitted.

Which operations need kernel threads? There is runtime.LockOSThread if you need underlying thread affinity, but I'm not aware of any other mechanism to interact with the underlying threads of execution.

For example os.Stat()

Interesting, I've never encountered a situation where that kind of thing matters, out of curiosity what are your use cases?

Re: Goroutines and APIs

#25

Some operations can use green threads, while others need kernel threads. Last I tried (~2015) Go would spawn unlimited kernel threads, so function callers had to know whether a call required a kernel thread or not, and rate-limit appropriately. Is this still the case? How does modern Go handle operations which require kernel threads?

Yes, they still need unlimited kernel threads (for liveness reasons). When I asked a few years back about this, go-nuts@ suggested implemented my own rate limiting in application space.

In my case, it was for running Go on a resource constrained Raspberry-PI, where the kernel threads could easily live too long, and use up all memory. The threads were calling read(2) on a network mounted fuse fs, and would last for 30s+.

Re: Goroutines and APIs

#26
The problem with goroutines is that an unhandled crash can cause a shutdown, and if the goroutine is started by a badly-written library with no proper recovery, you are dead.

Re: Goroutines and APIs

#27
In general, decouple libraries from use case dependent assumptions. This holds true not just for concurrency, but for memory management, sockets, file streams etc. Don't assume that a per-object heap allocation is good enough for me. Don't assume that I want to serialize to a file system. Don't assume that a channel has a large enough buffer.

Re: Goroutines and APIs

#28
post #26

The problem with goroutines is that an unhandled crash can cause a shutdown, and if the goroutine is started by a badly-written library with no proper recovery, you are dead.

But that's good, right? As a rule, panics should terminate your program.

Re: Goroutines and APIs

#29
post #12

I think that it is universal advice for libraries: do not do multithreading inside, let the client code do it and/or pass necessary constructs. Simple example for Java: do not create executorservice instances inside of a library, but allow client code to pass them. Ayende wrote much better than me long time ago https://ayende.com/blog/159201/multi-threaded-design-guideli...

I think what Kotlin is doing with co-routines improves a bit on this. The approach there is to make asynchronous code look (almost) like normal code. The only difference is that 1) you are dealing with functions that are marked as suspend and 2) you can only call these from a co-routine scope.

The last bit is the crucial thing. The co-routine scope is how you structure concurrency and is something I have not really seen in other approaches. In a mobile application you have the main thread, IO threads, and maybe some CPU threads. When calling something that is asynchronous, you have to assign it a scope. When that scope ends (because of a timeout, error, etc.) there's a cleanup step that cancels all the dangling tasks.

The nice thing about this is that you get clean separation of how to structure your concurrency and the logic. They also made it very easy to integrate with existing asynchronous code (there are a lot of frameworks for this in Java).

Re: Goroutines and APIs

#30
post #12

I think that it is universal advice for libraries: do not do multithreading inside, let the client code do it and/or pass necessary constructs. Simple example for Java: do not create executorservice instances inside of a library, but allow client code to pass them. Ayende wrote much better than me long time ago https://ayende.com/blog/159201/multi-threaded-design-guideli...

I think what Kotlin is doing with co-routines improves a bit on this. The approach there is to make asynchronous code look (almost) like normal code. The only difference is that 1) you are dealing with functions that are marked as suspend and 2) you can only call these from a co-routine scope. The last bit is the crucial thing. The co-routine scope is how you structure concurrency and is something I have not really s…

Kotlin's approach is extremely nice, not perfect but nice, for example most of Kotlins collection operations are inline so functions like mapIndexed etc.. you can call a suspend function even though mapIndexed is agnostic (by nature of just a ~template) as long as your in a co-routine scope of course, I don't often call suspending operations in collection operations but the inline fun approach well in other areas (your own code). Other languages don't have this - Javascript for example map/filter/reduce won't await if you return a Promise. C# would need specialized Task aware version of the Linq stuff (they probably already exist).

The scope tree thing is really nice too once you get your head around it, much nicer than in c# with carrying cancellationtokens around and creating your own cancellationsource when you need something like a supervisor scope.

Post reply on HN