]> 127.0.0.1 Git - part/.git/commitdiff
1 v0.28.20240302090836
authorqydysky <qydysky@foxmail.com>
Sat, 2 Mar 2024 09:03:45 +0000 (17:03 +0800)
committerqydysky <qydysky@foxmail.com>
Sat, 2 Mar 2024 09:03:45 +0000 (17:03 +0800)
.github/workflows/test.yml
sync/Map.go
web/Web.go
web/Web_test.go

index f040f75b038668cdabc3543006de73e9efd99584..79867eafb36810955511ed271c8427857ac3055b 100644 (file)
@@ -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
index f81ce207ec4beb032a0ba91b6333f1f2d358b2ab..734adcf92fb86d61d32571fd0e0b6f9724973d87 100644 (file)
@@ -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() {
index f0e7a8fc03854fc2b1c0ccb483d01fc8abbce738..5c581eb95be5cade16e7e919041142f2a3f96fd4 100644 (file)
@@ -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()
 }
 
index dd5acc13d6a640ee2a3dc979a51ce86190f4f87e..392f02a451512c159a01b50c3fee4f9cacfeefba 100644 (file)
@@ -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()
+               }
        }
 }