Files
cc-connect/cmd/cc-connect/instance_lock_windows.go
cg33andClaude Opus 4.6 bc0098b18b feat(core): add single-instance guard to prevent duplicate processes (#440)
* feat(core): add single-instance guard to prevent duplicate processes

When two cc-connect instances run with the same config, both poll
the same connections and process messages, causing duplicate replies.

Changes:
- Add instance_lock.go with flock-based exclusive locking (Unix)
- Add instance_lock_windows.go stub for Windows compatibility
- Lock file is placed in config directory as .<config>.lock
- Add --force flag to kill existing instance before starting
- Write PID to lock file for better error messages

Fixes #439

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(lint): handle errcheck errors in instance_lock.go

Explicitly ignore return values from Truncate, Seek, Flock, Remove,
and WriteFile calls where the errors are non-critical (best-effort
cleanup and diagnostics). Fixes CI lint failure on PR #440.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-09 16:50:51 +08:00

52 lines
1.2 KiB
Go

//go:build windows
package main
import (
"fmt"
"os"
"path/filepath"
)
// InstanceLock is a no-op on Windows for now.
// TODO: implement proper Windows locking using CreateFile with exclusive mode.
type InstanceLock struct {
path string
}
// AcquireInstanceLock attempts to acquire an exclusive lock for the given config path.
// On Windows, this currently always succeeds (no-op).
func AcquireInstanceLock(configPath string) (*InstanceLock, error) {
configDir := filepath.Dir(configPath)
configBase := filepath.Base(configPath)
lockName := fmt.Sprintf(".%s.lock", configBase)
lockPath := filepath.Join(configDir, lockName)
// Write our PID to the lock file for diagnostics
pid := os.Getpid()
// Non-fatal on Windows
_ = os.WriteFile(lockPath, []byte(fmt.Sprintf("%d\n", pid)), 0644)
return &InstanceLock{path: lockPath}, nil
}
// Release releases the instance lock.
func (l *InstanceLock) Release() {
if l == nil {
return
}
// Remove lock file
if l.path != "" {
_ = os.Remove(l.path)
}
}
// Path returns the path to the lock file.
func (l *InstanceLock) Path() string {
return l.path
}
// KillExistingInstance is not implemented on Windows.
func KillExistingInstance(configPath string) bool {
return false
}