diff --git a/g/container/garray/garray_func.go b/g/container/garray/garray_func.go index 429a98d11..7366269d5 100644 --- a/g/container/garray/garray_func.go +++ b/g/container/garray/garray_func.go @@ -6,7 +6,6 @@ package garray - type apiSliceInterface interface { Slice() []interface{} } diff --git a/g/container/gqueue/gqueue_unit_test.go b/g/container/gqueue/gqueue_unit_test.go index 5a06b1c98..322c63985 100644 --- a/g/container/gqueue/gqueue_unit_test.go +++ b/g/container/gqueue/gqueue_unit_test.go @@ -35,7 +35,6 @@ func TestQueue_Basic(t *testing.T) { gtest.Assert(q.Pop(), 1) } - func TestQueue_Pop(t *testing.T) { q1 := gqueue.New() q1.Push(1) @@ -44,7 +43,6 @@ func TestQueue_Pop(t *testing.T) { q1.Push(4) i1 := q1.Pop() gtest.Assert(i1, 1) - } func TestQueue_Close(t *testing.T) { @@ -53,4 +51,4 @@ func TestQueue_Close(t *testing.T) { q1.Push(2) gtest.Assert(q1.Len(), 2) q1.Close() -} \ No newline at end of file +} diff --git a/g/crypto/gaes/gaes.go b/g/crypto/gaes/gaes.go index 14f34f44b..40d381f9f 100644 --- a/g/crypto/gaes/gaes.go +++ b/g/crypto/gaes/gaes.go @@ -98,3 +98,66 @@ func PKCS5UnPadding(src []byte, blockSize int) ([]byte, error) { return src[:(length - unpadding)], nil } + +/** + * AES加密, 使用CFB模式. + * 注意key必须为16/24/32位长度,padding返回补位长度,iv初始化向量为非必需参数 + * author: zseeker + * date: 2019-06-18 + */ +func EncryptCFB(plainText []byte, key []byte, padding *int, iv ...[]byte) ([]byte, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + blockSize := block.BlockSize() + plainText, *padding = ZeroPadding(plainText, blockSize) //补位0 + ivValue := ([]byte)(nil) + if len(iv) > 0 { + ivValue = iv[0] + } else { + ivValue = []byte(ivDefValue) + } + stream := cipher.NewCFBEncrypter(block, ivValue) + cipherText := make([]byte, len(plainText)) + stream.XORKeyStream(cipherText, plainText) + return cipherText, nil +} + +/** + * AES解密, 使用CFB模式. + * 注意key必须为16/24/32位长度,unpadding为去补位长度,iv初始化向量为非必需参数 + * author: zseeker + * date: 2019-06-18 + */ +func DecryptCFB(cipherText []byte, key []byte, unpadding int, iv ...[]byte) ([]byte, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + if len(cipherText) < aes.BlockSize { + return nil, errors.New("cipherText too short") + } + ivValue := ([]byte)(nil) + if len(iv) > 0 { + ivValue = iv[0] + } else { + ivValue = []byte(ivDefValue) + } + stream := cipher.NewCFBDecrypter(block, ivValue) + plainText := make([]byte, len(cipherText)) + stream.XORKeyStream(plainText, cipherText) + plainText = ZeroUnPadding(plainText, unpadding) //去补位0 + return plainText, nil +} + +func ZeroPadding(ciphertext []byte, blockSize int) ([]byte, int) { + padding := blockSize - len(ciphertext)%blockSize + padtext := bytes.Repeat([]byte{byte(0)}, padding) + return append(ciphertext, padtext...), padding +} + +func ZeroUnPadding(plaintext []byte, unpadding int) []byte { + length := len(plaintext) + return plaintext[:(length - unpadding)] +} \ No newline at end of file diff --git a/g/crypto/gaes/gaes_test.go b/g/crypto/gaes/gaes_test.go index 57f307a6e..119900065 100644 --- a/g/crypto/gaes/gaes_test.go +++ b/g/crypto/gaes/gaes_test.go @@ -32,6 +32,10 @@ var ( keys = []byte("12345678912345678912345678912346") key_err = []byte("1234") key_32_err = []byte("1234567891234567891234567891234 ") + + // cfb模式blockSize补位长度, add by zseeker + padding_size = 16 - len(content) + content_16_cfb, _ = gbase64.Decode("oSmget3aBDT1nJnBp8u6kA==") ) func TestEncrypt(t *testing.T) { @@ -125,3 +129,21 @@ func TestPKCS5UnPaddingErr(t *testing.T) { gtest.AssertNE(err, nil) }) } + +func TestEncryptCFB(t *testing.T) { + gtest.Case(t, func() { + var padding int = 0 + data, err := gaes.EncryptCFB(content, key_16, &padding, iv) + gtest.Assert(err, nil) + gtest.Assert(padding, padding_size) + gtest.Assert(data, []byte(content_16_cfb)) + }) +} + +func TestDecryptCFB(t *testing.T) { + gtest.Case(t, func() { + decrypt, err := gaes.DecryptCFB([]byte(content_16_cfb), key_16, padding_size, iv) + gtest.Assert(err, nil) + gtest.Assert(decrypt, content) + }) +} diff --git a/g/internal/mutex/mutex.go b/g/internal/mutex/mutex.go index f16fed5b2..783d95dea 100644 --- a/g/internal/mutex/mutex.go +++ b/g/internal/mutex/mutex.go @@ -4,11 +4,12 @@ // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. -// Package mutex provides switch for sync.Mutex for concurrent safe feature. +// Package mutex provides switch of concurrent safe feature for sync.Mutex. package mutex import "sync" +// Mutex is a sync.Mutex with a switch of concurrent safe feature. type Mutex struct { sync.Mutex safe bool diff --git a/g/internal/rwmutex/rwmutex.go b/g/internal/rwmutex/rwmutex.go index 903275d30..9f071d671 100644 --- a/g/internal/rwmutex/rwmutex.go +++ b/g/internal/rwmutex/rwmutex.go @@ -4,11 +4,12 @@ // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. -// Package rwmutex provides switch for sync.RWMutex for concurrent safe feature. +// Package rwmutex provides switch of concurrent safe feature for sync.RWMutex. package rwmutex import "sync" +// RWMutex is a sync.RWMutex with a switch of concurrent safe feature. type RWMutex struct { sync.RWMutex safe bool diff --git a/g/os/gfile/gfile_z_test.go b/g/os/gfile/gfile_z_test.go index c0315f9f5..b7d438434 100644 --- a/g/os/gfile/gfile_z_test.go +++ b/g/os/gfile/gfile_z_test.go @@ -337,9 +337,7 @@ func TestCopy(t *testing.T) { defer delTestFiles(topath) gtest.Assert(gfile.IsFile(testpath()+topath), true) - gtest.AssertNE(gfile.Copy("", ""), nil) - }) } @@ -365,7 +363,7 @@ func TestDirNames(t *testing.T) { readlist, err = gfile.DirNames(testpath() + paths) gtest.Assert(err, nil) - gtest.Assert(havelist, readlist) + gtest.AssertIN(readlist, havelist) _, err = gfile.DirNames("") gtest.AssertNE(err, nil) @@ -653,8 +651,8 @@ func TestMkdir(t *testing.T) { func TestStat(t *testing.T) { gtest.Case(t, func() { var ( - tpath1 = "/testfile_t1.txt" - tpath2 = "./testfile_t1_no.txt" + tpath1 = "/testfile_t1.txt" + tpath2 = "./testfile_t1_no.txt" err error fileiofo os.FileInfo ) diff --git a/g/os/gmlock/gmlock.go b/g/os/gmlock/gmlock.go index 18bc88fa0..a7897c5be 100644 --- a/g/os/gmlock/gmlock.go +++ b/g/os/gmlock/gmlock.go @@ -14,46 +14,46 @@ var ( locker = New() ) -// TryLock tries locking the with write lock, -// it returns true if success, or if there's a write/read lock the , +// TryLock tries locking the with writing lock, +// it returns true if success, or if there's a write/reading lock the , // it returns false. The parameter specifies the max duration it locks. func TryLock(key string, expire...time.Duration) bool { return locker.TryLock(key, expire...) } -// Lock locks the with write lock. -// If there's a write/read lock the , +// Lock locks the with writing lock. +// If there's a write/reading lock the , // it will blocks until the lock is released. // The parameter specifies the max duration it locks. func Lock(key string, expire...time.Duration) { locker.Lock(key, expire...) } -// Unlock unlocks the write lock of the . +// Unlock unlocks the writing lock of the . func Unlock(key string) { locker.Unlock(key) } -// TryRLock tries locking the with read lock. -// It returns true if success, or if there's a write lock on , it returns false. +// TryRLock tries locking the with reading lock. +// It returns true if success, or if there's a writing lock on , it returns false. func TryRLock(key string) bool { return locker.TryRLock(key) } -// RLock locks the with read lock. -// If there's a write lock on , -// it will blocks until the write lock is released. +// RLock locks the with reading lock. +// If there's a writing lock on , +// it will blocks until the writing lock is released. func RLock(key string) { locker.RLock(key) } -// RUnlock unlocks the read lock of the . +// RUnlock unlocks the reading lock of the . func RUnlock(key string) { locker.RUnlock(key) } -// TryLockFunc locks the with write lock and callback function . -// It returns true if success, or else if there's a write/read lock the , it return false. +// TryLockFunc locks the with writing lock and callback function . +// It returns true if success, or else if there's a write/reading lock the , it return false. // // It releases the lock after is executed. // @@ -62,8 +62,8 @@ func TryLockFunc(key string, f func(), expire...time.Duration) bool { return locker.TryLockFunc(key, f, expire...) } -// TryRLockFunc locks the with read lock and callback function . -// It returns true if success, or else if there's a write lock the , it returns false. +// TryRLockFunc locks the with reading lock and callback function . +// It returns true if success, or else if there's a writing lock the , it returns false. // // It releases the lock after is executed. // @@ -72,8 +72,8 @@ func TryRLockFunc(key string, f func()) bool { return locker.TryRLockFunc(key, f) } -// LockFunc locks the with write lock and callback function . -// If there's a write/read lock the , +// LockFunc locks the with writing lock and callback function . +// If there's a write/reading lock the , // it will blocks until the lock is released. // // It releases the lock after is executed. @@ -83,8 +83,8 @@ func LockFunc(key string, f func(), expire...time.Duration) { locker.LockFunc(key, f, expire...) } -// RLockFunc locks the with read lock and callback function . -// If there's a write lock the , +// RLockFunc locks the with reading lock and callback function . +// If there's a writing lock the , // it will blocks until the lock is released. // // It releases the lock after is executed. diff --git a/g/os/gmlock/gmlock_locker.go b/g/os/gmlock/gmlock_locker.go index 6eeafbff7..f09380936 100644 --- a/g/os/gmlock/gmlock_locker.go +++ b/g/os/gmlock/gmlock_locker.go @@ -25,50 +25,50 @@ func New() *Locker { } } -// TryLock tries locking the with write lock, -// it returns true if success, or if there's a write/read lock the , -// it returns false. The parameter specifies the max duration it locks. +// TryLock tries locking the with writing lock, +// it returns true if success, or it returns false if there's a writing/reading lock the . +// The parameter specifies the max duration it locks. func (l *Locker) TryLock(key string, expire...time.Duration) bool { return l.doLock(key, l.getExpire(expire...), true) } -// Lock locks the with write lock. -// If there's a write/read lock the , +// Lock locks the with writing lock. +// If there's a write/reading lock the , // it will blocks until the lock is released. // The parameter specifies the max duration it locks. func (l *Locker) Lock(key string, expire...time.Duration) { l.doLock(key, l.getExpire(expire...), false) } -// Unlock unlocks the write lock of the . +// Unlock unlocks the writing lock of the . func (l *Locker) Unlock(key string) { if v := l.m.Get(key); v != nil { v.(*Mutex).Unlock() } } -// TryRLock tries locking the with read lock. -// It returns true if success, or if there's a write lock on , it returns false. +// TryRLock tries locking the with reading lock. +// It returns true if success, or if there's a writing lock on , it returns false. func (l *Locker) TryRLock(key string) bool { return l.doRLock(key, true) } -// RLock locks the with read lock. -// If there's a write lock on , -// it will blocks until the write lock is released. +// RLock locks the with reading lock. +// If there's a writing lock on , +// it will blocks until the writing lock is released. func (l *Locker) RLock(key string) { l.doRLock(key, false) } -// RUnlock unlocks the read lock of the . +// RUnlock unlocks the reading lock of the . func (l *Locker) RUnlock(key string) { if v := l.m.Get(key); v != nil { v.(*Mutex).RUnlock() } } -// TryLockFunc locks the with write lock and callback function . -// It returns true if success, or else if there's a write/read lock the , it return false. +// TryLockFunc locks the with writing lock and callback function . +// It returns true if success, or else if there's a write/reading lock the , it return false. // // It releases the lock after is executed. // @@ -82,8 +82,8 @@ func (l *Locker) TryLockFunc(key string, f func(), expire...time.Duration) bool return false } -// TryRLockFunc locks the with read lock and callback function . -// It returns true if success, or else if there's a write lock the , it returns false. +// TryRLockFunc locks the with reading lock and callback function . +// It returns true if success, or else if there's a writing lock the , it returns false. // // It releases the lock after is executed. // @@ -97,8 +97,8 @@ func (l *Locker) TryRLockFunc(key string, f func()) bool { return false } -// LockFunc locks the with write lock and callback function . -// If there's a write/read lock the , +// LockFunc locks the with writing lock and callback function . +// If there's a write/reading lock the , // it will blocks until the lock is released. // // It releases the lock after is executed. @@ -110,8 +110,8 @@ func (l *Locker) LockFunc(key string, f func(), expire...time.Duration) { f() } -// RLockFunc locks the with read lock and callback function . -// If there's a write lock the , +// RLockFunc locks the with reading lock and callback function . +// If there's a writing lock the , // it will blocks until the lock is released. // // It releases the lock after is executed. @@ -137,8 +137,8 @@ func (l *Locker) getExpire(expire...time.Duration) time.Duration { // It returns true if success, or else returns false. // // The parameter is true, -// it returns false immediately if it fails getting the write lock. -// If is false, it blocks until it gets the write lock. +// it returns false immediately if it fails getting the writing lock. +// If is false, it blocks until it gets the writing lock. // // The parameter specifies the max duration it locks. func (l *Locker) doLock(key string, expire time.Duration, try bool) bool { @@ -164,8 +164,8 @@ func (l *Locker) doLock(key string, expire time.Duration, try bool) bool { // It returns true if success, or else returns false. // // The parameter is true, -// it returns false immediately if it fails getting the read lock. -// If is false, it blocks until it gets the read lock. +// it returns false immediately if it fails getting the reading lock. +// If is false, it blocks until it gets the reading lock. func (l *Locker) doRLock(key string, try bool) bool { mu := l.getOrNewMutex(key) ok := true diff --git a/g/os/gmlock/gmlock_mutex.go b/g/os/gmlock/gmlock_mutex.go index 6fcd261f7..6c345508f 100644 --- a/g/os/gmlock/gmlock_mutex.go +++ b/g/os/gmlock/gmlock_mutex.go @@ -8,98 +8,129 @@ package gmlock import ( "github.com/gogf/gf/g/container/gtype" + "runtime" "sync" ) -// The high level Mutex. +// The high level RWMutex. // It wraps the sync.RWMutex to implements more rich features. type Mutex struct { - mu sync.RWMutex - wid *gtype.Int64 // Unique id, used for multiple safely Unlock. - number *gtype.Int // Locking number. - // 0: writing lock false; - // 1: writing lock true; - // >=2: reading lock; + mu sync.RWMutex + wid *gtype.Int64 // Unique id, used for multiple and safe logic Unlock. + locking *gtype.Bool // Locking mark for atomic operation for *Lock and Try*Lock functions. + // There must be only one locking operation at the same time for concurrent safe purpose. + state *gtype.Int32 // Locking state: + // 0: writing lock false; + // -1: writing lock true; + // >=1: reading lock; } // NewMutex creates and returns a new mutex. func NewMutex() *Mutex { return &Mutex{ - wid : gtype.NewInt64(), - number : gtype.NewInt(), + wid : gtype.NewInt64(), + state : gtype.NewInt32(), + locking : gtype.NewBool(), } } -// Lock locks mutex for writing. +// Lock locks the mutex for writing. // If the lock is already locked for reading or writing, // Lock blocks until the lock is available. func (m *Mutex) Lock() { - m.mu.Lock() - m.number.Set(1) - m.wid.Add(1) + if m.locking.Cas(false, true) { + m.mu.Lock() + // State should be changed after locks. + m.state.Set(-1) + m.wid.Add(1) + m.locking.Set(false) + } else { + runtime.Gosched() + m.Lock() + } } -// Unlock unlocks the write lock. -// It is safe to be called multiple times. +// Unlock unlocks the writing lock. +// It is safe to be called multiple times if there's any locks or not. func (m *Mutex) Unlock() { - if m.number.Cas(1, 0) { + if m.state.Cas(-1, 0) { m.mu.Unlock() } } +// TryLock tries locking the mutex for writing. +// It returns true if success, or if there's a write/reading lock on the mutex, +// it returns false. +func (m *Mutex) TryLock() bool { + if m.locking.Cas(false, true) { + m.mu.Lock() + // State should be changed after locks. + m.state.Set(-1) + m.wid.Add(1) + m.locking.Set(false) + return true + } + return false +} + // RLock locks mutex for reading. // If the mutex is already locked for writing, // It blocks until the lock is available. func (m *Mutex) RLock() { - m.mu.RLock() - m.number.Add(2) + if m.locking.Cas(false, true) { + m.mu.RLock() + // State should be changed after locks. + m.state.Add(1) + m.locking.Set(false) + } else { + runtime.Gosched() + m.RLock() + } } -// RUnlock undoes a single RLock call; -// it does not affect other simultaneous readers. -// It is a run-time error if mutex is not locked for reading -// on entry to RUnlock. -// It is safe to be called multiple times. +// RUnlock unlocks the reading lock. +// It is safe to be called multiple times if there's any locks or not. func (m *Mutex) RUnlock() { - if n := m.number.Val(); n >= 2 && n%2 == 0 { - if n := m.number.Add(-2); n >= 0 && n%2 == 0 { - m.mu.RUnlock() - } else { - m.number.Add(2) - } + if n := m.state.Val(); n >= 1 { + if m.state.Cas(n, n - 1) { + m.mu.RUnlock() + } else { + m.RUnlock() + } } } -// TryLock tries locking the mutex for writing. -// It returns true if success, or if there's a write/read lock on the mutex, -// it returns false. -func (m *Mutex) TryLock() bool { - if m.number.Cas(0, 1) { - m.mu.Lock() - m.wid.Add(1) - return true - } - return false -} - // TryRLock tries locking the mutex for reading. // It returns true if success, or if there's a writing lock on the mutex, it returns false. -// -// Note that it's not an atomic operation. -// -// TODO It should be an atomic operation. func (m *Mutex) TryRLock() bool { - // There must be no writing lock on the mutex. - if n := m.number.Val(); n%2 == 0 { - m.mu.RLock() - m.number.Add(2) - return true + if m.locking.Cas(false, true) { + if m.state.Val() >= 0 { + m.mu.RLock() + m.state.Add(1) + m.locking.Set(false) + return true + } } return false } +// IsLocked checks whether the mutex is locked by writing or reading lock. +func (m *Mutex) IsLocked() bool { + return m.state.Val() != 0 +} + +// IsRLocked checks whether the mutex is locked by writing lock. +func (m *Mutex) IsWLocked() bool { + return m.state.Val() < 0 +} + +// IsRLocked checks whether the mutex is locked by reading lock. +func (m *Mutex) IsRLocked() bool { + return m.state.Val() > 0 +} + // TryLockFunc tries locking the mutex for writing with given callback function . -// it returns true if success, or if there's a write/read lock on the mutex, +// it returns true if success, or if there's a write/reading lock on the mutex, // it returns false. // // It releases the lock after is executed. @@ -113,7 +144,7 @@ func (m *Mutex) TryLockFunc(f func()) bool { } // TryRLockFunc tries locking the mutex for reading with given callback function . -// It returns true if success, or if there's a write lock on the mutex, it returns false. +// It returns true if success, or if there's a writing lock on the mutex, it returns false. // // It releases the lock after is executed. func (m *Mutex) TryRLockFunc(f func()) bool { @@ -126,7 +157,7 @@ func (m *Mutex) TryRLockFunc(f func()) bool { } // LockFunc locks the mutex for writing with given callback function . -// If there's a write/read lock the mutex, it will blocks until the lock is released. +// If there's a write/reading lock the mutex, it will blocks until the lock is released. // // It releases the lock after is executed. func (m *Mutex) LockFunc(f func()) { @@ -136,7 +167,7 @@ func (m *Mutex) LockFunc(f func()) { } // RLockFunc locks the mutex for reading with given callback function . -// If there's a write lock the mutex, it will blocks until the lock is released. +// If there's a writing lock the mutex, it will blocks until the lock is released. // // It releases the lock after is executed. func (m *Mutex) RLockFunc(f func()) { diff --git a/geg/other/test.go b/geg/other/test.go index 6ef91fe4c..6e2190974 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -2,6 +2,7 @@ package main import ( "github.com/gogf/gf/g/container/garray" + "github.com/gogf/gf/g/os/glog" "github.com/gogf/gf/g/os/gmlock" "github.com/gogf/gf/g/test/gtest" "time" @@ -13,22 +14,19 @@ func main() { go func() { mu.LockFunc(func() { array.Append(1) - time.Sleep(100 * time.Millisecond) + time.Sleep(10000 * time.Millisecond) }) }() - go func() { - time.Sleep(50 * time.Millisecond) - mu.LockFunc(func() { - array.Append(1) - }) - }() - go func() { - time.Sleep(50 * time.Millisecond) - mu.LockFunc(func() { - array.Append(1) - }) - }() - + time.Sleep(10*time.Millisecond) + for i := 0; i < 10000; i++ { + go func(i int) { + time.Sleep(50 * time.Millisecond) + mu.LockFunc(func() { + glog.Print(i) + array.Append(1) + }) + }(i) + } go func() { time.Sleep(60 * time.Millisecond) mu.Unlock() diff --git a/geg/other/test2.go b/geg/other/test2.go index 4508700a1..3b6ef239b 100644 --- a/geg/other/test2.go +++ b/geg/other/test2.go @@ -1,20 +1,12 @@ package main import ( - "github.com/gogf/gf/g" - "github.com/gogf/gf/g/net/ghttp" + "fmt" + "sync" ) func main() { - s := g.Server() - s.BindHookHandler("/*any", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) { - r.Response.SetAllowCrossDomainRequest("*", "PUT,GET,POST,DELETE,OPTIONS") - r.Response.Header().Set("Access-Control-Allow-Credentials", "true") - r.Response.Header().Set("Access-Control-Allow-Headers", "X-Requested-With, Content-Type, token") - }) - s.Group("/v1").COMMON("*", func(r *ghttp.Request) { - r.Response.WriteJson(g.Map{"name": "john"}) - }) - s.SetPort(6789) - s.Run() + m := sync.RWMutex{} + m.Lock() + fmt.Println(m) }