sync/waitgroup: initial sync.WaitGroup wrapper with context support (#319)
Some checks failed
/ autoupdate (push) Failing after 1m12s
Some checks failed
/ autoupdate (push) Failing after 1m12s
Reviewed-on: #319 Co-authored-by: Vasiliy Tolstov <v.tolstov@unistack.org> Co-committed-by: Vasiliy Tolstov <v.tolstov@unistack.org>
This commit is contained in:
parent
e416a5b38c
commit
856aac4aa9
@ -1,6 +1,5 @@
|
||||
run:
|
||||
concurrency: 4
|
||||
deadline: 5m
|
||||
issues-exit-code: 1
|
||||
tests: true
|
||||
|
||||
@ -13,15 +12,13 @@ linters-settings:
|
||||
linters:
|
||||
enable:
|
||||
- govet
|
||||
- deadcode
|
||||
- errcheck
|
||||
- govet
|
||||
- ineffassign
|
||||
- staticcheck
|
||||
- structcheck
|
||||
- typecheck
|
||||
- unused
|
||||
- varcheck
|
||||
- spancheck
|
||||
- bodyclose
|
||||
- gci
|
||||
- goconst
|
||||
@ -41,4 +38,5 @@ linters:
|
||||
- prealloc
|
||||
- unconvert
|
||||
- unparam
|
||||
- unused
|
||||
disable-all: false
|
||||
|
@ -58,14 +58,14 @@ func NewOptions(opts ...options.Option) Options {
|
||||
type PublishOptions struct {
|
||||
// Context holds external options
|
||||
Context context.Context
|
||||
// BodyOnly flag says the message contains raw body bytes
|
||||
BodyOnly bool
|
||||
// Message metadata usually passed as message headers
|
||||
Metadata metadata.Metadata
|
||||
// Content-Type of message for marshal
|
||||
ContentType string
|
||||
// Topic destination
|
||||
Topic string
|
||||
// BodyOnly flag says the message contains raw body bytes
|
||||
BodyOnly bool
|
||||
}
|
||||
|
||||
// NewPublishOptions creates PublishOptions struct
|
||||
|
@ -19,8 +19,8 @@ var typeOfError = reflect.TypeOf((*error)(nil)).Elem()
|
||||
|
||||
// Is this an exported - upper case - name?
|
||||
func isExported(name string) bool {
|
||||
rune, _ := utf8.DecodeRuneInString(name)
|
||||
return unicode.IsUpper(rune)
|
||||
r, _ := utf8.DecodeRuneInString(name)
|
||||
return unicode.IsUpper(r)
|
||||
}
|
||||
|
||||
// Is this type exported or a builtin?
|
||||
|
@ -75,7 +75,10 @@ func ParseDSN(dsn string) (*Config, error) {
|
||||
// Find last '/' that goes before dbname
|
||||
foundSlash := false
|
||||
for i := len(dsn) - 1; i >= 0; i-- {
|
||||
if dsn[i] == '/' {
|
||||
if dsn[i] != '/' {
|
||||
continue
|
||||
}
|
||||
|
||||
foundSlash = true
|
||||
var j, k int
|
||||
|
||||
@ -147,7 +150,6 @@ func ParseDSN(dsn string) (*Config, error) {
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !foundSlash && len(dsn) > 0 {
|
||||
return nil, ErrInvalidDSNNoSlash
|
||||
|
13
go.mod
13
go.mod
@ -5,17 +5,16 @@ go 1.20
|
||||
require (
|
||||
dario.cat/mergo v1.0.0
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.0
|
||||
github.com/google/uuid v1.3.1
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||
github.com/silas/dag v0.0.0-20220518035006-a7e85ada93c5
|
||||
golang.org/x/sync v0.3.0
|
||||
golang.org/x/sys v0.12.0
|
||||
google.golang.org/grpc v1.58.2
|
||||
google.golang.org/protobuf v1.31.0
|
||||
golang.org/x/sync v0.6.0
|
||||
golang.org/x/sys v0.16.0
|
||||
google.golang.org/grpc v1.62.1
|
||||
google.golang.org/protobuf v1.32.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
golang.org/x/net v0.15.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect
|
||||
)
|
||||
|
31
go.sum
31
go.sum
@ -6,29 +6,28 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
|
||||
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||
github.com/silas/dag v0.0.0-20220518035006-a7e85ada93c5 h1:G/FZtUu7a6NTWl3KUHMV9jkLAh/Rvtf03NWMHaEDl+E=
|
||||
github.com/silas/dag v0.0.0-20220518035006-a7e85ada93c5/go.mod h1:7RTUFBdIRC9nZ7/3RyRNH1bdqIShrDejd1YbLwgPS+I=
|
||||
golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
|
||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
|
||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 h1:bVf09lpb+OJbByTj913DRJioFFAjf/ZGxEz7MajTp2U=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM=
|
||||
google.golang.org/grpc v1.58.2 h1:SXUpjxeVF3FKrTYQI4f4KvbGD5u2xccdYdurwowix5I=
|
||||
google.golang.org/grpc v1.58.2/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 h1:AjyfHzEPEFp/NpvfN5g+KDla3EMojjhRVZc1i7cj+oM=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s=
|
||||
google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk=
|
||||
google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
|
||||
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
@ -18,18 +18,12 @@ type Options struct {
|
||||
Out io.Writer
|
||||
// Context holds exernal options
|
||||
Context context.Context
|
||||
// Attrs holds additional attributes
|
||||
Attrs []interface{}
|
||||
// Name holds the logger name
|
||||
Name string
|
||||
// The logging level the logger should log
|
||||
Level Level
|
||||
// CallerSkipCount number of frmaes to skip
|
||||
CallerSkipCount int
|
||||
// ContextAttrFuncs contains funcs that executed before log func on context
|
||||
ContextAttrFuncs []ContextAttrFunc
|
||||
// TimeFunc used to obtain current time
|
||||
TimeFunc func() time.Time
|
||||
// TimeKey is the key used for the time of the log call
|
||||
TimeKey string
|
||||
// Name holds the logger name
|
||||
Name string
|
||||
// LevelKey is the key used for the level of the log call
|
||||
LevelKey string
|
||||
// MessageKey is the key used for the message of the log call
|
||||
@ -40,12 +34,18 @@ type Options struct {
|
||||
SourceKey string
|
||||
// StacktraceKey is the key used for the stacktrace
|
||||
StacktraceKey string
|
||||
// Attrs holds additional attributes
|
||||
Attrs []interface{}
|
||||
// ContextAttrFuncs contains funcs that executed before log func on context
|
||||
ContextAttrFuncs []ContextAttrFunc
|
||||
// CallerSkipCount number of frmaes to skip
|
||||
CallerSkipCount int
|
||||
// The logging level the logger should log
|
||||
Level Level
|
||||
// AddStacktrace controls writing of stacktaces on error
|
||||
AddStacktrace bool
|
||||
// AddSource enabled writing source file and position in log
|
||||
AddSource bool
|
||||
// TimeFunc used to obtain current time
|
||||
TimeFunc func() time.Time
|
||||
}
|
||||
|
||||
// NewOptions creates new options struct
|
||||
|
@ -56,9 +56,9 @@ type Wrapper struct {
|
||||
s fmt.State
|
||||
pointers map[uintptr]int
|
||||
opts *Options
|
||||
takeMap map[int]bool
|
||||
depth int
|
||||
ignoreNextType bool
|
||||
takeMap map[int]bool
|
||||
protoWrapperType bool
|
||||
sqlWrapperType bool
|
||||
}
|
||||
|
@ -54,7 +54,24 @@ func testOutgoingCtx(ctx context.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestPassing(t *testing.T) {
|
||||
func TestIncoming(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
md1 := New(2)
|
||||
md1.Set("Key1", "Val1")
|
||||
md1.Set("Key2", "Val2")
|
||||
|
||||
ctx = NewIncomingContext(ctx, md1)
|
||||
testIncomingCtx(ctx)
|
||||
md, ok := FromIncomingContext(ctx)
|
||||
if !ok {
|
||||
t.Fatalf("missing metadata from incoming context")
|
||||
}
|
||||
if v, ok := md.Get("Key1"); !ok || v != "Val1_new" {
|
||||
t.Fatalf("invalid metadata value %#+v", md)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOutgoing(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
md1 := New(2)
|
||||
md1.Set("Key1", "Val1")
|
||||
|
36
micro.go
36
micro.go
@ -65,6 +65,8 @@ func As(b any, target any) bool {
|
||||
break
|
||||
case targetType.Implements(routerType):
|
||||
break
|
||||
case targetType.Implements(tracerType):
|
||||
break
|
||||
default:
|
||||
return false
|
||||
}
|
||||
@ -76,19 +78,21 @@ func As(b any, target any) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
var brokerType = reflect.TypeOf((*broker.Broker)(nil)).Elem()
|
||||
var loggerType = reflect.TypeOf((*logger.Logger)(nil)).Elem()
|
||||
var clientType = reflect.TypeOf((*client.Client)(nil)).Elem()
|
||||
var serverType = reflect.TypeOf((*server.Server)(nil)).Elem()
|
||||
var codecType = reflect.TypeOf((*codec.Codec)(nil)).Elem()
|
||||
var flowType = reflect.TypeOf((*flow.Flow)(nil)).Elem()
|
||||
var fsmType = reflect.TypeOf((*fsm.FSM)(nil)).Elem()
|
||||
var meterType = reflect.TypeOf((*meter.Meter)(nil)).Elem()
|
||||
var registerType = reflect.TypeOf((*register.Register)(nil)).Elem()
|
||||
var resolverType = reflect.TypeOf((*resolver.Resolver)(nil)).Elem()
|
||||
var routerType = reflect.TypeOf((*router.Router)(nil)).Elem()
|
||||
var selectorType = reflect.TypeOf((*selector.Selector)(nil)).Elem()
|
||||
var storeType = reflect.TypeOf((*store.Store)(nil)).Elem()
|
||||
var syncType = reflect.TypeOf((*sync.Sync)(nil)).Elem()
|
||||
var tracerType = reflect.TypeOf((*tracer.Tracer)(nil)).Elem()
|
||||
var serviceType = reflect.TypeOf((*Service)(nil)).Elem()
|
||||
var (
|
||||
brokerType = reflect.TypeOf((*broker.Broker)(nil)).Elem()
|
||||
loggerType = reflect.TypeOf((*logger.Logger)(nil)).Elem()
|
||||
clientType = reflect.TypeOf((*client.Client)(nil)).Elem()
|
||||
serverType = reflect.TypeOf((*server.Server)(nil)).Elem()
|
||||
codecType = reflect.TypeOf((*codec.Codec)(nil)).Elem()
|
||||
flowType = reflect.TypeOf((*flow.Flow)(nil)).Elem()
|
||||
fsmType = reflect.TypeOf((*fsm.FSM)(nil)).Elem()
|
||||
meterType = reflect.TypeOf((*meter.Meter)(nil)).Elem()
|
||||
registerType = reflect.TypeOf((*register.Register)(nil)).Elem()
|
||||
resolverType = reflect.TypeOf((*resolver.Resolver)(nil)).Elem()
|
||||
routerType = reflect.TypeOf((*router.Router)(nil)).Elem()
|
||||
selectorType = reflect.TypeOf((*selector.Selector)(nil)).Elem()
|
||||
storeType = reflect.TypeOf((*store.Store)(nil)).Elem()
|
||||
syncType = reflect.TypeOf((*sync.Sync)(nil)).Elem()
|
||||
tracerType = reflect.TypeOf((*tracer.Tracer)(nil)).Elem()
|
||||
serviceType = reflect.TypeOf((*Service)(nil)).Elem()
|
||||
)
|
||||
|
@ -1,7 +1,6 @@
|
||||
package options_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"go.unistack.org/micro/v4/codec"
|
||||
@ -132,7 +131,6 @@ func TestMetadataAny(t *testing.T) {
|
||||
var opts []options.Option
|
||||
switch valData := tt.Data.(type) {
|
||||
case []any:
|
||||
fmt.Printf("%s any %#+v\n", tt.Name, valData)
|
||||
opts = append(opts, options.Metadata(valData...))
|
||||
case map[string]string, map[string][]string, metadata.Metadata:
|
||||
opts = append(opts, options.Metadata(valData))
|
||||
|
@ -32,10 +32,10 @@ type record struct {
|
||||
}
|
||||
|
||||
type memory struct {
|
||||
sync.RWMutex
|
||||
records map[string]services
|
||||
watchers map[string]*watcher
|
||||
opts register.Options
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
// services is a KV map with service name as the key and a map of records as the value
|
||||
@ -102,10 +102,20 @@ func (m *memory) sendEvent(r *register.Result) {
|
||||
}
|
||||
|
||||
func (m *memory) Connect(ctx context.Context) error {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *memory) Disconnect(ctx context.Context) error {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -126,6 +136,11 @@ func (m *memory) Options() register.Options {
|
||||
}
|
||||
|
||||
func (m *memory) Register(ctx context.Context, s *register.Service, opts ...register.RegisterOption) error {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
}
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
|
||||
@ -467,9 +482,7 @@ func serviceToRecord(s *register.Service, ttl time.Duration) *record {
|
||||
}
|
||||
|
||||
endpoints := make([]*register.Endpoint, len(s.Endpoints))
|
||||
for i, e := range s.Endpoints { // TODO: vtolstov use copy
|
||||
endpoints[i] = e
|
||||
}
|
||||
copy(endpoints, s.Endpoints)
|
||||
|
||||
return &record{
|
||||
Name: s.Name,
|
||||
|
@ -3,12 +3,13 @@ package memory
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"go.unistack.org/micro/v4"
|
||||
"go.unistack.org/micro/v4/register"
|
||||
"reflect"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"go.unistack.org/micro/v4"
|
||||
"go.unistack.org/micro/v4/register"
|
||||
)
|
||||
|
||||
var testData = map[string][]*register.Service{
|
||||
@ -209,9 +210,9 @@ func TestMemoryRegistryTTLConcurrent(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
//if len(os.Getenv("IN_TRAVIS_CI")) == 0 {
|
||||
// if len(os.Getenv("IN_TRAVIS_CI")) == 0 {
|
||||
// t.Logf("test will wait %v, then check TTL timeouts", waitTime)
|
||||
//}
|
||||
// }
|
||||
|
||||
errChan := make(chan error, concurrency)
|
||||
syncChan := make(chan struct{})
|
||||
@ -252,6 +253,13 @@ func TestMemoryWildcard(t *testing.T) {
|
||||
m := NewRegister()
|
||||
ctx := context.TODO()
|
||||
|
||||
if err := m.Init(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := m.Connect(ctx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
testSrv := ®ister.Service{Name: "foo", Version: "1.0.0"}
|
||||
|
||||
if err := m.Register(ctx, testSrv, register.RegisterDomain("one")); err != nil {
|
||||
@ -291,8 +299,12 @@ func TestWatcher(t *testing.T) {
|
||||
|
||||
ctx := context.TODO()
|
||||
m := NewRegister()
|
||||
m.Init()
|
||||
m.Connect(ctx)
|
||||
if err := m.Init(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := m.Connect(ctx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
wc, err := m.Watch(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("cant watch: %v", err)
|
||||
|
69
sync/waitgroup.go
Normal file
69
sync/waitgroup.go
Normal file
@ -0,0 +1,69 @@
|
||||
package sync
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type WaitGroup struct {
|
||||
wg *sync.WaitGroup
|
||||
c int
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func WrapWaitGroup(wg *sync.WaitGroup) *WaitGroup {
|
||||
g := &WaitGroup{
|
||||
wg: wg,
|
||||
}
|
||||
return g
|
||||
}
|
||||
|
||||
func NewWaitGroup() *WaitGroup {
|
||||
var wg sync.WaitGroup
|
||||
return WrapWaitGroup(&wg)
|
||||
}
|
||||
|
||||
func (g *WaitGroup) Add(n int) {
|
||||
g.mu.Lock()
|
||||
g.c += n
|
||||
g.wg.Add(n)
|
||||
g.mu.Unlock()
|
||||
}
|
||||
|
||||
func (g *WaitGroup) Done() {
|
||||
g.mu.Lock()
|
||||
g.c += -1
|
||||
g.wg.Add(-1)
|
||||
g.mu.Unlock()
|
||||
}
|
||||
|
||||
func (g *WaitGroup) Wait() {
|
||||
g.wg.Wait()
|
||||
}
|
||||
|
||||
func (g *WaitGroup) WaitContext(ctx context.Context) {
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
g.wg.Wait()
|
||||
close(done)
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
g.mu.Lock()
|
||||
g.wg.Add(-g.c)
|
||||
<-done
|
||||
g.wg.Add(g.c)
|
||||
g.mu.Unlock()
|
||||
return
|
||||
case <-done:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (g *WaitGroup) Waiters() int {
|
||||
g.mu.Lock()
|
||||
c := g.c
|
||||
g.mu.Unlock()
|
||||
return c
|
||||
}
|
37
sync/waitgroup_test.go
Normal file
37
sync/waitgroup_test.go
Normal file
@ -0,0 +1,37 @@
|
||||
package sync
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestWaitGroupContext(t *testing.T) {
|
||||
wg := NewWaitGroup()
|
||||
_ = t
|
||||
wg.Add(1)
|
||||
ctx, cancel := context.WithTimeout(context.TODO(), 1*time.Second)
|
||||
defer cancel()
|
||||
wg.WaitContext(ctx)
|
||||
}
|
||||
|
||||
func TestWaitGroupReuse(t *testing.T) {
|
||||
wg := NewWaitGroup()
|
||||
defer func() {
|
||||
if wg.Waiters() != 0 {
|
||||
t.Fatal("lost goroutines")
|
||||
}
|
||||
}()
|
||||
|
||||
wg.Add(1)
|
||||
defer wg.Done()
|
||||
ctx, cancel := context.WithTimeout(context.TODO(), 1*time.Second)
|
||||
defer cancel()
|
||||
wg.WaitContext(ctx)
|
||||
|
||||
wg.Add(1)
|
||||
defer wg.Done()
|
||||
ctx, cancel = context.WithTimeout(context.TODO(), 1*time.Second)
|
||||
defer cancel()
|
||||
wg.WaitContext(ctx)
|
||||
}
|
@ -109,13 +109,12 @@ func Merge(olist []*register.Service, nlist []*register.Service) []*register.Ser
|
||||
seen = true
|
||||
srv = append(srv, sp)
|
||||
break
|
||||
} else {
|
||||
}
|
||||
sp := ®ister.Service{}
|
||||
// make copy
|
||||
*sp = *o
|
||||
srv = append(srv, sp)
|
||||
}
|
||||
}
|
||||
if !seen {
|
||||
srv = append(srv, Copy([]*register.Service{n})...)
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ type EC2Metadata struct {
|
||||
InstanceType string `json:"instance-type"`
|
||||
LocalHostname string `json:"local-hostname"`
|
||||
LocalIPv4 string `json:"local-ipv4"`
|
||||
kernelID int `json:"kernel-id"`
|
||||
KernelID int `json:"kernel-id"`
|
||||
Placement string `json:"placement"`
|
||||
AvailabilityZone string `json:"availability-zone"`
|
||||
ProductCodes string `json:"product-codes"`
|
||||
|
@ -67,9 +67,9 @@ func (fi *fileInfo) Name() string {
|
||||
|
||||
func (fi *fileInfo) Mode() os.FileMode {
|
||||
if strings.HasSuffix(fi.name, "/") {
|
||||
return os.FileMode(0755) | os.ModeDir
|
||||
return os.FileMode(0o755) | os.ModeDir
|
||||
}
|
||||
return os.FileMode(0644)
|
||||
return os.FileMode(0o644)
|
||||
}
|
||||
|
||||
func (fi *fileInfo) IsDir() bool {
|
||||
@ -112,15 +112,14 @@ func (f *file) Readdir(count int) ([]os.FileInfo, error) {
|
||||
func (f *file) Seek(offset int64, whence int) (int64, error) {
|
||||
// log.Printf("seek %d %d %s\n", offset, whence, f.name)
|
||||
switch whence {
|
||||
case os.SEEK_SET:
|
||||
case io.SeekStart:
|
||||
f.offset = offset
|
||||
case os.SEEK_CUR:
|
||||
case io.SeekCurrent:
|
||||
f.offset += offset
|
||||
case os.SEEK_END:
|
||||
case io.SeekEnd:
|
||||
f.offset = int64(len(f.data)) + offset
|
||||
}
|
||||
return f.offset, nil
|
||||
|
||||
}
|
||||
|
||||
func (f *file) Stat() (os.FileInfo, error) {
|
||||
|
@ -2,7 +2,7 @@ package structfs
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"testing"
|
||||
@ -82,7 +82,7 @@ func get(path string) ([]byte, error) {
|
||||
return nil, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
return ioutil.ReadAll(res.Body)
|
||||
return io.ReadAll(res.Body)
|
||||
}
|
||||
|
||||
func TestAll(t *testing.T) {
|
||||
|
Loading…
Reference in New Issue
Block a user