mirror of
https://github.com/cubefs/cubefs.git
synced 2026-08-02 02:00:56 +00:00
perf(util): add mutex with run function safely
. #22309205 Signed-off-by: slasher <shenjie1@oppo.com>
This commit is contained in:
parent
c65a4aa48b
commit
1d1efefa15
98
blobstore/util/mutex/example_test.go
Normal file
98
blobstore/util/mutex/example_test.go
Normal file
@ -0,0 +1,98 @@
|
||||
// Copyright 2024 The CubeFS Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
// implied. See the License for the specific language governing
|
||||
// permissions and limitations under the License.
|
||||
|
||||
package mutex_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/cubefs/cubefs/blobstore/util/mutex"
|
||||
)
|
||||
|
||||
func ExampleMutex() {
|
||||
var m mutex.Mutex
|
||||
m.Lock()
|
||||
fmt.Println("mutex locked")
|
||||
m.Unlock()
|
||||
|
||||
err := m.WithLock(func() error { return mutex.Nil })
|
||||
fmt.Println("mutex got error:", err)
|
||||
|
||||
fmt.Println()
|
||||
l := mutex.NewLocker(&sync.Mutex{})
|
||||
l.Lock()
|
||||
fmt.Println("locker locked")
|
||||
l.Unlock()
|
||||
|
||||
err = l.WithLock(func() error { return fmt.Errorf("locker error") })
|
||||
fmt.Println("locker got error:", err)
|
||||
|
||||
// Output:
|
||||
// mutex locked
|
||||
// mutex got error: mutex.Nil
|
||||
//
|
||||
// locker locked
|
||||
// locker got error: locker error
|
||||
}
|
||||
|
||||
func ExampleRWMutex() {
|
||||
var m mutex.RWMutex
|
||||
m.Lock()
|
||||
fmt.Println("rwmutex locked")
|
||||
m.Unlock()
|
||||
|
||||
m.RLock()
|
||||
fmt.Println("rwmutex rlocked")
|
||||
m.RUnlock()
|
||||
|
||||
err := m.WithLock(func() error { return mutex.Nil })
|
||||
fmt.Println("rwmutex got error:", err)
|
||||
err = m.WithRLock(func() error { return nil })
|
||||
fmt.Println("rwmutex got nil:", err == nil)
|
||||
|
||||
rl := m.RLocker()
|
||||
rl.Lock()
|
||||
rl.Lock()
|
||||
fmt.Println("rwmutex RLock as Lock")
|
||||
rl.Unlock()
|
||||
rl.Unlock()
|
||||
|
||||
fmt.Println()
|
||||
l := mutex.NewRLocker(&sync.RWMutex{})
|
||||
l.Lock()
|
||||
fmt.Println("rlocker locked")
|
||||
l.Unlock()
|
||||
|
||||
l.RLock()
|
||||
fmt.Println("rlocker rlocked")
|
||||
l.RUnlock()
|
||||
|
||||
err = l.WithLock(func() error { return mutex.Nil })
|
||||
fmt.Println("rlocker got error:", err)
|
||||
err = l.WithRLock(func() error { return nil })
|
||||
fmt.Println("rlocker got nil:", err == nil)
|
||||
|
||||
// Output:
|
||||
// rwmutex locked
|
||||
// rwmutex rlocked
|
||||
// rwmutex got error: mutex.Nil
|
||||
// rwmutex got nil: true
|
||||
// rwmutex RLock as Lock
|
||||
//
|
||||
// rlocker locked
|
||||
// rlocker rlocked
|
||||
// rlocker got error: mutex.Nil
|
||||
// rlocker got nil: true
|
||||
}
|
||||
116
blobstore/util/mutex/mutex.go
Normal file
116
blobstore/util/mutex/mutex.go
Normal file
@ -0,0 +1,116 @@
|
||||
// Copyright 2024 The CubeFS Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
// implied. See the License for the specific language governing
|
||||
// permissions and limitations under the License.
|
||||
|
||||
package mutex
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// TODO: add TryLock and TryRLock upgrating to Go1.18+
|
||||
|
||||
type nilError struct{}
|
||||
|
||||
func (nilError) Error() string { return "mutex.Nil" }
|
||||
|
||||
// Nil is the error which you want an error in function WithLock or WithRLock,
|
||||
// and then returns it outside to do other choice.
|
||||
var Nil error = nilError{}
|
||||
|
||||
// Locker represents an object that can be locked, unlocked.
|
||||
type Locker interface {
|
||||
sync.Locker
|
||||
}
|
||||
|
||||
// WithLocker represents a function to run with locker.
|
||||
type WithLocker interface {
|
||||
Locker
|
||||
// WithLock runs function in lock.
|
||||
WithLock(func() error) error
|
||||
}
|
||||
|
||||
// RLocker represents a reader/writer mutual exclusion locker.
|
||||
type RLocker interface {
|
||||
Locker
|
||||
RLock()
|
||||
RUnlock()
|
||||
}
|
||||
|
||||
// WithRLocker represents a function to run with reading locker.
|
||||
type WithRLocker interface {
|
||||
RLocker
|
||||
WithLocker
|
||||
// WithRLock runs function in reading lock.
|
||||
WithRLock(func() error) error
|
||||
}
|
||||
|
||||
// Mutex is a WithLocker with sync.Mutex.
|
||||
type Mutex struct{ sync.Mutex }
|
||||
|
||||
var _ WithLocker = (*Mutex)(nil)
|
||||
|
||||
func (m *Mutex) WithLock(f func() error) error {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
return f()
|
||||
}
|
||||
|
||||
type mutex struct{ Locker }
|
||||
|
||||
func (m *mutex) WithLock(f func() error) error {
|
||||
m.Locker.Lock()
|
||||
defer m.Locker.Unlock()
|
||||
return f()
|
||||
}
|
||||
|
||||
// NewLocker returns a WithLocker with the Locker.
|
||||
func NewLocker(locker Locker) WithLocker {
|
||||
return &mutex{Locker: locker}
|
||||
}
|
||||
|
||||
// RWMutex is a WithRLocker with sync.RWMutex.
|
||||
type RWMutex struct{ sync.RWMutex }
|
||||
|
||||
var _ WithRLocker = (*RWMutex)(nil)
|
||||
|
||||
func (m *RWMutex) WithLock(f func() error) error {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
return f()
|
||||
}
|
||||
|
||||
func (m *RWMutex) WithRLock(f func() error) error {
|
||||
m.RLock()
|
||||
defer m.RUnlock()
|
||||
return f()
|
||||
}
|
||||
|
||||
type rwMutex struct{ RLocker }
|
||||
|
||||
func (m *rwMutex) WithLock(f func() error) error {
|
||||
m.RLocker.Lock()
|
||||
defer m.RLocker.Unlock()
|
||||
return f()
|
||||
}
|
||||
|
||||
func (m *rwMutex) WithRLock(f func() error) error {
|
||||
m.RLocker.RLock()
|
||||
defer m.RLocker.RUnlock()
|
||||
return f()
|
||||
}
|
||||
|
||||
// NewRLocker returns a WithRLocker with the RLocker.
|
||||
func NewRLocker(rlocker RLocker) WithRLocker {
|
||||
return &rwMutex{RLocker: rlocker}
|
||||
}
|
||||
78
blobstore/util/mutex/mutex_test.go
Normal file
78
blobstore/util/mutex/mutex_test.go
Normal file
@ -0,0 +1,78 @@
|
||||
// Copyright 2024 The CubeFS Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
// implied. See the License for the specific language governing
|
||||
// permissions and limitations under the License.
|
||||
|
||||
package mutex_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/cubefs/cubefs/blobstore/util/mutex"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMutexWith(t *testing.T) {
|
||||
var m mutex.Mutex
|
||||
val := -1
|
||||
f := func() error {
|
||||
val++
|
||||
switch val {
|
||||
case 0:
|
||||
return nil
|
||||
case 1:
|
||||
return mutex.Nil
|
||||
default:
|
||||
return errors.New("")
|
||||
}
|
||||
}
|
||||
require.Nil(t, m.WithLock(f))
|
||||
nilErr := m.WithLock(f)
|
||||
require.ErrorIs(t, nilErr, mutex.Nil)
|
||||
require.Equal(t, mutex.Nil, nilErr)
|
||||
require.Error(t, m.WithLock(f))
|
||||
}
|
||||
|
||||
func Benchmark_Span_Assertion(b *testing.B) {
|
||||
var m mutex.RWMutex
|
||||
f := func() error { return nil }
|
||||
b.Run("Lock", func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
for ii := 0; ii <= b.N; ii++ {
|
||||
m.Lock()
|
||||
_ = struct{}{}
|
||||
m.Unlock()
|
||||
}
|
||||
})
|
||||
b.Run("WithLock", func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
for ii := 0; ii <= b.N; ii++ {
|
||||
m.WithLock(f)
|
||||
}
|
||||
})
|
||||
|
||||
b.Run("RLock", func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
for ii := 0; ii <= b.N; ii++ {
|
||||
m.RLock()
|
||||
_ = struct{}{}
|
||||
m.RUnlock()
|
||||
}
|
||||
})
|
||||
b.Run("WithRLock", func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
for ii := 0; ii <= b.N; ii++ {
|
||||
m.WithRLock(f)
|
||||
}
|
||||
})
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user