> Golang is militantly anti-event; it doesn't even offer a select primitive! Just alternating read/write on two different sockets seems to demand threads!
Go only blocks the goroutine. It uses native event primitives (epoll/kqueue/etc) under the hood so that IO is non-blocking to the rest of the process (e.g. other goroutines). You said you have used Go for 2 months, so I assume you are aware of that.
So..can you be more specific about what you were talking about in that statement?
There are times when you might reach for select() not to scale the whole program, but because for instance you're trying to hot potato data from a Reader to a Writer.
Obviously, the whole of Golang's concurrency model is lightweight threads scheduled on I/O events. But that's not exposed to the programmer; in fact, it's hermetically sealed away from the programmer from what I can tell.
every time you perform i/o, the current goroutine yields to the scheduler.
If you want to manually yield to the scheduler, you call runtime.Gosched() and the calling goroutine yields. If you want to lock a goroutine into a specific thread, you call runtime.LockOSThread(). Everything is single-threaded to start until you call runtime.GOMAXPROCS(), but that's intended to go away in the very near future. So... I'm not sure what you mean by sealed away; the Go runtime does the sensible things that it can do automatically, but you still have the ability to change the scheduling behavior if you really want to.
what do you mean by "doesn't offer a select primitive"? I'm not familiar with your definition of select, because Go has a select keyword for concurrency control, and I'm under the impression you're talking about select as its defined in EventMachine, could you elaborate?
Go provides concurrency primitives (scheduler yielding i/o, channels, goroutines, etc) upon which you can build your own "events". I agree (and this seems to be your point) that Go doesn't provide a canned event mechanism that you can just hook into for callbacks on IO.
For example in the Go http server[1] a goroutine accepts in a loop and kicks off goroutines to process and handle new requests.
Take the example of a simple proxy to see where I'm coming from. Sure, I'm happy to have Go manage all the connections and sockets and I'm happy to spawn new goroutines for each connection and all that. But a proxy accepts an inbound connection, makes an outbound connection, and then monitors the outbound and inbound sides for data. The loop to do this with sockets could be a simple two-descriptor read select(2) call. But instead, I have to spawn two more goroutines, one to "monitor" (really, read) from inbound, and one for outbound.
Is there some feature of the language or the libraries that I am missing that turns a socket (err, a TCPConn) into a channel that I can read with Golang's select construction? Seriously asking. That would be awesome.
No, I think you would need to wrap the TCPConn in a goroutine that does the Read, handles any errors, then passes the bytes back to the consumer via a channel. At least it would be a very generic and small goroutine to do this.
You know what's happening in this thread? I made it sound like I was criticizing Go for needing to spawn goroutines to do this. I'm not! It makes sense, in the context of Go, to do it this way, even though as a C programmer by training that's not my first thought on how to do it.
I'm not criticizing Go for being anti-event; I'm just observing that it is. Idiomatic Go --- like, the code in the standard library --- has a strong bias towards straight-line code.
> I'm not criticizing Go for being anti-event; I'm just observing that it is. Idiomatic Go --- like, the code in the standard library --- has a strong bias towards straight-line code.
I think I understand where you are coming from now - I think you are saying Go is "anti-event-based-callback-driven" rather than "anti-event-loop-implementation" - which is absolutely true. Go's concurrency model is build on CSP (Hoare's Communicating Sequential Processes)[1] which seems to advocate procedural threads rather than callbacks.
Is there a large overhead for having to use a goroutine rather than the lower level? Do you think it's appropriate for Go to expose that, or do you just miss it?
Yes. This allows you to have the "when any of these socket's state changes" semantics in your controller/dispatcher function. I would like it if they had such an interface in stdlib...
If you really do just need to take data from one Reader and send it to a Writer, you can make a new Pipe.
So..can you be more specific about what you were talking about in that statement?