package syncengine import ( "errors" "sync" ) var ErrAlreadyRunning = errors.New("job already running for this sync pair") type Queue struct { mu sync.Mutex runs map[int64]*RunInfo } type RunInfo struct { JobID int64 SyncPairID int64 Cancel func() CancelledBy bool } func NewQueue() *Queue { return &Queue{runs: make(map[int64]*RunInfo)} } func (q *Queue) Enqueue(syncPairID, jobID int64, cancelFn func()) error { q.mu.Lock() defer q.mu.Unlock() if _, exists := q.runs[jobID]; exists { return ErrAlreadyRunning } for _, info := range q.runs { if info.SyncPairID == syncPairID { return ErrAlreadyRunning } } q.runs[jobID] = &RunInfo{JobID: jobID, SyncPairID: syncPairID, Cancel: cancelFn} return nil } func (q *Queue) Dequeue(jobID int64) { q.mu.Lock() defer q.mu.Unlock() delete(q.runs, jobID) } func (q *Queue) IsRunning(jobID int64) bool { q.mu.Lock() defer q.mu.Unlock() _, exists := q.runs[jobID] return exists } func (q *Queue) GetByPair(syncPairID int64) (jobID int64, exists bool) { q.mu.Lock() defer q.mu.Unlock() for _, info := range q.runs { if info.SyncPairID == syncPairID { return info.JobID, true } } return 0, false } func (q *Queue) Cancel(jobID int64, byUser bool) bool { q.mu.Lock() defer q.mu.Unlock() if info, exists := q.runs[jobID]; exists && info.Cancel != nil { info.CancelledBy = byUser info.Cancel() return true } return false } func (q *Queue) IsCancelledByUser(jobID int64) bool { q.mu.Lock() defer q.mu.Unlock() info, exists := q.runs[jobID] return exists && info.CancelledBy } func (q *Queue) RunningJobs() []int64 { q.mu.Lock() defer q.mu.Unlock() ids := make([]int64, 0, len(q.runs)) for id := range q.runs { ids = append(ids, id) } return ids } func (q *Queue) Len() int { q.mu.Lock() defer q.mu.Unlock() return len(q.runs) }