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
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
}
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()
}
import (
"bytes"
+ "context"
"encoding/json"
+ "errors"
"fmt"
"io"
"net"
"net/http"
- "strconv"
"strings"
"testing"
"time"
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",
}
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()
+ }
}
}