diff --git a/zjson/get.go b/zjson/get.go index ef5772d..6a110ed 100644 --- a/zjson/get.go +++ b/zjson/get.go @@ -12,7 +12,6 @@ import ( "unicode/utf16" "unicode/utf8" - "github.com/sohaha/zlsgo/zlog" "github.com/sohaha/zlsgo/zreflect" "github.com/sohaha/zlsgo/zstring" "github.com/sohaha/zlsgo/ztime" @@ -1987,7 +1986,6 @@ func assign(jsval *Res, val reflect.Value, fmap *fieldMaps) { s := key.Kind() == reflect.String if s { kind := t.Elem().Kind() - zlog.Debug(kind) switch kind { case reflect.Interface: val.Set(zreflect.ValueOf(jsval.Value())) diff --git a/zsync/content.go b/zsync/content.go new file mode 100644 index 0000000..9878c9d --- /dev/null +++ b/zsync/content.go @@ -0,0 +1,105 @@ +package zsync + +import ( + "context" + "sync" + "time" +) + +// MergeContext merges multiple contexts into a single one +func MergeContext(ctxs ...context.Context) Context { + if len(ctxs) == 0 { + return context.Background() + } + + mc := mergeContext{ + ctxs: ctxs, + doneCh: make(chan struct{}), + doneIndex: -1, + } + go mc.monitor() + + return &mc +} + +type Context interface { + context.Context +} + +type mergeContext struct { + ctxs []context.Context + doneCh chan struct{} + doneIndex int + err error +} + +func (mc *mergeContext) Deadline() (time.Time, bool) { + dl := time.Time{} + for _, ctx := range mc.ctxs { + thisDL, ok := ctx.Deadline() + if ok { + if dl.IsZero() { + dl = thisDL + } else if thisDL.Before(dl) { + dl = thisDL + } + } + } + return dl, !dl.IsZero() +} + +func (mc *mergeContext) Done() <-chan struct{} { + return mc.doneCh +} + +func (mc *mergeContext) Err() error { + return mc.err +} + +func (mc *mergeContext) Value(key any) any { + for _, ctx := range mc.ctxs { + if v := ctx.Value(key); v != nil { + return v + } + } + return nil +} + +func (mc *mergeContext) monitor() { + winner := multiselect(mc.ctxs) + + mc.doneIndex = winner + mc.err = mc.ctxs[winner].Err() + + close(mc.doneCh) +} + +func multiselect(ctxs []context.Context) int { + res := make(chan int) + + count := len(ctxs) + if count == 1 { + <-ctxs[0].Done() + return 0 + } + + var wg sync.WaitGroup + wg.Add(count) + + for i, ctx := range ctxs { + go func(i int, ctx context.Context) { + defer wg.Done() + <-ctx.Done() + if ctx.Err() != nil { + } + res <- i + }(i, ctx) + } + + go func() { + wg.Wait() + close(res) + }() + + return <-res +} diff --git a/zsync/content_test.go b/zsync/content_test.go new file mode 100644 index 0000000..060b836 --- /dev/null +++ b/zsync/content_test.go @@ -0,0 +1,62 @@ +package zsync + +import ( + "context" + "reflect" + "testing" + "time" + + "github.com/sohaha/zlsgo" +) + +func TestMergeContext(t *testing.T) { + tt := zlsgo.NewTest(t) + + { + ctx1 := context.Background() + ctx2 := context.WithValue(context.Background(), "key", "value2") + ctx3, cancel3 := context.WithCancel(context.Background()) + + ctx := MergeContext(ctx1, ctx2, ctx3) + tt.Equal(reflect.TypeOf(ctx), reflect.TypeOf(&mergeContext{})) + tt.Equal("value2", ctx.Value("key")) + + now := time.Now() + go func() { + time.Sleep(time.Second / 5) + cancel3() + }() + + <-ctx.Done() + + tt.EqualTrue(time.Since(now).Seconds() > 0.2) + } + + { + ctx1 := context.Background() + ctx2, cancel2 := context.WithTimeout(context.Background(), time.Second/5) + defer cancel2() + ctx := MergeContext(ctx1, ctx2) + now := time.Now() + select { + case <-ctx.Done(): + tt.EqualTrue(ctx.Err() != nil) + } + tt.EqualTrue(time.Since(now).Seconds() > 0.2) + } + + { + ctx1 := context.Background() + ctx2, cancel2 := context.WithTimeout(context.Background(), time.Second/5) + ctx3, cancel3 := context.WithTimeout(context.Background(), time.Second/10) + defer cancel2() + defer cancel3() + ctx := MergeContext(ctx1, ctx2, ctx3) + now := time.Now() + select { + case <-ctx.Done(): + tt.EqualTrue(ctx.Err() != nil) + } + tt.EqualTrue(time.Since(now).Seconds() > 0.1) + } +}