I love the way Go handles I/O multiplexing internally which epoll
and another mechanisms and schedules green threads (go-routine here) on its own giving the freedom to write synchronous code.
I know TCP sockets are non-blocking
and read
will give EAGAIN
when no data is available. Given that, conn.Read(buffer)
will detect this and blocks the go routine doing a connection read with no data available in the socket buffer. Is there a way to stop such go routine without closing the underlying connection. I am using a connection pool so closing the TCP connection won't make sense for me and want to return that connection back to the pool.
Here is the code to simulate such scenario:
func main() {
conn, _ := net.Dial("tcp", "127.0.0.1:9090")
// Spawning a go routine
go func(conn net.Conn) {
var message bytes.Buffer
for {
k := make([]byte, 255) // buffer
m, err := conn.Read(k) // blocks here
if err != nil {
if err != io.EOF {
fmt.Println("Read error : ", err)
} else {
fmt.Println("End of the file")
}
break // terminate loop if error
}
// converting bytes to string for printing
if m > 0 {
for _, b := range k {
message.WriteByte(b)
}
fmt.Println(message.String())
}
}
}(conn)
// prevent main from exiting
select {}
}
What are the other approaches can I take if it's not possible:
1) Call syscall.Read
and handle this manually. In this case, I need a way to check if the socket is readable before calling syscall.Read
otherwise I will end up wasting unnecessary CPU cycles. For my scenario, I think I can skip the event based polling thing and keep on calling syscall.Read
as there always be data in my use case.
2) Any suggestions :)
Send an empty struct to
kill
from another goroutine to stop receiving from the connection. Here's a program that stops receiving after a second:This might not be exactly what you're looking for, because the reading goroutine would still be blocked on
Read
even after you send tokill
. However, the goroutine that handles incoming reads would terminate.