When I invoke a C code from a goroutine, does it affect scheduling of other goroutines in any way? I know that if I call a NIF in Erlang, it blocks the other (Erlang) processes, until the function returns. Is this the case in Golang? Does C code block the goroutines scheduler?
As I have mentioned in the last paragraph, Goroutines are cooperatively scheduled. In cooperative scheduling there is no concept of scheduler time slice. In such scheduling Goroutines yield the control periodically when they are idle or logically blocked in order to run multiple Goroutines concurrently.
Goroutines are useful when you want to do multiple things simultaneously. For example, if you have ten things you want to do at the same time, you can do each one on a separate goroutine, and wait for all of them to finish.
Threads are hardware dependent. Goroutines have easy communication medium known as channel. Thread does not have easy communication medium. Due to the presence of channel one goroutine can communicate with other goroutine with low latency.
Typically, you pass the goroutine a (possibly separate) signal channel. That signal channel is used to push a value into when you want the goroutine to stop. The goroutine polls that channel regularly. As soon as it detects a signal, it quits.
Calling a C function from Go code does not prevent other goroutines from running.
It does have an effect on the scheduler. A goroutine running a C function does not necessarily count against the GOMAXPROCS limit. It will start out counting against GOMAXPROCS, but if the C function has blocked for more than 20us at the time the sysmon background goroutine runs, then the scheduler will be permitted to start another goroutine if there is one ready to run. These details depend on the specific Go version and are subject to change.
This is a very good question for which I did not find any official statement on the fly except in the code. I would be glad for any hint to official documentation.
The answer is no, cgo calls do not block the scheduler.
For the following it's good to know that internally Go uses G for goroutines, M for machines (threads) and P for proccessor. Goroutines are run on processors that run on machines.
Calling a C function from G works as follows according to the code documentation:
// To call into the C function f from Go, the cgo-generated code calls
// runtime.cgocall(_cgo_Cfunc_f, frame), where _cgo_Cfunc_f is a
// gcc-compiled function written by cgo.
//
// runtime.cgocall (below) locks g to m, calls entersyscall
// so as not to block other goroutines or the garbage collector,
// and then calls runtime.asmcgocall(_cgo_Cfunc_f, frame).
//
// runtime.asmcgocall (in asm_$GOARCH.s) switches to the m->g0 stack
// (assumed to be an operating system-allocated stack, so safe to run
// gcc-compiled code on) and calls _cgo_Cfunc_f(frame).
//
// _cgo_Cfunc_f invokes the actual C function f with arguments
// taken from the frame structure, records the results in the frame,
// and returns to runtime.asmcgocall.
//
// After it regains control, runtime.asmcgocall switches back to the
// original g (m->curg)'s stack and returns to runtime.cgocall.
//
// After it regains control, runtime.cgocall calls exitsyscall, which blocks
// until this m can run Go code without violating the $GOMAXPROCS limit,
// and then unlocks g from m.
entersyscall
essentially tells the runtime that this goroutine is now under 'external' control, just like in the situation when we do syscalls to the kernel. Another probably useful bit is that locking g
to m
(locking the cgo calling goroutine to the OS thread) enables the runtime to allocate a new OS thread (theoretically exceeding GOMAXPROCS
).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With