Compare commits

...

2 Commits

Author SHA1 Message Date
Jan Dittberner 4345e5d899 Implement graceful shutdown of client
Install handlers for SIGTERM and command line interrupt.
1 year ago
Jan Dittberner 9c608ed81f Fix potential race condition in client
Synchronize go routines in client.Run to make sure to avoid access to the
common context before use.
1 year ago

@ -24,7 +24,9 @@ import (
"fmt"
"io"
"os"
"os/signal"
"strings"
"syscall"
"github.com/sirupsen/logrus"
"gopkg.in/yaml.v3"
@ -150,7 +152,21 @@ func startClient(configFile string, logger *logrus.Logger) error {
logger.Info("setup complete, starting client operation")
if err = signerClient.Run(context.Background()); err != nil {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
go func() {
<-c
logger.Info("received shutdown signal")
cancel()
}()
if err = signerClient.Run(ctx); err != nil {
return fmt.Errorf("error in client: %w", err)
}

@ -3,7 +3,7 @@ module git.cacert.org/cacert-gosignerclient
go 1.19
require (
git.cacert.org/cacert-gosigner v0.0.0-20221203104439-bc81ab84cb4a
git.cacert.org/cacert-gosigner v0.0.0-20221203123337-46407b368528
github.com/balacode/go-delta v0.1.0
github.com/shamaton/msgpackgen v0.3.0
github.com/sirupsen/logrus v1.9.0

@ -6,6 +6,8 @@ git.cacert.org/cacert-gosigner v0.0.0-20221202173159-afe7d23c9b6f h1:VcIwyogvdmY
git.cacert.org/cacert-gosigner v0.0.0-20221202173159-afe7d23c9b6f/go.mod h1:OGIB5wLUhJiBhTzSXReOhGxuy7sT5VvyOyT8Ux8EGyw=
git.cacert.org/cacert-gosigner v0.0.0-20221203104439-bc81ab84cb4a h1:yX3lhEoBQkUKu23xggAzAeYWuziCkRYktSjsAOfNGHY=
git.cacert.org/cacert-gosigner v0.0.0-20221203104439-bc81ab84cb4a/go.mod h1:OGIB5wLUhJiBhTzSXReOhGxuy7sT5VvyOyT8Ux8EGyw=
git.cacert.org/cacert-gosigner v0.0.0-20221203123337-46407b368528 h1:W1K/YiNp8ganr2GuOpoNC+ZcaVsG15tczpNuvAjHn+U=
git.cacert.org/cacert-gosigner v0.0.0-20221203123337-46407b368528/go.mod h1:OGIB5wLUhJiBhTzSXReOhGxuy7sT5VvyOyT8Ux8EGyw=
github.com/balacode/go-delta v0.1.0 h1:pwz4CMn06P2bIaIfAx3GSabMPwJp/Ww4if+7SgPYa3I=
github.com/balacode/go-delta v0.1.0/go.mod h1:wLNrwTI3lHbPBvnLzqbHmA7HVVlm1u22XLvhbeA6t3o=
github.com/balacode/zr v1.0.0/go.mod h1:pLeSAL3DhZ9L0JuiRkUtIX3mLOCtzBLnDhfmykbSmkE=

@ -91,44 +91,81 @@ type Client struct {
}
func (c *Client) Run(ctx context.Context) error {
protocolErrors := make(chan error)
framerErrors := make(chan error)
const componentCount = 4
protocolErrors, framerErrors := make(chan error), make(chan error)
subCtx, cancel := context.WithCancel(ctx)
wg := sync.WaitGroup{}
wg.Add(componentCount)
defer func() {
cancel()
c.logger.Info("context canceled, waiting for shutdown of components")
wg.Wait()
c.logger.Info("shutdown complete")
}()
go func(f protocol.Framer) {
framerErrors <- f.ReadFrames(ctx, c.port, c.in)
defer wg.Done()
err := f.ReadFrames(subCtx, c.port, c.in)
c.logger.Info("frame reading stopped")
select {
case framerErrors <- err:
case <-subCtx.Done():
}
}(c.framer)
go func(f protocol.Framer) {
framerErrors <- f.WriteFrames(ctx, c.port, c.out)
defer wg.Done()
err := f.WriteFrames(subCtx, c.port, c.out)
c.logger.Info("frame writing stopped")
select {
case framerErrors <- err:
case <-subCtx.Done():
}
}(c.framer)
go func() {
defer wg.Done()
clientProtocol := protocol.NewClient(c.handler, c.commands, c.in, c.out, c.logger)
protocolErrors <- clientProtocol.Handle(ctx)
err := clientProtocol.Handle(subCtx)
c.logger.Info("client protocol stopped")
select {
case protocolErrors <- err:
case <-subCtx.Done():
}
}()
ctx, cancelCommandLoop := context.WithCancel(ctx)
go func() {
defer wg.Done()
c.commandLoop(subCtx)
go c.commandLoop(ctx)
c.logger.Info("client command loop stopped")
}()
for {
select {
case <-ctx.Done():
cancelCommandLoop()
return nil
case err := <-framerErrors:
cancelCommandLoop()
if err != nil {
return fmt.Errorf("error from framer: %w", err)
}
return nil
case err := <-protocolErrors:
cancelCommandLoop()
if err != nil {
return fmt.Errorf("error from protocol: %w", err)
}

Loading…
Cancel
Save