返回 DeepSeek-Reasonix
identitylock.go
根目录 / internal / identitylock / identitylock.go
1 // Package identitylock combines filesystem path identity with filelock's
2 // process-local queue and cross-process advisory lock.
3 package identitylock
4
5 import (
6 "context"
7 "fmt"
8 "os"
9 "path/filepath"
10 "strings"
11 "time"
12
13 "reasonix/internal/filelock"
14 "reasonix/internal/pathidentity"
15 )
16
17 type Mode = filelock.Mode
18
19 const (
20 ModeExclusive = filelock.ModeExclusive
21 ModeShared = filelock.ModeShared
22 )
23
24 var ErrHeld = filelock.ErrHeld
25
26 var identityLockAfterResolve = func() {}
27
28 func Acquire(ctx context.Context, path string) (func(), error) {
29 return AcquireMode(ctx, path, ModeExclusive)
30 }
31
32 func AcquireMode(ctx context.Context, path string, mode Mode) (func(), error) {
33 accessPath, key, err := resolve(path)
34 if err != nil {
35 return nil, err
36 }
37 identityLockAfterResolve()
38 release, err := filelock.AcquireModeWithKey(ctx, accessPath, key, mode)
39 return revalidate(accessPath, key, release, err)
40 }
41
42 func AcquireWithExternalTimeout(ctx context.Context, path string, timeout time.Duration) (func(), error) {
43 accessPath, key, err := resolve(path)
44 if err != nil {
45 return nil, err
46 }
47 identityLockAfterResolve()
48 release, err := filelock.AcquireWithExternalTimeoutAndKey(ctx, accessPath, key, timeout)
49 return revalidate(accessPath, key, release, err)
50 }
51
52 func TryAcquire(path string) (func(), error) {
53 return TryAcquireMode(path, ModeExclusive)
54 }
55
56 func TryAcquireMode(path string, mode Mode) (func(), error) {
57 accessPath, key, err := resolve(path)
58 if err != nil {
59 return nil, err
60 }
61 identityLockAfterResolve()
62 release, err := filelock.TryAcquireModeWithKey(accessPath, key, mode)
63 return revalidate(accessPath, key, release, err)
64 }
65
66 func revalidate(accessPath, expectedKey string, release func(), acquireErr error) (func(), error) {
67 if acquireErr != nil {
68 return nil, acquireErr
69 }
70 _, actualKey, err := resolve(accessPath)
71 if err != nil {
72 release()
73 return nil, fmt.Errorf("revalidate file lock identity: %w", err)
74 }
75 if actualKey != expectedKey {
76 release()
77 return nil, fmt.Errorf("file lock identity changed while acquiring")
78 }
79 return release, nil
80 }
81
82 func resolve(path string) (string, string, error) {
83 baseDir := ""
84 if !filepath.IsAbs(strings.TrimSpace(path)) {
85 var err error
86 baseDir, err = os.Getwd()
87 if err != nil {
88 return "", "", fmt.Errorf("resolve file lock identity: %w", err)
89 }
90 }
91 identity, err := pathidentity.Resolve(path, pathidentity.Options{BaseDir: baseDir, FollowLeaf: true})
92 if err != nil {
93 return "", "", fmt.Errorf("resolve file lock identity: %w", err)
94 }
95 return identity.AccessPath, identity.Key, nil
96 }
97
97 lines GO