返回 DeepSeek-Reasonix
topic_state_maintenance.go
根目录 / desktop / topic_state_maintenance.go
1 package main
2
3 import (
4 "context"
5 "errors"
6 "fmt"
7 "log/slog"
8 "os"
9 "path/filepath"
10
11 filelock "reasonix/internal/identitylock"
12 "reasonix/internal/topicstate"
13 )
14
15 // withExclusiveScope keeps the authoritative topic database and its legacy
16 // mirror under one cross-process lock. The callback must use the supplied
17 // store and must not re-enter this manager for the same workspace.
18 func (m *topicStateManager) withExclusiveScope(workspaceRoot string, mutate func(context.Context, *topicstate.Store) error) error {
19 scope := m.scope(workspaceRoot)
20 scope.mu.Lock()
21 defer scope.mu.Unlock()
22 if scope.root != "" && !existingDirectory(scope.root) {
23 return fmt.Errorf("workspace root %q no longer exists", scope.root)
24 }
25 if scope.path == "" {
26 return errors.New("topic state directory is unavailable")
27 }
28 if err := os.MkdirAll(filepath.Dir(scope.path), 0o700); err != nil {
29 return err
30 }
31 lockCtx, cancelLock := context.WithTimeout(context.Background(), topicStateLockTimeout)
32 release, err := filelock.Acquire(lockCtx, scope.path+".compat.lock")
33 cancelLock()
34 if err != nil {
35 return err
36 }
37 defer release()
38 if err := m.ensureOpenAndReconcileLocked(scope); err != nil {
39 return err
40 }
41 ctx, cancelOperation := m.operationContext()
42 defer cancelOperation()
43 if mutate != nil {
44 if err := mutate(ctx, scope.store); err != nil {
45 return err
46 }
47 }
48 if err := m.mirrorIfPendingLocked(ctx, scope); err != nil {
49 slog.Warn("desktop: topic legacy mirror pending after maintenance", "scope", topicScopeKind(scope.root), "error_type", topicStateErrorType(err))
50 }
51 return nil
52 }
53
53 lines GO