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/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/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..bf40cc5bf 100644 --- a/g/os/gmlock/gmlock_mutex.go +++ b/g/os/gmlock/gmlock_mutex.go @@ -8,98 +8,114 @@ 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 } // 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 +129,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 +142,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 +152,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) }