1
0

fix: 桌面应用跨平台编译和单实例锁

- 使用 gofrs/flock 替代 syscall.Flock 以支持 Windows
- 引入 SingletonLock 结构体,支持锁路径参数化(测试与生产隔离)
- 对齐服务初始化流程与 cmd/server(RoutingCache、StatsBuffer)
- 添加 gofrs/flock 依赖
- 重写单例测试,覆盖加锁/解锁/重复加锁场景
- 更新 desktop-app 规范,补充跨平台锁细节
- 新增 cross-platform-singleton 规范
This commit is contained in:
2026-04-22 22:32:39 +08:00
parent 380586afa6
commit 15f08ee2ca
6 changed files with 141 additions and 52 deletions

View File

@@ -3,37 +3,56 @@ package main
import (
"os"
"path/filepath"
"syscall"
"testing"
)
func TestAcquireSingleInstance(t *testing.T) {
lockPath := filepath.Join(os.TempDir(), "nex-gateway-test.lock")
origLockFile := lockFile
lockFile = nil
defer func() { lockFile = origLockFile }()
f, err := os.OpenFile(lockPath, os.O_CREATE|os.O_RDWR, 0666)
if err != nil {
t.Fatalf("无法创建锁文件: %v", err)
}
defer f.Close()
func TestSingletonLock_FirstLockSuccess(t *testing.T) {
lockPath := filepath.Join(os.TempDir(), "nex-gateway-test-first.lock")
defer os.Remove(lockPath)
err = syscall.Flock(int(f.Fd()), syscall.LOCK_EX|syscall.LOCK_NB)
if err != nil {
t.Fatalf("无法获取文件锁: %v", err)
lock := NewSingletonLock(lockPath)
if err := lock.Lock(); err != nil {
t.Fatalf("首次加锁应成功,但返回错误: %v", err)
}
defer syscall.Flock(int(f.Fd()), syscall.LOCK_UN)
t.Log("单实例锁测试通过")
defer lock.Unlock()
}
func TestReleaseSingleInstance(t *testing.T) {
lockFile = nil
releaseSingleInstance()
t.Log("释放空锁测试通过")
func TestSingletonLock_DuplicateLockFails(t *testing.T) {
lockPath := filepath.Join(os.TempDir(), "nex-gateway-test-dup.lock")
defer os.Remove(lockPath)
lock1 := NewSingletonLock(lockPath)
if err := lock1.Lock(); err != nil {
t.Fatalf("首次加锁应成功: %v", err)
}
defer lock1.Unlock()
lock2 := NewSingletonLock(lockPath)
err := lock2.Lock()
if err == nil {
lock2.Unlock()
t.Fatal("重复加锁应失败,但返回 nil")
}
}
func TestSingletonLock_UnlockThenRelock(t *testing.T) {
lockPath := filepath.Join(os.TempDir(), "nex-gateway-test-relock.lock")
defer os.Remove(lockPath)
lock1 := NewSingletonLock(lockPath)
if err := lock1.Lock(); err != nil {
t.Fatalf("首次加锁应成功: %v", err)
}
lock1.Unlock()
lock2 := NewSingletonLock(lockPath)
if err := lock2.Lock(); err != nil {
t.Fatalf("释放后重新加锁应成功: %v", err)
}
lock2.Unlock()
}
func TestSingletonLock_UnlockWithoutLock(t *testing.T) {
lock := NewSingletonLock(filepath.Join(os.TempDir(), "nex-gateway-test-nil.lock"))
lock.Unlock()
}