| @@ -2,6 +2,7 @@ package future | |||||
| import ( | import ( | ||||
| "context" | "context" | ||||
| "sync" | |||||
| ) | ) | ||||
| type SetValueFuture[T any] struct { | type SetValueFuture[T any] struct { | ||||
| @@ -9,6 +10,7 @@ type SetValueFuture[T any] struct { | |||||
| err error | err error | ||||
| isCompleted bool | isCompleted bool | ||||
| completeChan chan any | completeChan chan any | ||||
| completeOnce sync.Once | |||||
| } | } | ||||
| func NewSetValue[T any]() *SetValueFuture[T] { | func NewSetValue[T any]() *SetValueFuture[T] { | ||||
| @@ -18,22 +20,28 @@ func NewSetValue[T any]() *SetValueFuture[T] { | |||||
| } | } | ||||
| func (f *SetValueFuture[T]) SetComplete(val T, err error) { | func (f *SetValueFuture[T]) SetComplete(val T, err error) { | ||||
| f.value = val | |||||
| f.err = err | |||||
| f.isCompleted = true | |||||
| close(f.completeChan) | |||||
| f.completeOnce.Do(func() { | |||||
| f.value = val | |||||
| f.err = err | |||||
| f.isCompleted = true | |||||
| close(f.completeChan) | |||||
| }) | |||||
| } | } | ||||
| func (f *SetValueFuture[T]) SetValue(val T) { | func (f *SetValueFuture[T]) SetValue(val T) { | ||||
| f.value = val | |||||
| f.isCompleted = true | |||||
| close(f.completeChan) | |||||
| f.completeOnce.Do(func() { | |||||
| f.value = val | |||||
| f.isCompleted = true | |||||
| close(f.completeChan) | |||||
| }) | |||||
| } | } | ||||
| func (f *SetValueFuture[T]) SetError(err error) { | func (f *SetValueFuture[T]) SetError(err error) { | ||||
| f.err = err | |||||
| f.isCompleted = true | |||||
| close(f.completeChan) | |||||
| f.completeOnce.Do(func() { | |||||
| f.err = err | |||||
| f.isCompleted = true | |||||
| close(f.completeChan) | |||||
| }) | |||||
| } | } | ||||
| func (f *SetValueFuture[T]) Error() error { | func (f *SetValueFuture[T]) Error() error { | ||||
| @@ -77,6 +85,7 @@ type SetValueFuture2[T1 any, T2 any] struct { | |||||
| err error | err error | ||||
| isCompleted bool | isCompleted bool | ||||
| completeChan chan any | completeChan chan any | ||||
| completeOnce sync.Once | |||||
| } | } | ||||
| func NewSetValue2[T1 any, T2 any]() *SetValueFuture2[T1, T2] { | func NewSetValue2[T1 any, T2 any]() *SetValueFuture2[T1, T2] { | ||||
| @@ -86,24 +95,30 @@ func NewSetValue2[T1 any, T2 any]() *SetValueFuture2[T1, T2] { | |||||
| } | } | ||||
| func (f *SetValueFuture2[T1, T2]) SetComplete(val1 T1, val2 T2, err error) { | func (f *SetValueFuture2[T1, T2]) SetComplete(val1 T1, val2 T2, err error) { | ||||
| f.value1 = val1 | |||||
| f.value2 = val2 | |||||
| f.err = err | |||||
| f.isCompleted = true | |||||
| close(f.completeChan) | |||||
| f.completeOnce.Do(func() { | |||||
| f.value1 = val1 | |||||
| f.value2 = val2 | |||||
| f.err = err | |||||
| f.isCompleted = true | |||||
| close(f.completeChan) | |||||
| }) | |||||
| } | } | ||||
| func (f *SetValueFuture2[T1, T2]) SetValue(val1 T1, val2 T2) { | func (f *SetValueFuture2[T1, T2]) SetValue(val1 T1, val2 T2) { | ||||
| f.value1 = val1 | |||||
| f.value2 = val2 | |||||
| f.isCompleted = true | |||||
| close(f.completeChan) | |||||
| f.completeOnce.Do(func() { | |||||
| f.value1 = val1 | |||||
| f.value2 = val2 | |||||
| f.isCompleted = true | |||||
| close(f.completeChan) | |||||
| }) | |||||
| } | } | ||||
| func (f *SetValueFuture2[T1, T2]) SetError(err error) { | func (f *SetValueFuture2[T1, T2]) SetError(err error) { | ||||
| f.err = err | |||||
| f.isCompleted = true | |||||
| close(f.completeChan) | |||||
| f.completeOnce.Do(func() { | |||||
| f.err = err | |||||
| f.isCompleted = true | |||||
| close(f.completeChan) | |||||
| }) | |||||
| } | } | ||||
| func (f *SetValueFuture2[T1, T2]) Error() error { | func (f *SetValueFuture2[T1, T2]) Error() error { | ||||
| @@ -2,12 +2,14 @@ package future | |||||
| import ( | import ( | ||||
| "context" | "context" | ||||
| "sync" | |||||
| ) | ) | ||||
| type SetVoidFuture struct { | type SetVoidFuture struct { | ||||
| err error | err error | ||||
| isCompleted bool | isCompleted bool | ||||
| completeChan chan any | completeChan chan any | ||||
| completeOnce sync.Once | |||||
| } | } | ||||
| func NewSetVoid() *SetVoidFuture { | func NewSetVoid() *SetVoidFuture { | ||||
| @@ -17,14 +19,18 @@ func NewSetVoid() *SetVoidFuture { | |||||
| } | } | ||||
| func (f *SetVoidFuture) SetVoid() { | func (f *SetVoidFuture) SetVoid() { | ||||
| f.isCompleted = true | |||||
| close(f.completeChan) | |||||
| f.completeOnce.Do(func() { | |||||
| f.isCompleted = true | |||||
| close(f.completeChan) | |||||
| }) | |||||
| } | } | ||||
| func (f *SetVoidFuture) SetError(err error) { | func (f *SetVoidFuture) SetError(err error) { | ||||
| f.err = err | |||||
| f.isCompleted = true | |||||
| close(f.completeChan) | |||||
| f.completeOnce.Do(func() { | |||||
| f.err = err | |||||
| f.isCompleted = true | |||||
| close(f.completeChan) | |||||
| }) | |||||
| } | } | ||||
| func (f *SetVoidFuture) Error() error { | func (f *SetVoidFuture) Error() error { | ||||
| @@ -128,6 +128,10 @@ type Node struct { | |||||
| LastReportTime *time.Time `db:"LastReportTime" json:"lastReportTime"` | LastReportTime *time.Time `db:"LastReportTime" json:"lastReportTime"` | ||||
| } | } | ||||
| func (n Node) String() string { | |||||
| return fmt.Sprintf("%v(%v)", n.Name, n.NodeID) | |||||
| } | |||||
| type PinnedObject struct { | type PinnedObject struct { | ||||
| ObjectID ObjectID `db:"ObjectID" json:"objectID"` | ObjectID ObjectID `db:"ObjectID" json:"objectID"` | ||||
| NodeID NodeID `db:"NodeID" json:"nodeID"` | NodeID NodeID `db:"NodeID" json:"nodeID"` | ||||
| @@ -24,6 +24,7 @@ const ( | |||||
| ContentTypeForm = "application/x-www-form-urlencoded" | ContentTypeForm = "application/x-www-form-urlencoded" | ||||
| ContentTypeMultiPart = "multipart/form-data" | ContentTypeMultiPart = "multipart/form-data" | ||||
| ContentTypeOctetStream = "application/octet-stream" | ContentTypeOctetStream = "application/octet-stream" | ||||
| ContentTypeEventStream = "text/event-stream" | |||||
| ) | ) | ||||
| var defaultClient = http.DefaultClient | var defaultClient = http.DefaultClient | ||||
| @@ -19,6 +19,22 @@ func RemoveAt[T any](arr []T, index int) []T { | |||||
| return append(arr[:index], arr[index+1:]...) | return append(arr[:index], arr[index+1:]...) | ||||
| } | } | ||||
| func RemoveAllDefault[T comparable](arr []T) []T { | |||||
| var def T | |||||
| return lo.Filter(arr, func(i T, idx int) bool { | |||||
| return i != def | |||||
| }) | |||||
| } | |||||
| func Clear[T comparable](arr []T, item T) { | |||||
| var def T | |||||
| for i := 0; i < len(arr); i++ { | |||||
| if arr[i] == item { | |||||
| arr[i] = def | |||||
| } | |||||
| } | |||||
| } | |||||
| func ArrayClone[T any](arr []T) []T { | func ArrayClone[T any](arr []T) []T { | ||||
| return append([]T{}, arr...) | return append([]T{}, arr...) | ||||
| } | } | ||||
| @@ -29,3 +45,12 @@ func Insert[T any](arr []T, index int, item T) []T { | |||||
| arr[index] = item | arr[index] = item | ||||
| return arr | return arr | ||||
| } | } | ||||
| func Deref[T any](arr []*T) []T { | |||||
| result := make([]T, len(arr)) | |||||
| for i := 0; i < len(arr); i++ { | |||||
| result[i] = *arr[i] | |||||
| } | |||||
| return result | |||||
| } | |||||
| @@ -32,7 +32,9 @@ func (c *CounterCond) Wait() bool { | |||||
| } | } | ||||
| func (c *CounterCond) Release() { | func (c *CounterCond) Release() { | ||||
| c.cond.L.Lock() | |||||
| c.count++ | c.count++ | ||||
| c.cond.L.Unlock() | |||||
| c.cond.Signal() | c.cond.Signal() | ||||
| } | } | ||||
| @@ -0,0 +1,48 @@ | |||||
| package sync2 | |||||
| import ( | |||||
| "context" | |||||
| "errors" | |||||
| "sync" | |||||
| ) | |||||
| var ErrEventClosed = errors.New("event is closed") | |||||
| var ErrContextCanceled = errors.New("context canceled") | |||||
| type Event struct { | |||||
| ch chan any | |||||
| closeOnce sync.Once | |||||
| } | |||||
| func NewEvent() Event { | |||||
| return Event{ | |||||
| ch: make(chan any, 1), | |||||
| } | |||||
| } | |||||
| func (e *Event) Set() { | |||||
| select { | |||||
| case e.ch <- nil: | |||||
| default: | |||||
| } | |||||
| } | |||||
| func (e *Event) Wait(ctx context.Context) error { | |||||
| select { | |||||
| case _, ok := <-e.ch: | |||||
| if ok { | |||||
| return nil | |||||
| } | |||||
| return ErrEventClosed | |||||
| case <-ctx.Done(): | |||||
| return ErrContextCanceled | |||||
| } | |||||
| } | |||||
| func (e *Event) Close() { | |||||
| e.closeOnce.Do(func() { | |||||
| close(e.ch) | |||||
| }) | |||||
| } | |||||
| @@ -1,9 +1,13 @@ | |||||
| package sync2 | package sync2 | ||||
| import "sync" | |||||
| import ( | |||||
| "sync" | |||||
| ) | |||||
| func ParallelDo[T any](args []T, fn func(val T, index int) error) error { | func ParallelDo[T any](args []T, fn func(val T, index int) error) error { | ||||
| lock := sync.Mutex{} | |||||
| var err error | var err error | ||||
| var wg sync.WaitGroup | var wg sync.WaitGroup | ||||
| wg.Add(len(args)) | wg.Add(len(args)) | ||||
| for i, arg := range args { | for i, arg := range args { | ||||
| @@ -11,10 +15,15 @@ func ParallelDo[T any](args []T, fn func(val T, index int) error) error { | |||||
| defer wg.Done() | defer wg.Done() | ||||
| if e := fn(arg, index); e != nil { | if e := fn(arg, index); e != nil { | ||||
| err = e | |||||
| lock.Lock() | |||||
| if err == nil { | |||||
| err = e | |||||
| } | |||||
| lock.Unlock() | |||||
| } | } | ||||
| }(arg, i) | }(arg, i) | ||||
| } | } | ||||
| wg.Wait() | wg.Wait() | ||||
| return err | return err | ||||
| } | } | ||||
| @@ -0,0 +1,74 @@ | |||||
| package sync2 | |||||
| import ( | |||||
| "container/list" | |||||
| "sync" | |||||
| ) | |||||
| type UnboundChannel[T any] struct { | |||||
| values *list.List | |||||
| cond *sync.Cond | |||||
| err error | |||||
| } | |||||
| func NewUnboundChannel[T any]() *UnboundChannel[T] { | |||||
| return &UnboundChannel[T]{ | |||||
| values: list.New(), | |||||
| cond: sync.NewCond(&sync.Mutex{}), | |||||
| } | |||||
| } | |||||
| func (c *UnboundChannel[T]) Error() error { | |||||
| return c.err | |||||
| } | |||||
| func (c *UnboundChannel[T]) Send(val T) error { | |||||
| c.cond.L.Lock() | |||||
| if c.err != nil { | |||||
| c.cond.L.Unlock() | |||||
| return c.err | |||||
| } | |||||
| c.values.PushBack(val) | |||||
| c.cond.L.Unlock() | |||||
| c.cond.Signal() | |||||
| return nil | |||||
| } | |||||
| func (c *UnboundChannel[T]) Receive() (T, error) { | |||||
| c.cond.L.Lock() | |||||
| defer c.cond.L.Unlock() | |||||
| if c.values.Len() == 0 { | |||||
| c.cond.Wait() | |||||
| } | |||||
| if c.values.Len() == 0 { | |||||
| var ret T | |||||
| return ret, c.err | |||||
| } | |||||
| ret := c.values.Front().Value.(T) | |||||
| c.values.Remove(c.values.Front()) | |||||
| return ret, nil | |||||
| } | |||||
| func (c *UnboundChannel[T]) Close() { | |||||
| c.cond.L.Lock() | |||||
| if c.err != nil { | |||||
| return | |||||
| } | |||||
| c.err = ErrChannelClosed | |||||
| c.cond.L.Unlock() | |||||
| c.cond.Broadcast() | |||||
| } | |||||
| func (c *UnboundChannel[T]) CloseWithError(err error) { | |||||
| c.cond.L.Lock() | |||||
| if c.err != nil { | |||||
| return | |||||
| } | |||||
| c.err = err | |||||
| c.cond.L.Unlock() | |||||
| c.cond.Broadcast() | |||||
| } | |||||
| @@ -0,0 +1,28 @@ | |||||
| package time2 | |||||
| import ( | |||||
| "fmt" | |||||
| "time" | |||||
| ) | |||||
| type Duration struct { | |||||
| time.Duration | |||||
| } | |||||
| func (d *Duration) Std() time.Duration { | |||||
| return d.Duration | |||||
| } | |||||
| func (d *Duration) Scan(state fmt.ScanState, verb rune) error { | |||||
| data, err := state.Token(true, nil) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| d.Duration, err = time.ParseDuration(string(data)) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| return nil | |||||
| } | |||||
| @@ -0,0 +1,25 @@ | |||||
| package time2 | |||||
| import ( | |||||
| "fmt" | |||||
| "testing" | |||||
| "time" | |||||
| . "github.com/smartystreets/goconvey/convey" | |||||
| ) | |||||
| func Test_Duration(t *testing.T) { | |||||
| Convey("从字符串解析", t, func() { | |||||
| dur := Duration{} | |||||
| _, err := fmt.Sscanf("10s", "%v", &dur) | |||||
| So(err, ShouldEqual, nil) | |||||
| So(dur.Std(), ShouldEqual, 10*time.Second) | |||||
| }) | |||||
| Convey("包含空白字符", t, func() { | |||||
| dur := Duration{} | |||||
| _, err := fmt.Sscanf(" 10s\t\n\r", "%v", &dur) | |||||
| So(err, ShouldEqual, nil) | |||||
| So(dur.Std(), ShouldEqual, 10*time.Second) | |||||
| }) | |||||
| } | |||||