add Cas function for gtype; improve gmlock

This commit is contained in:
John
2019-06-18 08:37:21 +08:00
parent 9db8ed2dfc
commit 78ccbeac3b
16 changed files with 229 additions and 147 deletions

View File

@ -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 <value> 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)
}

View File

@ -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 <value> 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 <delta> 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))
}

View File

@ -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 <value> into t.value and returns the previous value of t.value.
// Note: The parameter <value> 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

View File

@ -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 <value> 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 <delta> 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
}
}
// 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))
}

View File

@ -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 <value> 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 <delta> 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))
}

View File

@ -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 <value> 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 <delta> 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))
}

View File

@ -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 <value> 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 <delta> 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)
}

View File

@ -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 <value> 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 <delta> 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)
}

View File

@ -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 <value> into t.value and returns the previous value of t.value.
// Note: The parameter <value> 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()
}

View File

@ -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 <value> 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)
}

View File

@ -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 <value> 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 <delta> 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))
}

View File

@ -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 <value> 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 <delta> 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)
}

View File

@ -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 <value> 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 <delta> 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)
}

View File

@ -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)
}

View File

@ -15,17 +15,18 @@ import (
// 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.
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;
}
// NewMutex creates and returns a new mutex.
func NewMutex() *Mutex {
return &Mutex{
wid : gtype.NewInt64(),
rcount : gtype.NewInt(),
wcount : gtype.NewInt(),
number : gtype.NewInt(),
}
}
@ -33,29 +34,25 @@ func NewMutex() *Mutex {
// 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)
m.mu.Lock()
m.number.Set(1)
m.wid.Add(1)
}
// Unlock unlocks the write lock.
// It is safe to be called multiple times.
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.number.Cas(1, 0) {
m.mu.Unlock()
}
}
// 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()
m.number.Add(2)
}
// RUnlock undoes a single RLock call;
@ -64,11 +61,11 @@ func (m *Mutex) RLock() {
// on entry to RUnlock.
// It is safe to be called multiple times.
func (m *Mutex) RUnlock() {
if m.rcount.Val() > 0 {
if m.rcount.Add(-1) >= 0 {
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.rcount.Add(1)
m.number.Add(2)
}
}
}
@ -77,27 +74,25 @@ func (m *Mutex) RUnlock() {
// 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)
}
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 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.
//
// Note that it's not an atomic operation.
//
// TODO It should be an atomic operation.
func (m *Mutex) TryRLock() bool {
// There must be no write lock on mutex.
if m.wcount.Val() == 0 {
m.rcount.Add(1)
// 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
}
return false

View File

@ -1,15 +1,45 @@
package main
import (
"fmt"
"sync"
"github.com/gogf/gf/g/container/garray"
"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(100 * 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)
})
}()
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)
}