<commit_before>// Copyright 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package singular

import (
	"fmt"
	"sync"
	"time"

	"github.com/juju/loggo"

	"github.com/juju/juju/worker"
)

var logger = loggo.GetLogger("juju.worker.singular")

var PingInterval = 10 * time.Second

type runner struct {
	pingErr         error
	pingerDied      chan struct{}
	startPingerOnce sync.Once
	isMaster        bool
	worker.Runner
	conn Conn
}

// Conn represents a connection to some resource.
type Conn interface {
	// IsMaster reports whether this connection is currently held by
	// the (singular) master of the resource.
	IsMaster() (bool, error)

	// Ping probes the resource and returns an error if the the
	// connection has failed. If the master changes, this method
	// must return an error.
	Ping() error
}

// New returns a Runner that can be used to start workers that will only
// run a single instance. The conn value is used to determine whether to
// run the workers or not.
//
// If conn.IsMaster returns true, any workers started will be started on the
// underlying runner.
//
// If conn.IsMaster returns false, any workers started will actually
// start do-nothing placeholder workers on the underlying runner
// that continually ping the connection until a ping fails and then exit
// with that error.
func New(underlying worker.Runner, conn Conn) (worker.Runner, error) {
	isMaster, err := conn.IsMaster()
	if err != nil {
		return nil, fmt.Errorf("cannot get master status: %v", err)
	}
	logger.Infof("runner created; isMaster %v", isMaster)
	return &runner{
		isMaster:   isMaster,
		Runner:     underlying,
		conn:       conn,
		pingerDied: make(chan struct{}),
	}, nil
}

// pinger periodically pings the connection to make sure that the
// master-status has not changed. When the ping fails, it sets r.pingErr
// to the error and closes r.pingerDied to signal the other workers to
// quit.
func (r *runner) pinger() {
	underlyingDead := make(chan struct{})
	go func() {
		r.Runner.Wait()
		close(underlyingDead)
	}()
	timer := time.NewTimer(0)
	for {
		if err := r.conn.Ping(); err != nil {
			// The ping has failed: cause all other workers
			// to exit with the ping error.
			logger.Infof("pinger has died: %v", err)
			r.pingErr = err
			close(r.pingerDied)
			return
		}
		timer.Reset(PingInterval)
		select {
		case <-timer.C:
		case <-underlyingDead:
			return
		}
	}
}
<commit_msg>Fix the singular worker timer looping.<commit_after>// Copyright 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package singular

import (
	"fmt"
	"sync"
	"time"

	"github.com/juju/loggo"

	"github.com/juju/juju/worker"
)

var logger = loggo.GetLogger("juju.worker.singular")

var PingInterval = 10 * time.Second

type runner struct {
	pingErr         error
	pingerDied      chan struct{}
	startPingerOnce sync.Once
	isMaster        bool
	worker.Runner
	conn Conn
}

// Conn represents a connection to some resource.
type Conn interface {
	// IsMaster reports whether this connection is currently held by
	// the (singular) master of the resource.
	IsMaster() (bool, error)

	// Ping probes the resource and returns an error if the the
	// connection has failed. If the master changes, this method
	// must return an error.
	Ping() error
}

// New returns a Runner that can be used to start workers that will only
// run a single instance. The conn value is used to determine whether to
// run the workers or not.
//
// If conn.IsMaster returns true, any workers started will be started on the
// underlying runner.
//
// If conn.IsMaster returns false, any workers started will actually
// start do-nothing placeholder workers on the underlying runner
// that continually ping the connection until a ping fails and then exit
// with that error.
func New(underlying worker.Runner, conn Conn) (worker.Runner, error) {
	isMaster, err := conn.IsMaster()
	if err != nil {
		return nil, fmt.Errorf("cannot get master status: %v", err)
	}
	logger.Infof("runner created; isMaster %v", isMaster)
	return &runner{
		isMaster:   isMaster,
		Runner:     underlying,
		conn:       conn,
		pingerDied: make(chan struct{}),
	}, nil
}

// pinger periodically pings the connection to make sure that the
// master-status has not changed. When the ping fails, it sets r.pingErr
// to the error and closes r.pingerDied to signal the other workers to
// quit.
func (r *runner) pinger() {
	underlyingDead := make(chan struct{})
	go func() {
		r.Runner.Wait()
		close(underlyingDead)
	}()
	for timer := time.NewTimer(PingInterval); ; timer.Reset(PingInterval) {
		if err := r.conn.Ping(); err != nil {
			// The ping has failed: cause all other workers
			// to exit with the ping error.
			logger.Infof("pinger has died: %v", err)
			r.pingErr = err
			close(r.pingerDied)
			return
		}

		select {
		case <-timer.C:
		case <-underlyingDead:
			return
		}
	}
}

{'fraction_non_alphanumeric': 0.10079413561392792, 'fraction_numerical': 0.0030543677458766036, 'mean_word_length': 3.263888888888889, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5601541', 'n_tokens_mistral': 1620, 'n_tokens_neox': 1501, 'n_words': 599}