1

How can I perform a non-blocking write on a network connection in Go?

I thought it might be possible to do so by setting the deadline in the past:

conn.SetWriteDeadline(time.Date(0, 0, 0, 0, 0, 0, 1, time.UTC))
n, err := conn.Write(buffer)

... but this just fails with an 'i/o timeout' error without actually writing any bytes to the connection.

Scott Stensland
  • 26,870
  • 12
  • 93
  • 104
cpcallen
  • 1,834
  • 1
  • 16
  • 27
  • 5
    You don't. That's sort of the point of goroutines. What are you trying to do? – JimB May 26 '17 at 19:54
  • 1
    Also that deadline is far in the past, so it times out immediately. Did you mean `time.Now().Add(1 * time.Second)`? – Adrian May 26 '17 at 20:16
  • @JimB: Non-blocking writes in a one-thread-at-a-time (bytecode-like) interpreter. If the write would block I will either inform or suspend the thread (and allow another to run). I can do non-blocking writes to a channel, so I can achieve what I want indirectly by having a dedicated goroutine for each socket to schlep the bytes from a channel to the socket, but that seems like a stupid amount of extra overhead (and complexity) to do something that ought to be very straight forward. – cpcallen May 31 '17 at 14:21
  • @cpcallen: goroutines are not threads, and a blocked write to a network does not block an entire thread. I don't know what the "stupid amount of extra overhead" you're referring to is. – JimB May 31 '17 at 14:25
  • @Adrian: No; stopping the world for one second is not acceptable - and attempting to tune the timeout to be as small as possible risks having writes that would not block fail unnecessarily. – cpcallen May 31 '17 at 14:43
  • It doesn't stop the world, only the current goroutine. – Adrian May 31 '17 at 14:48
  • @JimB: I am aware that blocked goroutines do not tie up OS threads; the threads I refer to are threads of execution in the language I am implementing. Re: overhead: there are two kinds: the conceptual complexity of managing the per-socket goroutines and reporting back to the interpreted code which writes ultimately succeeded/failed; additionally the performance hit of otherwise unnecessary channel reads/writes + associated data copying and scheduling. (Performance is likely a non-issue for my use case, but were I writing some kind of high-performance web server I might think otherwise.) – cpcallen May 31 '17 at 14:56
  • @Adrian: The language I'm implementing has a co-operative multitasking model, with only a single thread running at a time. The interpreter runs in a single goroutine; if this blocks it cannot run another thread instead. (I could have a goroutine per interpreted-language thread, but this would require extensive synchronisation to ensure that only one such interpreter goroutine could run at a time, to avoid violating language semantics. This would also require some non-trivial changes to the overall architecture of the interpreter.) – cpcallen May 31 '17 at 15:03
  • In trying to write an interpreter in Go for a language that behaves very unlike Go, you're likely to run into these kinds of obstacles on a regular basis; the mismatch will cause things like this, which are trivial in Go, to become bigger headaches. And if you were writing a high-performance web server, I'd hope you would learn from Node's mistakes and not make it single-threaded! – Adrian May 31 '17 at 15:10

1 Answers1

3

In Go you use blocking I/O and the runtime "converts" it to non-blocking I/O: a goroutine blocked while reading from a socket, will be swapped by the scheduler with another goroutine ready to continue its execution.

Thus, you get the best of both worlds: code that's easier to follow (eg. no callback hell) and the efficiency of non-blocking I/O (ie. epoll, kqueue, completion ports).

UPDATE:

@cpcallen I need to be able to write to a socket in such a way that I can be certain that the thread performing the write does not block.

If by "thread" you refer the OS-level thread, it is not blocked. If by "thread" you refer to the goroutine performing the write, that's not yet possible but there's a proposal: https://github.com/golang/go/issues/15735

Agis
  • 32,639
  • 3
  • 73
  • 81
  • I need to be able to write to a socket in such a way that I can be certain that the thread performing the write does not block. – cpcallen May 31 '17 at 14:11
  • @cpcallen Updated my answer addressing your comment. – Agis May 31 '17 at 15:17
  • yes: in my comment I said thread but meant goroutine. Good spot with issues/15735, but I think that's blocking without reading, rather than writing without blocking... – cpcallen May 31 '17 at 23:14
  • @Agis referred to [this](https://stackoverflow.com/questions/39685437/if-goroutines-involve-userspace-threads-can-a-blocking-operation-leads-to-conte) when you call a blocking syscall the goroutine and os-level thread blocked. – Amin Paydar Feb 16 '20 at 17:48