]> 127.0.0.1 Git - part/.git/commitdiff
fix
authorqydysky <32743305+qydysky@users.noreply.github.com>
Sun, 12 Mar 2023 04:13:38 +0000 (12:13 +0800)
committerqydysky <32743305+qydysky@users.noreply.github.com>
Sun, 12 Mar 2023 04:13:38 +0000 (12:13 +0800)
limit/Limit.go
limit/Limit_test.go

index fcc0e230fd680f0ccc1562b6acf2c8c32038d2c4..5e13218845aa24026aa94ec8c65cf02789d32130 100644 (file)
 package part
 
 import (
-       "time"
        "sync/atomic"
+       "time"
+
+       signal "github.com/qydysky/part/signal"
 )
 
 type Limit struct {
-       maxNum_in_period int
-       ms_in_period int
-       ms_to_timeout int
-       bucket chan struct{}
-       pre_bucket_token_num int
-       wait_num int32
+       druation             time.Duration
+       druation_timeout     time.Duration
+       bucket               chan struct{}
+       wait_num             atomic.Int32
+       pre_bucket_token_num atomic.Int64
+       maxNum_in_period     int
+       cancel               *signal.Signal
 }
 
 // create a Limit Object
 // it will allow maxNum_in_period requests(call TO()) in ms_in_period. if the request(call TO()) is out of maxNum_in_period,it will wait ms_to_timeout
-//ms_to_timeout [>0:will wait ms] [=0:no wait] [<0:will block]
-func New(maxNum_in_period,ms_in_period,ms_to_timeout int) (*Limit) {
+// ms_to_timeout [>0:will wait ms] [=0:no wait] [<0:will block]
+func New(maxNum_in_period int, druation, druation_timeout string) *Limit {
        object := Limit{
-               maxNum_in_period:maxNum_in_period,
-               ms_in_period:ms_in_period,
-               ms_to_timeout:ms_to_timeout,
-               bucket:make(chan struct{},maxNum_in_period),
+               bucket: make(chan struct{}, maxNum_in_period),
+               cancel: signal.Init(),
+       }
+
+       if maxNum_in_period <= 0 {
+               panic("maxNum_in_period <= 0")
+       } else {
+               object.maxNum_in_period = maxNum_in_period
+       }
+
+       if t, e := time.ParseDuration(druation); e != nil {
+               panic(e)
+       } else {
+               object.druation = t.Abs()
        }
 
-       go func(object *Limit){
-               for object.maxNum_in_period > 0 {
-                       for i:=0;i<object.maxNum_in_period;i++{
+       if t, e := time.ParseDuration(druation_timeout); e != nil {
+               panic(e)
+       } else {
+               object.druation_timeout = t
+       }
+
+       for i := 0; i < maxNum_in_period; i++ {
+               object.bucket <- struct{}{}
+       }
+
+       go func() {
+               ec, fin := object.cancel.WaitC()
+               defer fin()
+               defer close(object.bucket)
+
+               if object.druation == 0 {
+                       for {
+                               select {
+                               case object.bucket <- struct{}{}:
+                               case <-ec:
+                                       return
+                               }
+                       }
+               } else {
+                       for {
                                select {
-                               case object.bucket <- struct{}{}:;
-                               default :i = object.maxNum_in_period
+                               case <-time.After(object.druation):
+                               case <-ec:
+                                       return
+                               }
+
+                               object.pre_bucket_token_num.Store(int64(len(object.bucket)))
+
+                               for i := object.maxNum_in_period; i > 0; i-- {
+                                       select {
+                                       case object.bucket <- struct{}{}:
+                                       case <-ec:
+                                               return
+                                       default:
+                                               i = 0
+                                       }
                                }
                        }
-                       time.Sleep(time.Duration(object.ms_in_period)*time.Millisecond)
-                       object.pre_bucket_token_num = len(object.bucket)
                }
-               close(object.bucket)
-       }(&object)
+       }()
 
-       //make sure the bucket is full
-       for object.TK() != maxNum_in_period {}
-       object.pre_bucket_token_num = len(object.bucket)
-       
        return &object
 }
 
 // the func will return true if the request(call TO()) is up to limit and return false if not
 func (l *Limit) TO() bool {
-       var AfterMS = func(ReadTimeout int) (c <-chan time.Time) {
-               if ReadTimeout > 0 {
-                       c = time.NewTimer(time.Millisecond*time.Duration(ReadTimeout)).C
-               } else if ReadTimeout == 0 {
-                       tc := make(chan time.Time,1)
-                       tc <- time.Now()
-                       close(tc)
-                       c = tc
-               }
-               return
-       }
-       
-       atomic.AddInt32(&l.wait_num, 1)
-       defer atomic.AddInt32(&l.wait_num, -1)
-       
-       select {
-               case <-l.bucket:;
-               case <-AfterMS(l.ms_to_timeout):return true;
+       if !l.IsLive() {
+               return false
        }
 
+       l.wait_num.Add(1)
+       defer l.wait_num.Add(-1)
 
-       return false
+       if l.druation_timeout < 0 || l.druation == 0 {
+               <-l.bucket
+               return false
+       } else if l.druation_timeout == 0 {
+               select {
+               case <-l.bucket:
+                       return false
+               default:
+                       return true
+               }
+       } else {
+               select {
+               case <-l.bucket:
+                       return false
+               case <-time.NewTimer(l.druation_timeout).C:
+                       return true
+               }
+       }
+}
+
+func (l *Limit) IsLive() bool {
+       return l.cancel.Islive()
 }
 
 func (l *Limit) Close() {
-       l.maxNum_in_period = 0
+       l.cancel.Done()
 }
 
 // return the token number of bucket at now
@@ -83,9 +131,9 @@ func (l *Limit) TK() int {
 
 // return the token number of bucket at previous
 func (l *Limit) PTK() int {
-       return l.pre_bucket_token_num
+       return int(l.pre_bucket_token_num.Load())
 }
 
 func (l *Limit) WNum() int32 {
-       return atomic.LoadInt32(&l.wait_num)
-}
\ No newline at end of file
+       return l.wait_num.Load()
+}
index eff1698de511f5f86819f65123fbf2f1ec1e952a..4133325dceb02afae0d5e33264a2237eec98c262 100644 (file)
@@ -5,61 +5,96 @@ import (
        "time"
 )
 
-func Test_1(t *testing.T){
-       l := New(10,1000,0)
-       pass := 0
-       for i:=0;i<1500;i+=1{
-               go func(){
-                       if !l.TO() {pass += 1}
-               }()
-               time.Sleep(time.Millisecond)
-       }
-       if pass!=20 {t.Error(`pass != 20`)}
+func Test_6(t *testing.T) {
+       l := New(2, "1s", "-1s")
+       t0 := time.Now()
+       if l.TO() || time.Since(t0) > time.Millisecond {
+               t.Fatal()
+       }
+       if l.TO() || time.Since(t0) > time.Millisecond {
+               t.Fatal()
+       }
+       t0 = time.Now()
+       if l.TO() || time.Until(t0.Add(time.Second)) > time.Millisecond {
+               t.Fatal(time.Since(t0))
+       }
 }
 
-func Test_2(t *testing.T){
-       l := New(10,1000,1000)
-       pass := 0
-       for i:=0;i<500;i+=1{
-               go func(){
-                       if !l.TO() {pass += 1}
-               }()
-               time.Sleep(time.Millisecond)
-       }
-       if pass!=10 {t.Error(`pass != 10`,pass)}
+func Test_8(t *testing.T) {
+       l := New(2, "1s", "0s")
+       t0 := time.Now()
+       if l.TO() || time.Since(t0) > time.Millisecond {
+               t.Fatal()
+       }
+       if l.TO() || time.Since(t0) > time.Millisecond {
+               t.Fatal()
+       }
+       if !l.TO() {
+               t.Fatal()
+       }
 }
 
-func Test_3(t *testing.T){
-       l := New(10,0,0)
-       pass := 0
-       for i:=0;i<500;i+=1{
-               go func(){
-                       if !l.TO() {pass += 1}
-               }()
-               time.Sleep(time.Millisecond)
-       }
-       t.Log(pass)
+func Test_9(t *testing.T) {
+       l := New(2, "1s", "5ms")
+       t0 := time.Now()
+       if l.TO() || time.Since(t0) > time.Millisecond {
+               t.Fatal()
+       }
+       time.Sleep(time.Millisecond * 500)
+       if l.TO() || time.Until(t0.Add(time.Millisecond*500)) > time.Millisecond {
+               t.Fatal()
+       }
+       time.Sleep(time.Millisecond * 500)
+       if l.TO() || time.Until(t0.Add(time.Millisecond*505)) > time.Millisecond {
+               t.Fatal()
+       }
 }
 
-func Test_4(t *testing.T){
-       l := New(0,0,10)
-       pass := 0
-       for i:=0;i<500;i+=1{
-               go func(){
-                       if !l.TO() {pass += 1}
-               }()
-               time.Sleep(time.Millisecond)
-       }
-       t.Log(pass)
+func Test_1(t *testing.T) {
+       l := New(2, "0s", "0s")
+       t0 := time.Now()
+       if l.TO() || time.Since(t0) > time.Millisecond {
+               t.Fatal()
+       }
+       if l.TO() || time.Until(t0) > time.Millisecond {
+               t.Fatal()
+       }
+       if l.TO() || time.Until(t0) > time.Millisecond {
+               t.Fatal()
+       }
+}
+
+func Test_2(t *testing.T) {
+       l := New(2, "10s", "-1s")
+       go func() {
+               time.Sleep(time.Second)
+               l.Close()
+       }()
+       t0 := time.Now()
+       if l.TO() || time.Since(t0) > time.Millisecond {
+               t.Fatal()
+       }
+       if l.TO() || time.Until(t0) > time.Millisecond {
+               t.Fatal()
+       }
+       if l.TO() || time.Until(t0.Add(time.Second)) > time.Millisecond {
+               t.Fatal(l.IsLive(), time.Until(t0.Add(time.Second)))
+       }
 }
 
-func Test_5(t *testing.T){
-       l := New(100,3000,0)
-       t.Log(l.TK())
-       for i:=1;i<=50;i+=1{
+func Test_5(t *testing.T) {
+       l := New(100, "3s", "0s")
+       if l.TK() != 100 {
+               t.Error(`5`, l.TK())
+       }
+       for i := 1; i <= 50; i += 1 {
                l.TO()
        }
-       if l.TK() != 50 {t.Error(`5`,l.TK())}
-       time.Sleep(time.Second*3)
-       if l.PTK() != 50 {t.Error(`5`,l.PTK())}
-}
\ No newline at end of file
+       if l.TK() != 50 {
+               t.Error(`5`, l.TK())
+       }
+       time.Sleep(time.Second * 4)
+       if l.PTK() != 50 {
+               t.Error(`5`, l.PTK())
+       }
+}