diff --git a/DONATOR.MD b/DONATOR.MD index 741ff45d0..6a9aa30e6 100644 --- a/DONATOR.MD +++ b/DONATOR.MD @@ -15,6 +15,7 @@ |潘兄|wechat|¥100.00 |Fly的狐狸|wechat|¥100.00 |土豆相公|alipay|¥66.60 +|蔡蔡|wechat|¥666.00 |上海金保证网络科技|bank|¥2000.00 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/gtype/bool.go b/g/container/gtype/bool.go index 1c5880141..802f292e0 100644 --- a/g/container/gtype/bool.go +++ b/g/container/gtype/bool.go @@ -29,21 +29,33 @@ func NewBool(value...bool) *Bool { } // Clone clones and returns a new concurrent-safe object for bool type. -func (t *Bool) Clone() *Bool { - return NewBool(t.Val()) +func (v *Bool) Clone() *Bool { + return NewBool(v.Val()) } // Set atomically stores into t.value and returns the previous value of t.value. -func (t *Bool) Set(value bool) (old bool) { +func (v *Bool) Set(value bool) (old bool) { if value { - old = atomic.SwapInt32(&t.value, 1) == 1 + old = atomic.SwapInt32(&v.value, 1) == 1 } else { - old = atomic.SwapInt32(&t.value, 0) == 1 + old = atomic.SwapInt32(&v.value, 0) == 1 } return } // Val atomically loads t.valueue. -func (t *Bool) Val() bool { - return atomic.LoadInt32(&t.value) > 0 +func (v *Bool) Val() bool { + return atomic.LoadInt32(&v.value) > 0 } + +// Cas executes the compare-and-swap operation for value. +func (v *Bool) Cas(old, new bool) bool { + var oldInt32, newInt32 int32 + if old { + oldInt32 = 1 + } + if new { + newInt32 = 1 + } + return atomic.CompareAndSwapInt32(&v.value, oldInt32, newInt32) +} \ No newline at end of file diff --git a/g/container/gtype/byte.go b/g/container/gtype/byte.go index 14d92c7a0..5fa32ac0a 100644 --- a/g/container/gtype/byte.go +++ b/g/container/gtype/byte.go @@ -26,21 +26,26 @@ func NewByte(value...byte) *Byte { } // Clone clones and returns a new concurrent-safe object for byte type. -func (t *Byte) Clone() *Byte { - return NewByte(t.Val()) +func (v *Byte) Clone() *Byte { + return NewByte(v.Val()) } // Set atomically stores into t.value and returns the previous value of t.value. -func (t *Byte) Set(value byte) (old byte) { - return byte(atomic.SwapInt32(&t.value, int32(value))) +func (v *Byte) Set(value byte) (old byte) { + return byte(atomic.SwapInt32(&v.value, int32(value))) } // Val atomically loads t.value. -func (t *Byte) Val() byte { - return byte(atomic.LoadInt32(&t.value)) +func (v *Byte) Val() byte { + return byte(atomic.LoadInt32(&v.value)) } // Add atomically adds to t.value and returns the new value. -func (t *Byte) Add(delta int) (new byte) { - return byte(atomic.AddInt32(&t.value, int32(delta))) +func (v *Byte) Add(delta byte) (new byte) { + return byte(atomic.AddInt32(&v.value, int32(delta))) +} + +// Cas executes the compare-and-swap operation for value. +func (v *Byte) Cas(old, new byte) bool { + return atomic.CompareAndSwapInt32(&v.value, int32(old), int32(new)) } diff --git a/g/container/gtype/bytes.go b/g/container/gtype/bytes.go index b36eecf05..a5784592f 100644 --- a/g/container/gtype/bytes.go +++ b/g/container/gtype/bytes.go @@ -23,21 +23,21 @@ func NewBytes(value...[]byte) *Bytes { } // Clone clones and returns a new concurrent-safe object for []byte type. -func (t *Bytes) Clone() *Bytes { - return NewBytes(t.Val()) +func (v *Bytes) Clone() *Bytes { + return NewBytes(v.Val()) } // Set atomically stores into t.value and returns the previous value of t.value. // Note: The parameter cannot be nil. -func (t *Bytes) Set(value []byte) (old []byte) { - old = t.Val() - t.value.Store(value) +func (v *Bytes) Set(value []byte) (old []byte) { + old = v.Val() + v.value.Store(value) return } // Val atomically loads t.value. -func (t *Bytes) Val() []byte { - if s := t.value.Load(); s != nil { +func (v *Bytes) Val() []byte { + if s := v.value.Load(); s != nil { return s.([]byte) } return nil diff --git a/g/container/gtype/float32.go b/g/container/gtype/float32.go index 912c05d89..26661a465 100644 --- a/g/container/gtype/float32.go +++ b/g/container/gtype/float32.go @@ -28,27 +28,27 @@ func NewFloat32(value...float32) *Float32 { } // Clone clones and returns a new concurrent-safe object for float32 type. -func (t *Float32) Clone() *Float32 { - return NewFloat32(t.Val()) +func (v *Float32) Clone() *Float32 { + return NewFloat32(v.Val()) } // Set atomically stores into t.value and returns the previous value of t.value. -func (t *Float32) Set(value float32) (old float32) { - return math.Float32frombits(atomic.SwapUint32(&t.value, math.Float32bits(value))) +func (v *Float32) Set(value float32) (old float32) { + return math.Float32frombits(atomic.SwapUint32(&v.value, math.Float32bits(value))) } // Val atomically loads t.value. -func (t *Float32) Val() float32 { - return math.Float32frombits(atomic.LoadUint32(&t.value)) +func (v *Float32) Val() float32 { + return math.Float32frombits(atomic.LoadUint32(&v.value)) } // Add atomically adds to t.value and returns the new value. -func (t *Float32) Add(delta float32) (new float32) { +func (v *Float32) Add(delta float32) (new float32) { for { - old := math.Float32frombits(t.value) + old := math.Float32frombits(v.value) new = old + delta if atomic.CompareAndSwapUint32( - (*uint32)(unsafe.Pointer(&t.value)), + (*uint32)(unsafe.Pointer(&v.value)), math.Float32bits(old), math.Float32bits(new), ) { @@ -56,4 +56,9 @@ func (t *Float32) Add(delta float32) (new float32) { } } return -} \ No newline at end of file +} + +// Cas executes the compare-and-swap operation for value. +func (v *Float32) Cas(old, new float32) bool { + return atomic.CompareAndSwapUint32(&v.value, uint32(old), uint32(new)) +} diff --git a/g/container/gtype/float64.go b/g/container/gtype/float64.go index b96419d01..c7f8968c6 100644 --- a/g/container/gtype/float64.go +++ b/g/container/gtype/float64.go @@ -28,27 +28,27 @@ func NewFloat64(value...float64) *Float64 { } // Clone clones and returns a new concurrent-safe object for float64 type. -func (t *Float64) Clone() *Float64 { - return NewFloat64(t.Val()) +func (v *Float64) Clone() *Float64 { + return NewFloat64(v.Val()) } // Set atomically stores into t.value and returns the previous value of t.value. -func (t *Float64) Set(value float64) (old float64) { - return math.Float64frombits(atomic.SwapUint64(&t.value, math.Float64bits(value))) +func (v *Float64) Set(value float64) (old float64) { + return math.Float64frombits(atomic.SwapUint64(&v.value, math.Float64bits(value))) } // Val atomically loads t.value. -func (t *Float64) Val() float64 { - return math.Float64frombits(atomic.LoadUint64(&t.value)) +func (v *Float64) Val() float64 { + return math.Float64frombits(atomic.LoadUint64(&v.value)) } // Add atomically adds to t.value and returns the new value. -func (t *Float64) Add(delta float64) (new float64) { +func (v *Float64) Add(delta float64) (new float64) { for { - old := math.Float64frombits(t.value) + old := math.Float64frombits(v.value) new = old + delta if atomic.CompareAndSwapUint64( - (*uint64)(unsafe.Pointer(&t.value)), + (*uint64)(unsafe.Pointer(&v.value)), math.Float64bits(old), math.Float64bits(new), ) { @@ -57,3 +57,8 @@ func (t *Float64) Add(delta float64) (new float64) { } return } + +// Cas executes the compare-and-swap operation for value. +func (v *Float64) Cas(old, new float64) bool { + return atomic.CompareAndSwapUint64(&v.value, uint64(old), uint64(new)) +} diff --git a/g/container/gtype/int.go b/g/container/gtype/int.go index 270829e66..fa1717ba2 100644 --- a/g/container/gtype/int.go +++ b/g/container/gtype/int.go @@ -26,21 +26,26 @@ func NewInt(value...int) *Int { } // Clone clones and returns a new concurrent-safe object for int type. -func (t *Int) Clone() *Int { - return NewInt(t.Val()) +func (v *Int) Clone() *Int { + return NewInt(v.Val()) } // Set atomically stores into t.value and returns the previous value of t.value. -func (t *Int) Set(value int) (old int) { - return int(atomic.SwapInt64(&t.value, int64(value))) +func (v *Int) Set(value int) (old int) { + return int(atomic.SwapInt64(&v.value, int64(value))) } // Val atomically loads t.value. -func (t *Int) Val() int { - return int(atomic.LoadInt64(&t.value)) +func (v *Int) Val() int { + return int(atomic.LoadInt64(&v.value)) } // Add atomically adds to t.value and returns the new value. -func (t *Int) Add(delta int) (new int) { - return int(atomic.AddInt64(&t.value, int64(delta))) +func (v *Int) Add(delta int) (new int) { + return int(atomic.AddInt64(&v.value, int64(delta))) +} + +// Cas executes the compare-and-swap operation for value. +func (v *Int) Cas(old, new int) bool { + return atomic.CompareAndSwapInt64(&v.value, int64(old), int64(new)) } \ No newline at end of file diff --git a/g/container/gtype/int32.go b/g/container/gtype/int32.go index 06517d8bb..3e1bcfad5 100644 --- a/g/container/gtype/int32.go +++ b/g/container/gtype/int32.go @@ -26,21 +26,26 @@ func NewInt32(value...int32) *Int32 { } // Clone clones and returns a new concurrent-safe object for int32 type. -func (t *Int32) Clone() *Int32 { - return NewInt32(t.Val()) +func (v *Int32) Clone() *Int32 { + return NewInt32(v.Val()) } // Set atomically stores into t.value and returns the previous value of t.value. -func (t *Int32) Set(value int32) (old int32) { - return atomic.SwapInt32(&t.value, value) +func (v *Int32) Set(value int32) (old int32) { + return atomic.SwapInt32(&v.value, value) } // Val atomically loads t.value. -func (t *Int32) Val() int32 { - return atomic.LoadInt32(&t.value) +func (v *Int32) Val() int32 { + return atomic.LoadInt32(&v.value) } // Add atomically adds to t.value and returns the new value. -func (t *Int32) Add(delta int32) (new int32) { - return atomic.AddInt32(&t.value, delta) +func (v *Int32) Add(delta int32) (new int32) { + return atomic.AddInt32(&v.value, delta) +} + +// Cas executes the compare-and-swap operation for value. +func (v *Int32) Cas(old, new int32) bool { + return atomic.CompareAndSwapInt32(&v.value, old, new) } \ No newline at end of file diff --git a/g/container/gtype/int64.go b/g/container/gtype/int64.go index 38530dff4..9f2a35096 100644 --- a/g/container/gtype/int64.go +++ b/g/container/gtype/int64.go @@ -26,21 +26,26 @@ func NewInt64(value...int64) *Int64 { } // Clone clones and returns a new concurrent-safe object for int64 type. -func (t *Int64) Clone() *Int64 { - return NewInt64(t.Val()) +func (v *Int64) Clone() *Int64 { + return NewInt64(v.Val()) } // Set atomically stores into t.value and returns the previous value of t.value. -func (t *Int64) Set(value int64) (old int64) { - return atomic.SwapInt64(&t.value, value) +func (v *Int64) Set(value int64) (old int64) { + return atomic.SwapInt64(&v.value, value) } // Val atomically loads t.value. -func (t *Int64) Val() int64 { - return atomic.LoadInt64(&t.value) +func (v *Int64) Val() int64 { + return atomic.LoadInt64(&v.value) } // Add atomically adds to t.value and returns the new value. -func (t *Int64) Add(delta int64) int64 { - return atomic.AddInt64(&t.value, delta) +func (v *Int64) Add(delta int64) int64 { + return atomic.AddInt64(&v.value, delta) +} + +// Cas executes the compare-and-swap operation for value. +func (v *Int64) Cas(old, new int64) bool { + return atomic.CompareAndSwapInt64(&v.value, old, new) } \ No newline at end of file diff --git a/g/container/gtype/interface.go b/g/container/gtype/interface.go index 0b510320e..6d70f2ac2 100644 --- a/g/container/gtype/interface.go +++ b/g/container/gtype/interface.go @@ -25,19 +25,19 @@ func NewInterface(value...interface{}) *Interface { } // Clone clones and returns a new concurrent-safe object for interface{} type. -func (t *Interface) Clone() *Interface { - return NewInterface(t.Val()) +func (v *Interface) Clone() *Interface { + return NewInterface(v.Val()) } // Set atomically stores into t.value and returns the previous value of t.value. // Note: The parameter cannot be nil. -func (t *Interface) Set(value interface{}) (old interface{}) { - old = t.Val() - t.value.Store(value) +func (v *Interface) Set(value interface{}) (old interface{}) { + old = v.Val() + v.value.Store(value) return } // Val atomically loads t.value. -func (t *Interface) Val() interface{} { - return t.value.Load() +func (v *Interface) Val() interface{} { + return v.value.Load() } \ No newline at end of file diff --git a/g/container/gtype/string.go b/g/container/gtype/string.go index 390a0e507..2e1567a8a 100644 --- a/g/container/gtype/string.go +++ b/g/container/gtype/string.go @@ -25,20 +25,20 @@ func NewString(value...string) *String { } // Clone clones and returns a new concurrent-safe object for string type. -func (t *String) Clone() *String { - return NewString(t.Val()) +func (v *String) Clone() *String { + return NewString(v.Val()) } // Set atomically stores into t.value and returns the previous value of t.value. -func (t *String) Set(value string) (old string) { - old = t.Val() - t.value.Store(value) +func (v *String) Set(value string) (old string) { + old = v.Val() + v.value.Store(value) return } // Val atomically loads t.value. -func (t *String) Val() string { - s := t.value.Load() +func (v *String) Val() string { + s := v.value.Load() if s != nil { return s.(string) } diff --git a/g/container/gtype/uint.go b/g/container/gtype/uint.go index 3bc0b28ce..8f26024f6 100644 --- a/g/container/gtype/uint.go +++ b/g/container/gtype/uint.go @@ -26,21 +26,26 @@ func NewUint(value...uint) *Uint { } // Clone clones and returns a new concurrent-safe object for uint type. -func (t *Uint) Clone() *Uint { - return NewUint(t.Val()) +func (v *Uint) Clone() *Uint { + return NewUint(v.Val()) } // Set atomically stores into t.value and returns the previous value of t.value. -func (t *Uint) Set(value uint) (old uint) { - return uint(atomic.SwapUint64(&t.value, uint64(value))) +func (v *Uint) Set(value uint) (old uint) { + return uint(atomic.SwapUint64(&v.value, uint64(value))) } // Val atomically loads t.value. -func (t *Uint) Val() uint { - return uint(atomic.LoadUint64(&t.value)) +func (v *Uint) Val() uint { + return uint(atomic.LoadUint64(&v.value)) } // Add atomically adds to t.value and returns the new value. -func (t *Uint) Add(delta uint) (new uint) { - return uint(atomic.AddUint64(&t.value, uint64(delta))) +func (v *Uint) Add(delta uint) (new uint) { + return uint(atomic.AddUint64(&v.value, uint64(delta))) +} + +// Cas executes the compare-and-swap operation for value. +func (v *Uint) Cas(old, new uint) bool { + return atomic.CompareAndSwapUint64(&v.value, uint64(old), uint64(new)) } \ No newline at end of file diff --git a/g/container/gtype/uint32.go b/g/container/gtype/uint32.go index 35db55b86..e39b070b1 100644 --- a/g/container/gtype/uint32.go +++ b/g/container/gtype/uint32.go @@ -26,21 +26,26 @@ func NewUint32(value...uint32) *Uint32 { } // Clone clones and returns a new concurrent-safe object for uint32 type. -func (t *Uint32) Clone() *Uint32 { - return NewUint32(t.Val()) +func (v *Uint32) Clone() *Uint32 { + return NewUint32(v.Val()) } // Set atomically stores into t.value and returns the previous value of t.value. -func (t *Uint32) Set(value uint32) (old uint32) { - return atomic.SwapUint32(&t.value, value) +func (v *Uint32) Set(value uint32) (old uint32) { + return atomic.SwapUint32(&v.value, value) } // Val atomically loads t.value. -func (t *Uint32) Val() uint32 { - return atomic.LoadUint32(&t.value) +func (v *Uint32) Val() uint32 { + return atomic.LoadUint32(&v.value) } // Add atomically adds to t.value and returns the new value. -func (t *Uint32) Add(delta uint32) (new uint32) { - return atomic.AddUint32(&t.value, delta) +func (v *Uint32) Add(delta uint32) (new uint32) { + return atomic.AddUint32(&v.value, delta) +} + +// Cas executes the compare-and-swap operation for value. +func (v *Uint32) Cas(old, new uint32) bool { + return atomic.CompareAndSwapUint32(&v.value, old, new) } \ No newline at end of file diff --git a/g/container/gtype/uint64.go b/g/container/gtype/uint64.go index 923068398..ce8b42f90 100644 --- a/g/container/gtype/uint64.go +++ b/g/container/gtype/uint64.go @@ -26,21 +26,26 @@ func NewUint64(value...uint64) *Uint64 { } // Clone clones and returns a new concurrent-safe object for uint64 type. -func (t *Uint64) Clone() *Uint64 { - return NewUint64(t.Val()) +func (v *Uint64) Clone() *Uint64 { + return NewUint64(v.Val()) } // Set atomically stores into t.value and returns the previous value of t.value. -func (t *Uint64) Set(value uint64) (old uint64) { - return atomic.SwapUint64(&t.value, value) +func (v *Uint64) Set(value uint64) (old uint64) { + return atomic.SwapUint64(&v.value, value) } // Val atomically loads t.value. -func (t *Uint64) Val() uint64 { - return atomic.LoadUint64(&t.value) +func (v *Uint64) Val() uint64 { + return atomic.LoadUint64(&v.value) } // Add atomically adds to t.value and returns the new value. -func (t *Uint64) Add(delta uint64) (new uint64) { - return atomic.AddUint64(&t.value, delta) +func (v *Uint64) Add(delta uint64) (new uint64) { + return atomic.AddUint64(&v.value, delta) +} + +// Cas executes the compare-and-swap operation for value. +func (v *Uint64) Cas(old, new uint64) bool { + return atomic.CompareAndSwapUint64(&v.value, old, new) } \ No newline at end of file diff --git a/g/crypto/gaes/gaes.go b/g/crypto/gaes/gaes.go index 14f34f44b..48a075a9b 100644 --- a/g/crypto/gaes/gaes.go +++ b/g/crypto/gaes/gaes.go @@ -18,8 +18,18 @@ const ( ivDefValue = "I Love Go Frame!" ) -// AES加密, 使用CBC模式,注意key必须为16/24/32位长度,iv初始化向量为非必需参数 +// Encrypt is alias of EncryptCBC. func Encrypt(plainText []byte, key []byte, iv...[]byte) ([]byte, error) { + return EncryptCBC(plainText, key, iv...) +} + +// Decrypt is alias of DecryptCBC. +func Decrypt(cipherText []byte, key []byte, iv...[]byte) ([]byte, error) { + return DecryptCBC(cipherText, key, iv...) +} + +// AES加密, 使用CBC模式,注意key必须为16/24/32位长度,iv初始化向量为非必需参数。 +func EncryptCBC(plainText []byte, key []byte, iv...[]byte) ([]byte, error) { block, err := aes.NewCipher(key) if err != nil { return nil, err @@ -40,7 +50,7 @@ func Encrypt(plainText []byte, key []byte, iv...[]byte) ([]byte, error) { } // AES解密, 使用CBC模式,注意key必须为16/24/32位长度,iv初始化向量为非必需参数 -func Decrypt(cipherText []byte, key []byte, iv...[]byte) ([]byte, error) { +func DecryptCBC(cipherText []byte, key []byte, iv...[]byte) ([]byte, error) { block, err := aes.NewCipher(key) if err != nil { return nil, err @@ -62,9 +72,9 @@ func Decrypt(cipherText []byte, key []byte, iv...[]byte) ([]byte, error) { plainText := make([]byte, len(cipherText)) blockModel.CryptBlocks(plainText, cipherText) plainText, e := PKCS5UnPadding(plainText, blockSize) - if e != nil { - return nil, e - } + if e != nil { + return nil, e + } return plainText, nil } @@ -98,3 +108,58 @@ func PKCS5UnPadding(src []byte, blockSize int) ([]byte, error) { return src[:(length - unpadding)], nil } + +// AES加密, 使用CFB模式。 +// 注意key必须为16/24/32位长度,padding返回补位长度,iv初始化向量为非必需参数。 +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初始化向量为非必需参数。 +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/net/ghttp/ghttp.go b/g/net/ghttp/ghttp.go index 87840a86c..bfff905be 100644 --- a/g/net/ghttp/ghttp.go +++ b/g/net/ghttp/ghttp.go @@ -4,7 +4,5 @@ // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. -// Package ghttp provides a powerful http server and a simple client. -// -// ghttp是GF框架的核心模块,实现了一个强大的Web Server,并提供了一个简便的HTTP客户端。 +// Package ghttp provides powerful http server and simple client implements. package ghttp diff --git a/g/os/glog/glog.go b/g/os/glog/glog.go index 45f39011f..a3d6d3b51 100644 --- a/g/os/glog/glog.go +++ b/g/os/glog/glog.go @@ -37,8 +37,8 @@ func init() { } // SetPath sets the directory path for file logging. -func SetPath(path string) { - logger.SetPath(path) +func SetPath(path string) error { + return logger.SetPath(path) } // GetPath returns the logging directory path for file logging. @@ -89,7 +89,7 @@ func SetAsync(enabled bool) { logger.SetAsync(enabled) } -// SetStdoutPrint sets whether ouptput the logging contents to stdout, which is false in default. +// SetStdoutPrint sets whether ouptput the logging contents to stdout, which is true in default. func SetStdoutPrint(enabled bool) { logger.SetStdoutPrint(enabled) } diff --git a/g/os/glog/glog_logger.go b/g/os/glog/glog_logger.go index 5d6562d98..282e61c09 100644 --- a/g/os/glog/glog_logger.go +++ b/g/os/glog/glog_logger.go @@ -17,6 +17,7 @@ import ( "github.com/gogf/gf/g/util/gconv" "io" "os" + "regexp" "runtime" "strings" "time" @@ -356,6 +357,10 @@ func (l *Logger) GetBacktrace(skip...int) string { // Find the true caller file path using custom skip. index := 1 goRoot := runtime.GOROOT() + if goRoot != "" { + goRoot = strings.Replace(goRoot, "\\", "/", -1) + goRoot = regexp.QuoteMeta(goRoot) + } for i := from + customSkip + l.btSkip; i < 1000; i++ { if _, file, cline, ok := runtime.Caller(i); ok && len(file) > 2 { if (goRoot == "" || !gregex.IsMatchString("^" + goRoot, file)) && !gregex.IsMatchString(``, file) { 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 4faf7b82d..425c49d2c 100644 --- a/g/os/gmlock/gmlock_mutex.go +++ b/g/os/gmlock/gmlock_mutex.go @@ -8,103 +8,130 @@ 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. - rcount *gtype.Int // Reading locks count. - wcount *gtype.Int // Writing locks count. + 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(), - rcount : gtype.NewInt(), - wcount : 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.wcount.Add(1) - m.mu.Lock() - 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.wcount.Val() > 0 { - if m.wcount.Add(-1) >= 0 { - m.mu.Unlock() - } else { - m.wcount.Add(1) - } - } + 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) { + if m.state.Cas(0, -1) { + m.mu.Lock() + m.wid.Add(1) + m.locking.Set(false) + return true + } + m.locking.Set(false) + } + 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.rcount.Add(1) - m.mu.RLock() + 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 m.rcount.Val() > 0 { - if m.rcount.Add(-1) >= 0 { - m.mu.RUnlock() - } else { - m.rcount.Add(1) - } + 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 { - // The first check, but it cannot ensure the atomicity. - if m.wcount.Val() == 0 && m.rcount.Val() == 0 { - // The second check, it ensures the atomicity with atomic Add. - if m.wcount.Add(1) == 1 { - m.mu.Lock() - m.wid.Add(1) - return true - } else { - m.wcount.Add(-1) - } - } - return false -} - // TryRLock tries locking the mutex for reading. -// 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. func (m *Mutex) TryRLock() bool { - // There must be no write lock on mutex. - if m.wcount.Val() == 0 { - m.rcount.Add(1) - m.mu.RLock() - 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. @@ -118,7 +145,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 { @@ -131,7 +158,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()) { @@ -141,7 +168,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/g/test/gtest/gtest.go b/g/test/gtest/gtest.go index 9c3adc920..8e9e2b838 100644 --- a/g/test/gtest/gtest.go +++ b/g/test/gtest/gtest.go @@ -14,6 +14,7 @@ import ( "reflect" "regexp" "runtime" + "strings" "testing" ) @@ -315,8 +316,12 @@ func getBacktrace(skip...int) string { } } } - // Get the caller backtrace from business caller file. + // Converting all file separator to "/". goRoot := runtime.GOROOT() + if goRoot != "" { + goRoot = strings.Replace(goRoot, "\\", "/", -1) + goRoot = regexp.QuoteMeta(goRoot) + } for i := from + customSkip; i < 10000; i++ { if _, file, cline, ok := runtime.Caller(i); ok && file != "" { if reg, _ := regexp.Compile(``); reg.MatchString(file) { diff --git a/geg/other/test.go b/geg/other/test.go index 7e31feb29..6e2190974 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -1,15 +1,43 @@ package main import ( - "fmt" - "sync" + "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" ) func main() { - wg := sync.WaitGroup{} - wg.Add(1) - wg.Add(-100) - wg.Add() - wg.Wait() - fmt.Println(1) + mu := gmlock.NewMutex() + array := garray.New() + go func() { + mu.LockFunc(func() { + array.Append(1) + time.Sleep(10000 * time.Millisecond) + }) + }() + 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() + mu.Unlock() + mu.Unlock() + }() + + time.Sleep(20 * time.Millisecond) + gtest.Assert(array.Len(), 1) + time.Sleep(50 * time.Millisecond) + gtest.Assert(array.Len(), 1) + time.Sleep(50 * time.Millisecond) + gtest.Assert(array.Len(), 3) } diff --git a/geg/other/test2.go b/geg/other/test2.go index 4508700a1..45e8670ab 100644 --- a/geg/other/test2.go +++ b/geg/other/test2.go @@ -1,20 +1,14 @@ package main import ( - "github.com/gogf/gf/g" - "github.com/gogf/gf/g/net/ghttp" + "fmt" + "github.com/gogf/gf/g/os/gmlock" + "time" ) 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() + key := "test3" + gmlock.Lock(key, 200*time.Millisecond) + fmt.Println("TryLock:", gmlock.TryLock(key)) + fmt.Println("TryLock:", gmlock.TryLock(key)) }