fix panic on shutdown caused by double channel close (#209)
This commit is contained in:
20
service.go
20
service.go
@@ -99,6 +99,7 @@ type service struct {
|
|||||||
done chan struct{}
|
done chan struct{}
|
||||||
opts Options
|
opts Options
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
|
stopped bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewService creates and returns a new Service based on the packages within.
|
// NewService creates and returns a new Service based on the packages within.
|
||||||
@@ -424,7 +425,7 @@ func (s *service) Stop() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
close(s.done)
|
s.notifyShutdown()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -448,10 +449,23 @@ func (s *service) Run() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// wait on context cancel
|
|
||||||
<-s.done
|
<-s.done
|
||||||
|
|
||||||
return s.Stop()
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// notifyShutdown marks the service as stopped and closes the done channel.
|
||||||
|
// It ensures the channel is closed only once, preventing multiple closures.
|
||||||
|
func (s *service) notifyShutdown() {
|
||||||
|
s.Lock()
|
||||||
|
if s.stopped {
|
||||||
|
s.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.stopped = true
|
||||||
|
s.Unlock()
|
||||||
|
|
||||||
|
close(s.done)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Namer interface {
|
type Namer interface {
|
||||||
|
|||||||
@@ -3,7 +3,9 @@ package micro
|
|||||||
import (
|
import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
"go.unistack.org/micro/v4/broker"
|
"go.unistack.org/micro/v4/broker"
|
||||||
"go.unistack.org/micro/v4/client"
|
"go.unistack.org/micro/v4/client"
|
||||||
"go.unistack.org/micro/v4/config"
|
"go.unistack.org/micro/v4/config"
|
||||||
@@ -737,3 +739,41 @@ func Test_getNameIndex(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
func TestServiceShutdown(t *testing.T) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
t.Fatalf("service shutdown failed: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
s, ok := NewService().(*service)
|
||||||
|
require.NotNil(t, s)
|
||||||
|
require.True(t, ok)
|
||||||
|
|
||||||
|
require.NoError(t, s.Start())
|
||||||
|
require.False(t, s.stopped)
|
||||||
|
|
||||||
|
require.NoError(t, s.Stop())
|
||||||
|
require.True(t, s.stopped)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestServiceMultipleShutdowns(t *testing.T) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
t.Fatalf("service shutdown failed: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
s := NewService()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
// first call
|
||||||
|
require.NoError(t, s.Stop())
|
||||||
|
// duplicate call
|
||||||
|
require.NoError(t, s.Stop())
|
||||||
|
}()
|
||||||
|
|
||||||
|
require.NoError(t, s.Run())
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user