From 5ed4d45764b975f2b210ed0d46910ef4fd1177ad Mon Sep 17 00:00:00 2001 From: qydysky Date: Sat, 2 Mar 2024 17:03:45 +0800 Subject: [PATCH] 1 --- .github/workflows/test.yml | 4 +- sync/Map.go | 4 +- web/Web.go | 116 +++++++++++++++++++++---------------- web/Web_test.go | 39 ++++++++++--- 4 files changed, 104 insertions(+), 59 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f040f75..79867ea 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -33,7 +33,7 @@ jobs: go test -count 1 -timeout 50s -v -race github.com/qydysky/part/msgq go test -count 10 -race -timeout 10s -run ^Test_3$ github.com/qydysky/part/msgq go test -count 1 -timeout 10s -v -race github.com/qydysky/part/sync - go test -count 1 -timeout 15s -v -race github.com/qydysky/part/web + go test -count 1 -timeout 16s -v -race github.com/qydysky/part/web go test -count 1 -timeout 10s -v -run "Test_Client" -race github.com/qydysky/part/websocket go test -count 1 -timeout 10s -v -race github.com/qydysky/part/sql go test -count 1 -timeout 10s -v -race github.com/qydysky/part/rpc @@ -67,7 +67,7 @@ jobs: go test -count 1 -timeout 50s -v -race github.com/qydysky/part/msgq go test -count 10 -race -timeout 10s -run ^Test_3$ github.com/qydysky/part/msgq go test -count 1 -timeout 10s -v -race github.com/qydysky/part/sync - go test -count 1 -timeout 15s -v -race github.com/qydysky/part/web + go test -count 1 -timeout 16s -v -race github.com/qydysky/part/web go test -count 1 -timeout 10s -v -run "Test_Client" -race github.com/qydysky/part/websocket go test -count 1 -timeout 10s -v -race github.com/qydysky/part/sql go test -count 1 -timeout 10s -v -race github.com/qydysky/part/rpc diff --git a/sync/Map.go b/sync/Map.go index f81ce20..734adcf 100644 --- a/sync/Map.go +++ b/sync/Map.go @@ -38,10 +38,12 @@ func (t *Map) Range(f func(key, value any) bool) { t.m.Range(f) } -func (t *Map) Delete(k any) { +func (t *Map) Delete(k any) (ok bool) { if _, ok := t.m.LoadAndDelete(k); ok { t.size.Add(-1) + return true } + return false } func (t *Map) ClearAll() { diff --git a/web/Web.go b/web/Web.go index f0e7a8f..5c581eb 100644 --- a/web/Web.go +++ b/web/Web.go @@ -428,89 +428,107 @@ func (t withflush) WriteHeader(i int) { } type Exprier struct { - Max int + max int m psync.Map + mc chan string } var ( - ErrExprie = errors.New("ErrExprie") - ErrNoFound = errors.New("ErrNoFound") - ErrOverflow = errors.New("ErrOverflow") + ErrExpried = errors.New("ErrExpried") + ErrNoFound = errors.New("ErrNoFound") ) -func (t *Exprier) Reg(key string, dur time.Duration) (string, error) { - if t.Max <= 0 { +func NewExprier(max int) *Exprier { + return &Exprier{ + max: max, + mc: make(chan string, max), + } +} + +func (t *Exprier) SetMax(max int) { + t.max = max + t.mc = make(chan string, max) +} + +func (t *Exprier) Reg(dur time.Duration, reNewKey ...string) (string, error) { + if t.max <= 0 { return "noExprie", nil } - if key != "" { - if _, ok := t.m.Load(key); ok { - t.m.Store(key, time.Now().Add(dur)) - return key, nil + if len(reNewKey) != 0 && reNewKey[0] != "" { + if _, ok := t.m.Load(reNewKey[0]); ok { + t.m.Store(reNewKey[0], time.Now().Add(dur)) + return reNewKey[0], nil } else { - return key, ErrNoFound + return reNewKey[0], ErrNoFound } } else { - return uuid.NewString(), nil + newkey := uuid.NewString() + select { + case t.mc <- newkey: + t.m.Store(newkey, time.Now().Add(dur)) + return newkey, nil + default: + for { + select { + case key1 := <-t.mc: + if t.m.Delete(key1) { + t.mc <- newkey + t.m.Store(newkey, time.Now().Add(dur)) + return newkey, nil + } + default: + t.mc <- newkey + return newkey, nil + } + } + } } } -func (t *Exprier) Check(key string) error { - if t.Max <= 0 { - return nil +func (t *Exprier) Check(key string) (time.Time, error) { + if t.max <= 0 { + return time.Now(), nil } if key == "" { - return ErrNoFound + return time.Now(), ErrNoFound } ey, ok := t.m.LoadV(key).(time.Time) if !ok { - return ErrNoFound + return time.Now(), ErrNoFound } else if time.Now().After(ey) { t.m.Delete(key) - return ErrExprie + return time.Now(), ErrExpried } - return nil + return ey, nil } -func (t *Exprier) LoopCheck(key string, dru time.Duration, whenfail func(key string, e error)) (breakLoop func(), e error) { - breakLoop = func() {} - if t.Max <= 0 { - return - } - if key == "" { - e = ErrNoFound - return - } - if t.m.Len() >= t.Max { - e = ErrOverflow - return +func (t *Exprier) LoopCheck(ctx context.Context, key string, whenfail func(key string, e error)) (e error) { + if t.max <= 0 { + return nil } - - var close atomic.Bool - t.m.Store(key, time.Now().Add(dru)) - breakLoop = func() { - close.Store(true) - t.m.Delete(key) + if _, e := t.Check(key); e != nil { + whenfail(key, e) + return e } - go func() { - for !close.Load() && t.Max > 0 { - ey, ok := t.m.LoadV(key).(time.Time) - if !ok { - whenfail(key, ErrNoFound) - return - } else if time.Now().After(ey) { - t.m.Delete(key) - whenfail(key, ErrExprie) + for { + if ey, e := t.Check(key); e != nil { + whenfail(key, e) return + } else { + select { + case <-ctx.Done(): + return + case <-time.After(time.Until(ey) + time.Second): + } } - time.Sleep(time.Until(ey) + time.Second) } }() - return + return nil } func (t *Exprier) Disable() { - t.Max = 0 + t.max = 0 t.m.ClearAll() } diff --git a/web/Web_test.go b/web/Web_test.go index dd5acc1..392f02a 100644 --- a/web/Web_test.go +++ b/web/Web_test.go @@ -2,12 +2,13 @@ package part import ( "bytes" + "context" "encoding/json" + "errors" "fmt" "io" "net" "net/http" - "strconv" "strings" "testing" "time" @@ -16,6 +17,29 @@ import ( reqf "github.com/qydysky/part/reqf" ) +func Test_Exprier(t *testing.T) { + exp := NewExprier(1) + if key, e := exp.Reg(time.Second); e != nil { + t.Fail() + } else { + if _, e := exp.Check(key); e != nil { + t.Fail() + } + if k, e := exp.Reg(time.Second); e != nil { + t.Fail() + } else { + if _, e := exp.Check(key); !errors.Is(e, ErrNoFound) { + t.Fail() + } + key = k + } + time.Sleep(time.Second * 2) + if _, e := exp.Check(key); !errors.Is(e, ErrExpried) { + t.Fail() + } + } +} + func Test_Server(t *testing.T) { s := New(&http.Server{ Addr: "127.0.0.1:13000", @@ -310,25 +334,26 @@ func (t ResStruct) Write(w http.ResponseWriter) { } func Test1(b *testing.T) { - exp := Exprier{Max: 10} + exp := NewExprier(20) el := make(chan error, 100) for i := 0; i < 20; i++ { - done, e := exp.LoopCheck(strconv.Itoa(i), time.Second*10, func(key string, e error) { + key, _ := exp.Reg(time.Second) + e := exp.LoopCheck(context.Background(), key, func(key string, e error) { if e != nil { el <- e - b.Log(key, e) } }) if e != nil { b.Fatal(e) } - done() } time.Sleep(time.Second * 3) - if len(el) > 0 { - b.Fatal(<-el) + for len(el) > 0 { + if !errors.Is(<-el, ErrExpried) { + b.Fatal() + } } } -- 2.39.2