Documentation
¶
Overview ¶
Package genserver provides a generic server process inspired by Elixir's OTP GenServer. State is owned by a single goroutine; Call and Cast messages are serialized through a single channel to preserve ordering.
Call is synchronous: the caller blocks until HandleCall returns a response. Cast is asynchronous: the caller returns immediately after sending.
Without tail-call optimization in Go, the server loop is an explicit channel select rather than a recursive function, but the semantics are equivalent.
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type GenServer ¶
type GenServer[S, Req, Resp any] struct { // contains filtered or unexported fields }
GenServer is a running server process. All operations are goroutine-safe. Methods must not be called after Stop.
func Start ¶
Start initialises the server by calling srv.Init() and launches the message loop in a new goroutine.
func (*GenServer[S, Req, Resp]) Call ¶
func (g *GenServer[S, Req, Resp]) Call(req Req) Resp
Call sends a synchronous request and blocks until the server returns a response.
Example ¶
package main
import (
"fmt"
"github.com/mikehelmick/go-functional/genserver"
)
type counterCmd int
const (
cmdGet counterCmd = iota
cmdIncrement
cmdAdd
cmdReset
)
type counterReq struct {
cmd counterCmd
val int
}
// counterServer is a simple integer counter implementing genserver.Server.
type counterServer struct{}
func (counterServer) Init() int { return 0 }
func (counterServer) HandleCall(req counterReq, state int) (int, int) {
switch req.cmd {
case cmdGet:
return state, state
case cmdIncrement:
return state + 1, state + 1
case cmdAdd, cmdReset:
return state, state
}
return state, state
}
func (counterServer) HandleCast(req counterReq, state int) int {
switch req.cmd {
case cmdAdd:
return state + req.val
case cmdReset:
return 0
case cmdGet, cmdIncrement:
return state
}
return state
}
func main() {
gs := genserver.Start[int, counterReq, int](counterServer{})
defer gs.Stop()
gs.Cast(counterReq{cmd: cmdAdd, val: 10})
gs.Call(counterReq{cmd: cmdIncrement})
fmt.Println(gs.Call(counterReq{cmd: cmdGet}))
}
Output: 11
type Server ¶
type Server[S, Req, Resp any] interface { // Init returns the initial state of the server. Init() S // HandleCall processes a synchronous request. It returns a response // sent back to the caller and the updated state. HandleCall(req Req, state S) (Resp, S) // HandleCast processes an asynchronous message. It returns the // updated state. No response is sent to the caller. HandleCast(msg Req, state S) S }
Server defines the callbacks for a GenServer process. S is the state type, Req is the request/message type, and Resp is the response type returned by synchronous calls.