Skip to content

Instantly share code, notes, and snippets.

@nwaywood
Last active February 12, 2020 02:49
Show Gist options
  • Save nwaywood/ed3090b216f0aae378e20e7b97c11a51 to your computer and use it in GitHub Desktop.
Save nwaywood/ed3090b216f0aae378e20e7b97c11a51 to your computer and use it in GitHub Desktop.
Concurrency and State in Haskell

TLDR: I am trying to implement the equivalent of this handler struct in Haskell, which involves mutable variables and concurrency and I'm not sure how to go about implementing it

Question

I need to implement the equivalent of this Golang handler in Haskell:

// Handler handler implementation for shim side of chaincode.
type Handler struct {
	// serialLock is used to prevent concurrent calls to Send on the
	// PeerChaincodeStream. This is required by gRPC.
	serialLock sync.Mutex
	// chatStream is the client used to access the chaincode support server on
	// the peer.
	chatStream PeerChaincodeStream

	// cc is the chaincode associated with this handler.
	cc Chaincode
	// state holds the current state of this handler.
	state state

	// Multiple queries (and one transaction) with different txids can be executing in parallel for this chaincode
	// responseChannels is the channel on which responses are communicated by the shim to the chaincodeStub.
	// need lock to protect chaincode from attempting
	// concurrent requests to the peer
	responseChannelsMutex sync.Mutex
	responseChannels      map[string]chan pb.ChaincodeMessage
}

The snippet above has two things of note:

  • It contains mutable state, state, which is essentially a FSM to keep track of the current state of the handler
  • A mutex lock, responseChannelsMutex, which is used to lock/unlock the responseChannels variable which is accessed by multiple threads.

The handleResponse function is a typical example of how the responseChannelsMutex lock is used:

func (h *Handler) handleResponse(msg *pb.ChaincodeMessage) error {
	h.responseChannelsMutex.Lock()
	defer h.responseChannelsMutex.Unlock()

	if h.responseChannels == nil {
		return fmt.Errorf("[%s] Cannot send message response channel", shorttxid(msg.Txid))
	}

	txCtxID := transactionContextID(msg.ChannelId, msg.Txid)
	responseCh := h.responseChannels[txCtxID]
	if responseCh == nil {
		return fmt.Errorf("[%s] responseChannel does not exist", shorttxid(msg.Txid))
	}
	responseCh <- *msg
	return nil
}

What I have learnt so far/where I am at

There are two parts of porting this handler to Haskell that I am not sure how to do, the state management and the concurrency.

State

Regarding how to keep mutable state, my first instinct was to reach for the State/StateT monad which was covered in the data61/fp-course. But after some investigation discovered that the State monad is not thread safe and therefore is not suitable for this use case (I think).

Which lead me looking into ways to handle state in a multi-threaded context...

Concurrency

I have never done any concurrency in Haskell before and am finding all the different options quite overwhelming.

For example:

  • MVar vs TVar vs IORef
  • forkIO vs async
  • CSP vs STM
  • The StateM monad as an alternative?

To try and make it less confusing I decided to try and emulate Golang's CSP channel/goroutine approach to the concurrency so I have code to model the architecture off. But that lead me to finding out that the Control.Concurrent.Chan, Control.Concurrent.STM and Control.Concurrent.chp packages are have different types of channels.

Conclusion

Long story short is that I am overwhelmed trying to figure out how to implement a "handler" pattern in Haskell which uses state and concurrency.

Any guidance/help/recommendations would be greatly appreciated!

For reference, I consider myself a beginner-intermediate Haskeller (I just finished the qfpl/applied-fp-course)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment