sync/waitgroup: initial sync.WaitGroup wrapper with context support #319
| @@ -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{ | ||||
| @@ -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) { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user