// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT

// Package taskloop implements a task loop to run // tasks sequentially in a separate Goroutine.
package taskloop import ( atomicx ) // ErrClosed indicates that the loop has been stopped. var ErrClosed = errors.New("the agent is closed") type task struct { fn func(context.Context) done chan struct{} } // Loop runs submitted task serially in a dedicated Goroutine. type Loop struct { tasks chan task // State for closing done chan struct{} taskLoopDone chan struct{} err atomicx.Error } // New creates and starts a new task loop. func ( func()) *Loop { := &Loop{ tasks: make(chan task), done: make(chan struct{}), taskLoopDone: make(chan struct{}), } go .runLoop() return } // runLoop handles registered tasks and agent close. func ( *Loop) ( func()) { defer func() { () close(.taskLoopDone) }() for { select { case <-.done: return case := <-.tasks: .fn() close(.done) } } } // Close stops the loop after finishing the execution of the current task. // Other pending tasks will not be executed. func ( *Loop) () { if := .Err(); != nil { return } .err.Store(ErrClosed) close(.done) <-.taskLoopDone } // Run serially executes the submitted callback. // Blocking tasks must be cancelable by context. func ( *Loop) ( context.Context, func(context.Context)) error { if := .Err(); != nil { return } := make(chan struct{}) select { case <-.Done(): return .Err() case .tasks <- task{, }: <- return nil } } // The following methods implement context.Context for TaskLoop // Done returns a channel that's closed when the task loop has been stopped. func ( *Loop) () <-chan struct{} { return .done } // Err returns nil if the task loop is still running. // Otherwise it return errClosed if the loop has been closed/stopped. func ( *Loop) () error { select { case <-.done: return ErrClosed default: return nil } } // Deadline returns the no valid time as task loops have no deadline. func ( *Loop) () ( time.Time, bool) { return time.Time{}, false } // Value is not supported for task loops. func ( *Loop) (interface{}) interface{} { return nil }