perf(util): add mutex with run function safely

. #22309205

Signed-off-by: slasher <shenjie1@oppo.com>
This commit is contained in:
slasher 2024-06-19 20:30:03 +08:00
parent c65a4aa48b
commit 1d1efefa15
3 changed files with 292 additions and 0 deletions

View 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
}

View 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}
}

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