Skip to content

Commit

Permalink
Merge pull request #1 from ecodeclub/dev
Browse files Browse the repository at this point in the history
在list package中增加了skiplist
  • Loading branch information
AIJoe1995 authored Oct 16, 2023
2 parents 4f9b296 + 5f9ae28 commit afc4426
Show file tree
Hide file tree
Showing 6 changed files with 205 additions and 60 deletions.
2 changes: 1 addition & 1 deletion .CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# 开发中

- [syncx: 支持分key加锁](https://github.com/ecodeclub/ekit/pull/224)
# v0.0.8
- [atomicx: 泛型封装 atomic.Value](https://github.com/gotomicro/ekit/pull/101)
- [queue: API 定义](https://github.com/gotomicro/ekit/pull/109)
Expand Down
4 changes: 2 additions & 2 deletions internal/errs/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ func NewErrIndexOutOfRange(length int, index int) error {
}

// NewErrInvalidType 创建一个代表类型转换失败的错误
func NewErrInvalidType(want, got string) error {
return fmt.Errorf("ekit: 类型转换失败,want:%s, got:%s", want, got)
func NewErrInvalidType(want string, got any) error {
return fmt.Errorf("ekit: 类型转换失败,预期类型:%s, 实际值:%#v", want, got)
}

func NewErrInvalidIntervalValue(interval time.Duration) error {
Expand Down
80 changes: 80 additions & 0 deletions syncx/segment_key_lock.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Copyright 2021 ecodeclub
//
// 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 syncx

import (
"hash/fnv"
"sync"
)

// SegmentKeysLock 部分key lock结构定义
type SegmentKeysLock struct {
locks []*sync.RWMutex
size uint32
}

// NewSegmentKeysLock 创建 SegmentKeysLock 示例
func NewSegmentKeysLock(size uint32) *SegmentKeysLock {
locks := make([]*sync.RWMutex, size)
for i := range locks {
locks[i] = &sync.RWMutex{}
}
return &SegmentKeysLock{
locks: locks,
size: size,
}
}

// hash 索引锁的hash函数
func (s *SegmentKeysLock) hash(key string) uint32 {
h := fnv.New32a()
_, _ = h.Write([]byte(key))
return h.Sum32()
}

// RLock 读锁加锁
func (s *SegmentKeysLock) RLock(key string) {
s.getLock(key).RLock()
}

// TryRLock 试着加读锁,加锁成功会返回
func (s *SegmentKeysLock) TryRLock(key string) bool {
return s.getLock(key).TryRLock()
}

// RUnlock 读锁解锁
func (s *SegmentKeysLock) RUnlock(key string) {
s.getLock(key).RUnlock()
}

// Lock 写锁加锁
func (s *SegmentKeysLock) Lock(key string) {
s.getLock(key).Lock()
}

// TryLock 试着加锁,加锁成功会返回 true
func (s *SegmentKeysLock) TryLock(key string) bool {
return s.getLock(key).TryLock()
}

// Unlock 写锁解锁
func (s *SegmentKeysLock) Unlock(key string) {
s.getLock(key).Unlock()
}

func (s *SegmentKeysLock) getLock(key string) *sync.RWMutex {
hash := s.hash(key)
return s.locks[hash%s.size]
}
67 changes: 67 additions & 0 deletions syncx/segment_key_lock_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright 2021 ecodeclub
//
// 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 syncx

import (
"testing"

"github.com/stretchr/testify/assert"
)

// 通过 TryLock 和 TryRLock 来判定加锁问题
// 也就是只判定我们拿到了正确的锁,但是没有判定并发与互斥

// TestNewSegmentKeysLock_Lock 测试 Lock, UnLock 和 TryLock
func TestNewSegmentKeysLock_Lock(t *testing.T) {
l := NewSegmentKeysLock(8)
key1 := "key1"
l.Lock(key1)
// 必然加锁失败
assert.False(t, l.TryLock(key1))
// 读锁也失败
assert.False(t, l.TryRLock(key1))
key2 := "key2"
// 加锁成功
assert.True(t, l.TryLock(key2))
// 解锁不会触发 panic
defer l.Unlock(key2)

// 释放锁
l.Unlock(key1)
// 此时应该预期自己可以再次加锁
assert.True(t, l.TryLock(key1))
}

func TestNewSegmentKeysLock_RLock(t *testing.T) {
l := NewSegmentKeysLock(8)
key1, key2 := "key1", "key2"
l.RLock(key1)
// 必然加锁失败
assert.False(t, l.TryLock(key1))
// 读锁可以成功
assert.True(t, l.TryRLock(key1))
// 加锁成功
assert.True(t, l.TryRLock(key2))
// 解锁不会触发 panic
defer l.RUnlock(key2)

// 释放读锁
l.RUnlock(key1)
// 此时还有一个读锁没有释放
assert.False(t, l.TryLock(key1))
// 再次释放读锁
l.RUnlock(key1)
assert.True(t, l.TryLock(key1))
}
Loading

0 comments on commit afc4426

Please sign in to comment.