func (d *OrderedDaemon) IsStopped() bool {
	return d.stopped.IsSet()
}
IsStopped checks whether the daemon was stopped.
package daemon
import (
	"context"
	"errors"
	"sort"
	"sync"
	"golang.org/x/xerrors"
	"github.com/iotaledger/hive.go/logger"
	"github.com/iotaledger/hive.go/syncutils"
	"github.com/iotaledger/hive.go/typeutils"
)
var (
	ErrDaemonAlreadyStopped                 = errors.New("daemon was already stopped")
	ErrDuplicateBackgroundWorker            = errors.New("duplicate background worker")
	ErrExistingBackgroundWorkerStillRunning = errors.New("existing background worker is still running")
)
var defaultDaemon = New()
func GetRunningBackgroundWorkers() []string {
	return defaultDaemon.GetRunningBackgroundWorkers()
}
func BackgroundWorker(name string, handler WorkerFunc, priority ...int) error {
	return defaultDaemon.BackgroundWorker(name, handler, priority...)
}
func DebugEnabled(enabled bool) {
	defaultDaemon.DebugEnabled(enabled)
}
func Start() {
	defaultDaemon.Start()
}
func Run() {
	defaultDaemon.Run()
}
func Shutdown() {
	defaultDaemon.Shutdown()
}
func ShutdownAndWait() {
	defaultDaemon.ShutdownAndWait()
}
func IsRunning() bool {
	return defaultDaemon.IsRunning()
}
func IsStopped() bool {
	return defaultDaemon.IsStopped()
}
func ContextStopped() context.Context {
	return defaultDaemon.ContextStopped()
}
func New() *OrderedDaemon {
	stoppedCtx, stoppedCtxCancel := context.WithCancel(context.Background())
	return &OrderedDaemon{
		running:                typeutils.NewAtomicBool(),
		stopped:                typeutils.NewAtomicBool(),
		stoppedCtx:             stoppedCtx,
		stoppedCtxCancel:       stoppedCtxCancel,
		workers:                make(map[string]*worker),
		shutdownOrderWorker:    make([]string, 0),
		wgPerSameShutdownOrder: make(map[int]*sync.WaitGroup),
	}
}
type OrderedDaemon struct {
	running                *typeutils.AtomicBool
	stopped                *typeutils.AtomicBool
	stoppedCtx             context.Context
	stoppedCtxCancel       context.CancelFunc
	stopOnce               sync.Once
	workers                map[string]*worker
	shutdownOrderWorker    []string
	wgPerSameShutdownOrder map[int]*sync.WaitGroup
	lock                   syncutils.RWMutex
	logger                 *logger.Logger
}
type worker struct {
	ctx           context.Context
	ctxCancel     context.CancelFunc
	handler       WorkerFunc
	running       *typeutils.AtomicBool
	shutdownOrder int
}
func (d *OrderedDaemon) GetRunningBackgroundWorkers() []string {
	d.lock.RLock()
	defer d.lock.RUnlock()
	result := make([]string, 0)
	for _, name := range d.shutdownOrderWorker {
		if !d.workers[name].running.IsSet() {
			continue
		}
		result = append(result, name)
	}
	for i, j := 0, len(result)-1; i < j; i, j = i+1, j-1 {
		result[i], result[j] = result[j], result[i]
	}
	return result
}
func (d *OrderedDaemon) runBackgroundWorker(name string, backgroundWorker WorkerFunc) {
	worker := d.workers[name]
	shutdownOrderWaitGroup := d.wgPerSameShutdownOrder[worker.shutdownOrder]
	shutdownOrderWaitGroup.Add(1)
	worker.running.Set()
	go func() {
		if d.logger != nil {
			d.logger.Debugf("Starting Background Worker: %s ...", name)
		}
		backgroundWorker(worker.ctx)
		worker.running.UnSet()
		shutdownOrderWaitGroup.Done()
		if d.logger != nil {
			d.logger.Debugf("Stopping Background Worker: %s ... done", name)
		}
	}()
}
func (d *OrderedDaemon) BackgroundWorker(name string, handler WorkerFunc, order ...int) error {
	if d.IsStopped() {
		return ErrDaemonAlreadyStopped
	}
	d.lock.Lock()
	defer d.lock.Unlock()
	exWorker, workerExistsAlready := d.workers[name]
	if workerExistsAlready {
		if !d.running.IsSet() {
			return xerrors.Errorf("tried to overwrite existing background worker (%s): %w", name, ErrDuplicateBackgroundWorker)
		}
		if exWorker.running.IsSet() {
			return xerrors.Errorf("%w: %s is still running", ErrExistingBackgroundWorkerStillRunning, name)
		}
		for i, exName := range d.shutdownOrderWorker {
			if exName != name {
				continue
			}
			if i < len(d.shutdownOrderWorker)-1 {
				copy(d.shutdownOrderWorker[i:], d.shutdownOrderWorker[i+1:])
			}
			d.shutdownOrderWorker[len(d.shutdownOrderWorker)-1] = ""
			d.shutdownOrderWorker = d.shutdownOrderWorker[:len(d.shutdownOrderWorker)-1]
		}
	}
	var shutdownOrder int
	if len(order) > 0 && order[0] != 0 {
		shutdownOrder = order[0]
	} else {
		shutdownOrder = 0
	}
	if _, ok := d.wgPerSameShutdownOrder[shutdownOrder]; !ok {
		d.wgPerSameShutdownOrder[shutdownOrder] = &sync.WaitGroup{}
	}
	ctx, ctxCancel := context.WithCancel(context.Background())
	d.workers[name] = &worker{
		ctx:           ctx,
		ctxCancel:     ctxCancel,
		handler:       handler,
		running:       typeutils.NewAtomicBool(),
		shutdownOrder: shutdownOrder,
	}
	d.shutdownOrderWorker = append(d.shutdownOrderWorker, name)
	sort.Slice(d.shutdownOrderWorker, func(i, j int) bool {
		return d.workers[d.shutdownOrderWorker[i]].shutdownOrder > d.workers[d.shutdownOrderWorker[j]].shutdownOrder
	})
	if d.IsRunning() {
		d.runBackgroundWorker(name, handler)
	}
	return nil
}
func (d *OrderedDaemon) DebugEnabled(enabled bool) {
	if enabled {
		defaultDaemon.logger = logger.NewLogger("Daemon")
	} else {
		defaultDaemon.logger = nil
	}
}
func (d *OrderedDaemon) Start() {
	if d.IsStopped() {
		return
	}
	d.lock.Lock()
	defer d.lock.Unlock()
	if !d.IsRunning() {
		d.running.Set()
		for name, worker := range d.workers {
			d.runBackgroundWorker(name, worker.handler)
		}
	}
}
func (d *OrderedDaemon) Run() {
	d.Start()
	if wg := d.waitGroupForLastPriority(); wg != nil {
		wg.Wait()
	}
}
func (d *OrderedDaemon) waitGroupForLastPriority() *sync.WaitGroup {
	d.lock.Lock()
	defer d.lock.Unlock()
	if len(d.shutdownOrderWorker) == 0 {
		return nil
	}
	lowestShutdownPriorityWorker := d.workers[d.shutdownOrderWorker[len(d.shutdownOrderWorker)-1]]
	return d.wgPerSameShutdownOrder[lowestShutdownPriorityWorker.shutdownOrder]
}
func (d *OrderedDaemon) shutdown() {
	if d.logger != nil {
		d.logger.Debugf("Shutting down ...")
	}
	d.stopped.Set()
	d.stoppedCtxCancel()
	if !d.IsRunning() {
		return
	}
	d.stopWorkers()
	d.running.UnSet()
	d.clear()
}
func (d *OrderedDaemon) stopWorkers() {
	d.lock.RLock()
	defer d.lock.RUnlock()
	if len(d.shutdownOrderWorker) > 0 {
		prevPriority := d.workers[d.shutdownOrderWorker[0]].shutdownOrder
		for _, name := range d.shutdownOrderWorker {
			worker := d.workers[name]
			if !worker.running.IsSet() {
				worker.ctxCancel()
				continue
			}
			if worker.shutdownOrder < prevPriority {
				d.wgPerSameShutdownOrder[prevPriority].Wait()
				prevPriority = worker.shutdownOrder
			}
			if d.logger != nil {
				d.logger.Debugf("Stopping Background Worker: %s ...", name)
			}
			worker.ctxCancel()
		}
		d.wgPerSameShutdownOrder[prevPriority].Wait()
	}
}
func (d *OrderedDaemon) clear() {
	d.lock.Lock()
	defer d.lock.Unlock()
	d.workers = nil
	d.shutdownOrderWorker = nil
	d.wgPerSameShutdownOrder = nil
}
func (d *OrderedDaemon) Shutdown() {
	go d.stopOnce.Do(d.shutdown)
}
func (d *OrderedDaemon) ShutdownAndWait() {
	d.stopOnce.Do(d.shutdown)
}
func (d *OrderedDaemon) IsRunning() bool {
	return d.running.IsSet()
}