返回 DeepSeek-Reasonix
session_path_identity.go
根目录 / internal / agent / session_path_identity.go
1 package agent
2
3 import (
4 "context"
5 "crypto/sha256"
6 "encoding/hex"
7 "errors"
8 "fmt"
9 "os"
10 "path/filepath"
11 "strings"
12
13 "reasonix/internal/filelock"
14 "reasonix/internal/pathidentity"
15 )
16
17 // LockSessionMetaPath serializes a branch-meta read-modify-write cycle across
18 // goroutines and processes.
19 func LockSessionMetaPath(path string) (func(), error) {
20 lockPath, localKey, err := sessionMetaLockTarget(path)
21 if err != nil {
22 return nil, err
23 }
24 ctx, cancel := context.WithTimeout(context.Background(), sessionMetaLockWait)
25 releaseFile, err := filelock.AcquireModeWithKey(ctx, lockPath, localKey, filelock.ModeExclusive)
26 cancel()
27 if err != nil {
28 return nil, fmt.Errorf("lock session metadata: %w", err)
29 }
30 return releaseFile, nil
31 }
32
33 func tryLockSessionMetaPath(path string) (func(), bool, error) {
34 lockPath, localKey, err := sessionMetaLockTarget(path)
35 if err != nil {
36 return nil, false, err
37 }
38 releaseFile, err := filelock.TryAcquireModeWithKey(lockPath, localKey, filelock.ModeExclusive)
39 if errors.Is(err, filelock.ErrHeld) {
40 return nil, false, nil
41 }
42 if err != nil {
43 return nil, false, fmt.Errorf("try lock session metadata: %w", err)
44 }
45 return releaseFile, true, nil
46 }
47
48 func sessionMetaLockTarget(path string) (string, string, error) {
49 identity, err := resolveSessionPathIdentity(path)
50 if err != nil {
51 return "", "", err
52 }
53 if err := os.MkdirAll(filepath.Dir(identity.PhysicalPath), 0o755); err != nil {
54 return "", "", fmt.Errorf("create session metadata dir: %w", err)
55 }
56 return sessionMetaLockPath(identity.PhysicalPath), identity.Key + "\x00session-meta", nil
57 }
58
59 func sessionMetaLockPath(path string) string {
60 canonical := canonicalSessionSavePath(path)
61 digest := sha256.Sum256([]byte(canonical))
62 return filepath.Join(filepath.Dir(canonical), "."+hex.EncodeToString(digest[:8])+".meta.lock")
63 }
64
65 func canonicalSessionSavePath(path string) string {
66 identity, err := resolveSessionPathIdentity(path)
67 if err != nil {
68 return ""
69 }
70 return identity.PhysicalPath
71 }
72
73 // CanonicalSessionPath returns the comparison key shared by session owners,
74 // lease registries, and save queues. Empty input never becomes the current dir.
75 func CanonicalSessionPath(path string) string {
76 if strings.TrimSpace(path) == "" {
77 return ""
78 }
79 identity, err := resolveSessionPathIdentity(path)
80 if err != nil {
81 return ""
82 }
83 return identity.Key
84 }
85
86 func resolveSessionPathIdentity(path string) (pathidentity.Identity, error) {
87 baseDir := ""
88 if !filepath.IsAbs(strings.TrimSpace(path)) {
89 var err error
90 baseDir, err = os.Getwd()
91 if err != nil {
92 return pathidentity.Identity{}, err
93 }
94 }
95 return pathidentity.Resolve(path, pathidentity.Options{BaseDir: baseDir, FollowLeaf: true})
96 }
97
97 lines GO