/*
Copyright 2019 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package util

import (
	"fmt"
	"sync"

	"k8s.io/klog/v2"
)

// Stoppable implements the standard stop / shutdown logic
type Stoppable struct {
	// mutex is used to enforce only a single call to Stop is active.
	// Needed because we allow stopping through an http endpoint and
	// allowing concurrent stoppers leads to stack traces.
	// We also use it for lazy-init
	mutex       sync.Mutex
	shutdown    bool
	stopChannel chan struct{}
}

// StopChannel gets the stopChannel, initializing it if needed
func (s *Stoppable) StopChannel() <-chan struct{} {
	s.mutex.Lock()
	defer s.mutex.Unlock()

	if s.stopChannel == nil {
		s.stopChannel = make(chan struct{})
	}
	return s.stopChannel
}

// Stop stops the controller.
func (s *Stoppable) Stop() error {
	s.mutex.Lock()
	defer s.mutex.Unlock()

	if s.shutdown {
		return fmt.Errorf("shutdown already in progress")
	}

	// We initialize the channel to avoid a race if we Stop before anyone is watching
	if s.stopChannel == nil {
		s.stopChannel = make(chan struct{})
	}
	close(s.stopChannel)
	klog.Infof("shutting down controller")
	s.shutdown = true

	return nil
}

func (s *Stoppable) StopRequested() bool {
	return s.shutdown
}
