3 Commits
6 changed files with 103 additions and 45 deletions

No files matched your search

+1 -1
View File
@@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier same "printed page" as the copyright notice for easier
identification within third-party archives. identification within third-party archives.
Copyright 2025 肖其顿 Copyright 2025-2026 肖其顿 (XIAO QI DUN)
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
+4 -12
View File
@@ -1,13 +1,5 @@
Copyright 2025 肖其顿 klock
Copyright 2025-2026 肖其顿 (XIAO QI DUN)
Licensed under the Apache License, Version 2.0 (the "License"); This product includes software developed by
you may not use this file except in compliance with the License. 肖其顿 (XIAO QI DUN) (https://github.com/xiaoqidun/klock).
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.
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright 2025 肖其顿 // Copyright 2025-2026 肖其顿 (XIAO QI DUN)
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
+1 -1
View File
@@ -1,3 +1,3 @@
module github.com/xiaoqidun/klock module github.com/xiaoqidun/klock
go 1.25.1 go 1.24.0
+33 -15
View File
@@ -1,4 +1,4 @@
// Copyright 2025 肖其顿 // Copyright 2025-2026 肖其顿 (XIAO QI DUN)
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@@ -185,6 +185,7 @@ func (kl *KeyLock) TryLock(key string) bool {
// LockWithTimeout 尝试在给定的时间内为指定的键获取写锁。 // LockWithTimeout 尝试在给定的时间内为指定的键获取写锁。
// 如果在超时前成功获取锁,则返回 true;否则返回 false。 // 如果在超时前成功获取锁,则返回 true;否则返回 false。
func (kl *KeyLock) LockWithTimeout(key string, timeout time.Duration) bool { func (kl *KeyLock) LockWithTimeout(key string, timeout time.Duration) bool {
deadline := time.Now().Add(timeout)
le := kl.prepareLock(key) le := kl.prepareLock(key)
// 立即尝试一次,以避免在锁可用时产生不必要的延迟。 // 立即尝试一次,以避免在锁可用时产生不必要的延迟。
if le.rw.TryLock() { if le.rw.TryLock() {
@@ -197,17 +198,26 @@ func (kl *KeyLock) LockWithTimeout(key string, timeout time.Duration) bool {
kl.cancelLock(key, le) kl.cancelLock(key, le)
} }
}() }()
timer := time.NewTimer(timeout)
defer timer.Stop()
pollInterval := kl.config.InitialPollInterval pollInterval := kl.config.InitialPollInterval
maxPollInterval := kl.config.MaxPollInterval maxPollInterval := kl.config.MaxPollInterval
for { for {
select { remaining := time.Until(deadline)
case <-timer.C: if remaining <= 0 {
return false return false
default: }
time.Sleep(pollInterval) sleepInterval := pollInterval
if sleepInterval > remaining {
sleepInterval = remaining
}
time.Sleep(sleepInterval)
if time.Until(deadline) <= 0 {
return false
}
if le.rw.TryLock() { if le.rw.TryLock() {
if time.Until(deadline) <= 0 {
le.rw.Unlock()
return false
}
kl.commitLock(key, le) kl.commitLock(key, le)
acquired = true acquired = true
return true return true
@@ -217,7 +227,6 @@ func (kl *KeyLock) LockWithTimeout(key string, timeout time.Duration) bool {
pollInterval = maxPollInterval pollInterval = maxPollInterval
} }
} }
}
} }
// RLock 为指定的键获取一个读锁。 // RLock 为指定的键获取一个读锁。
@@ -244,6 +253,7 @@ func (kl *KeyLock) TryRLock(key string) bool {
// RLockWithTimeout 尝试在给定的时间内为指定的键获取读锁。 // RLockWithTimeout 尝试在给定的时间内为指定的键获取读锁。
// 如果在超时前成功获取锁,则返回 true;否则返回 false。 // 如果在超时前成功获取锁,则返回 true;否则返回 false。
func (kl *KeyLock) RLockWithTimeout(key string, timeout time.Duration) bool { func (kl *KeyLock) RLockWithTimeout(key string, timeout time.Duration) bool {
deadline := time.Now().Add(timeout)
le := kl.prepareLock(key) le := kl.prepareLock(key)
// 立即尝试一次 // 立即尝试一次
if le.rw.TryRLock() { if le.rw.TryRLock() {
@@ -256,17 +266,26 @@ func (kl *KeyLock) RLockWithTimeout(key string, timeout time.Duration) bool {
kl.cancelLock(key, le) kl.cancelLock(key, le)
} }
}() }()
timer := time.NewTimer(timeout)
defer timer.Stop()
pollInterval := kl.config.InitialPollInterval pollInterval := kl.config.InitialPollInterval
maxPollInterval := kl.config.MaxPollInterval maxPollInterval := kl.config.MaxPollInterval
for { for {
select { remaining := time.Until(deadline)
case <-timer.C: if remaining <= 0 {
return false return false
default: }
time.Sleep(pollInterval) sleepInterval := pollInterval
if sleepInterval > remaining {
sleepInterval = remaining
}
time.Sleep(sleepInterval)
if time.Until(deadline) <= 0 {
return false
}
if le.rw.TryRLock() { if le.rw.TryRLock() {
if time.Until(deadline) <= 0 {
le.rw.RUnlock()
return false
}
kl.commitLock(key, le) kl.commitLock(key, le)
acquired = true acquired = true
return true return true
@@ -276,7 +295,6 @@ func (kl *KeyLock) RLockWithTimeout(key string, timeout time.Duration) bool {
pollInterval = maxPollInterval pollInterval = maxPollInterval
} }
} }
}
} }
// Unlock 释放指定键的写锁。 // Unlock 释放指定键的写锁。
+49 -1
View File
@@ -1,4 +1,4 @@
// Copyright 2025 肖其顿 // Copyright 2025-2026 肖其顿 (XIAO QI DUN)
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@@ -250,6 +250,30 @@ func TestLockWithTimeout(t *testing.T) {
} }
} }
// TestLockWithTimeoutDoesNotAcquireAfterDeadline 测试写锁在超时后被释放时不会再被获取。
func TestLockWithTimeoutDoesNotAcquireAfterDeadline(t *testing.T) {
kl := NewWithConfig(Config{
MaxPollInterval: 50 * time.Millisecond,
InitialPollInterval: 50 * time.Millisecond,
})
key := "test_key"
released := make(chan struct{})
kl.Lock(key)
go func() {
time.Sleep(20 * time.Millisecond)
kl.Unlock(key)
close(released)
}()
acquired := kl.LockWithTimeout(key, 10*time.Millisecond)
if acquired {
kl.Unlock(key)
}
<-released
if acquired {
t.Fatal("LockWithTimeout 不应该在超时后获取锁")
}
}
// TestRLockWithTimeout 测试带超时的读锁获取功能。 // TestRLockWithTimeout 测试带超时的读锁获取功能。
func TestRLockWithTimeout(t *testing.T) { func TestRLockWithTimeout(t *testing.T) {
kl := New() kl := New()
@@ -270,6 +294,30 @@ func TestRLockWithTimeout(t *testing.T) {
} }
} }
// TestRLockWithTimeoutDoesNotAcquireAfterDeadline 测试读锁在超时后被释放时不会再被获取。
func TestRLockWithTimeoutDoesNotAcquireAfterDeadline(t *testing.T) {
kl := NewWithConfig(Config{
MaxPollInterval: 50 * time.Millisecond,
InitialPollInterval: 50 * time.Millisecond,
})
key := "test_key"
released := make(chan struct{})
kl.Lock(key)
go func() {
time.Sleep(20 * time.Millisecond)
kl.Unlock(key)
close(released)
}()
acquired := kl.RLockWithTimeout(key, 10*time.Millisecond)
if acquired {
kl.RUnlock(key)
}
<-released
if acquired {
t.Fatal("RLockWithTimeout 不应该在超时后获取锁")
}
}
// TestPanicOnUnlockOfUnlockedKey 测试对未锁定的键执行 Unlock 操作是否会引发 panic。 // TestPanicOnUnlockOfUnlockedKey 测试对未锁定的键执行 Unlock 操作是否会引发 panic。
func TestPanicOnUnlockOfUnlockedKey(t *testing.T) { func TestPanicOnUnlockOfUnlockedKey(t *testing.T) {
defer func() { defer func() {