Concurrency tidbit to GO

Consider the code snippet below- it creates a chat room struct with the following fields:

  1. a channel (messageFlow) to forward incoming messages to the room
  2. a channel (joinChat) to queue clients who want to join the room
  3. a channel (quitChat) for clients who want to leave the room
  4. a map of clients who are currently in the room

 

type room struct{
      messageFlow chan []byte
     // joinChat - channel for clients wanting to join the chat
     joinChat chan *client
     // quitChat - channel for clients wanting to leave a room
     quitChat chan *client
     // clients - a map object that holds all current clients in a room
     currentClients map[*clients] bool
}

Quick GO channel refresher – Channels are a typed conduit through which we can send and receive values with the channel operator, “<-“. All channels must be created before use. And by default, sends and receives block until the other side is ready. This allows goroutines to synchronize without explicit locks or condition variables.
More details on channels in GO is here: https://tour.golang.org/concurrency/2

 

Quick GO map refresher – A map maps keys to values. More on maps in GO – https://tour.golang.org/moretypes/19
The usual problem with the code like one above is that it is possible that two goroutines may try to modify the map at the same time thus resulting in an unpredictable state for the currentClients map.

To help mitigate this kind of setup, GO provides a powerful statement called select. As defined here (https://tour.golang.org/concurrency/5)The select statement lets a goroutine wait on multiple communication operations. Select statement can be used whenever we need to perform some operations on shared memory or actions that depend on various activities within the channels.

To address the case in the context of the code snippet above, we can use the select statement to monitor the channels: messageFlow, joinChat, quitClients. As and when a message arrives in any of the of the channels, the select statement will run the code for that particular case. Only the case related to any one channel will be run at any particular time – thus helping synchronize the operations. The select code will look something like:

::::::::::::::
    select{
    case client := <- room.joinChat:
        //do something to allow the client to join in
    case client := <-room.quitChat:
        //do domething to allow the client to leave
    case chatMsg := <- room.messageFlow
}
::::::::::::::

This code should run indefinitely in the background (as goroutines) till the chat program is terminated.

References:
1) GO Programming Blueprints – Mat Ryer
2) https://tour.golang.org/