0

Overview

I am writing a program where a server is launched in one goroutine. The server has a few different goroutines:

  1. The main goroutine: This handles initialization, launches a second goroutine which listens for new connections (2), and then goes into an infinite loop to act on the data received from connections (i.e., clients).
  2. The listen goroutine: This goroutine goes into an infinite loop, constantly listening for new connections. If a new connection is accepted, another goroutine is launched which listens for messages from the connection until it is closed.

The server seems to work well. I can successfully add many new connections, and I can also send data on these new connections after they are initially accepted by the server.

My client is fairly simple. There are two goroutines as well:

  1. The main goroutine: This handles intialization, registers the client with a server, launches a second goroutine to read data from the server (2), and then goes into an infinite loop to act on the data received from the server.
  2. The second goroutine: This goroutine constantly tries to read data from the server until the connection is closed.

Starvation

I'm having a huge issue with goroutine starvation on the client. Specifically, the second goroutine of the client always starves. Here's the source of the goroutine that's starving:

func receiver() {
    for {
        msg, err := bufio.NewReader(conn).ReadString(byte(protocol.EndOfMessage))
        if err != nil {
            fmt.Printf("Disconnected from server %v.\n", conn.RemoteAddr())
            return
        }
        if len(msg) < 2 {
            continue
        }
        receiverToHandler <- msg[1 : len(msg)-1]
    }
}

I am certain that a message is being sent from the server to the client. I am also sure that the message being sent ends in protocol.EndOfMessage. I am also certain that my method of getting data from the server is correct since I use the same code to register the client, only instead of running it in an infinite loop I allow it a prespecified number of attempts.

For some reason, my client will not receive data.

To be certain that I am not misunderstanding the nature of goroutines, if I replace the above code with this:

func receiver() {
    for {
        fmt.Println("In the receiver goroutine!")
    }
}

The goroutine works exactly as expected: everything functions exactly as before, but "In the receiver goroutine!" is constantly printed. So the routine is certainly executing correctly in this case.

My handler

func handleMessage(debug bool) {
    select {
    case msg := <-receiverToHandler:
        update(msg, debug)
    default:
        /* if debug {
         *     fmt.Printf("Nothing received!\n")
         * }
         */
    }
}

Right now, update(msg, debug) just calls fmt.Println(msg).

Is there anything I can do to cleanly solve this problem? I feel like giving away priority / forcing the scheduler to run is a hacky solution to this problem.

  • Show the code that receives from `receiverToHandler`. – Charlie Tumahai Apr 19 '16 at 18:09
  • 1
    You're creating a new `bufio.Reader` for every message, though each previous `bufio.Reader` may have already read more than you expect from the conn. – JimB Apr 19 '16 at 18:09
  • 1
    I would be interested to see a debug message right after you read the string. ReadString will continue reading until it sees the delimiter; let's be sure that the delimiter is actually working. – william.taylor.09 Apr 19 '16 at 18:10
  • @JimB Apparently that's not the issue. Good catch, but I am still experiencing the same problems. –  Apr 19 '16 at 18:18
  • @william.taylor.09 If the delimiter wasn't working, then `ReadString` would block forever, so the debug message would never be printed. –  Apr 19 '16 at 18:19
  • That's exactly my point. You already verified that your goroutine was being called. Now you need to see where it's getting stuck. Put a debug message before and after ReadString, and I bet you'll find that you'll only get one of those messages being printed. – william.taylor.09 Apr 19 '16 at 19:19
  • @gragas: still, printing the received message in the main receive loop will show you if you aren't receiving what you expect, or you're having trouble coordinating the channel and goroutines. Your `handleMessage ` only recieves once, and of you were to wrap that in a for loop, you turn it into a busy loop wich will cause other problems. Please show an example that can reproduce your problem. – JimB Apr 19 '16 at 19:20
  • @JimB @william.taylor.09 sorry, my program is quite large. `handleMessage` is called every iteration in the infinite loop of the main thread. –  Apr 19 '16 at 19:57
  • If your program is blocked, look at the stack trace to see where each goroutine is waiting, or add more debug statements to see what is progressing. We can't troubleshoot this without a way to reproduce your error. – JimB Apr 19 '16 at 21:14

0 Answers0