<commit_before>package signal

import (
	"context"
	"time"
)

type ActivityUpdater interface {
	Update()
}

type ActivityTimer struct {
	updated chan bool
	timeout chan time.Duration
}

func (t *ActivityTimer) Update() {
	select {
	case t.updated <- true:
	default:
	}
}

func (t *ActivityTimer) SetTimeout(timeout time.Duration) {
	t.timeout <- timeout
}

func (t *ActivityTimer) run(ctx context.Context, cancel context.CancelFunc) {
	ticker := time.NewTicker(<-t.timeout)
	defer func() {
		ticker.Stop()
	}()

	for {
		select {
		case <-ticker.C:
		case <-ctx.Done():
			return
		case timeout := <-t.timeout:
			if timeout == 0 {
				cancel()
				return
			}

			ticker.Stop()
			ticker = time.NewTicker(timeout)
			continue
		}

		select {
		case <-t.updated:
		// Updated keep waiting.
		default:
			cancel()
			return
		}
	}
}

func CancelAfterInactivity(ctx context.Context, cancel context.CancelFunc, timeout time.Duration) *ActivityTimer {
	timer := &ActivityTimer{
		timeout: make(chan time.Duration, 1),
		updated: make(chan bool, 1),
	}
	timer.timeout <- timeout
	go timer.run(ctx, cancel)
	return timer
}
<commit_msg>fix crash on init timeout = 0<commit_after>package signal

import (
	"context"
	"time"
)

type ActivityUpdater interface {
	Update()
}

type ActivityTimer struct {
	updated chan bool
	timeout chan time.Duration
}

func (t *ActivityTimer) Update() {
	select {
	case t.updated <- true:
	default:
	}
}

func (t *ActivityTimer) SetTimeout(timeout time.Duration) {
	t.timeout <- timeout
}

func (t *ActivityTimer) run(ctx context.Context, cancel context.CancelFunc) {
	defer cancel()

	timeout := <-t.timeout
	if timeout == 0 {
		return
	}

	ticker := time.NewTicker(<-t.timeout)
	defer func() {
		ticker.Stop()
	}()

	for {
		select {
		case <-ticker.C:
		case <-ctx.Done():
			return
		case timeout := <-t.timeout:
			if timeout == 0 {
				return
			}

			ticker.Stop()
			ticker = time.NewTicker(timeout)
			continue
		}

		select {
		case <-t.updated:
		// Updated keep waiting.
		default:
			return
		}
	}
}

func CancelAfterInactivity(ctx context.Context, cancel context.CancelFunc, timeout time.Duration) *ActivityTimer {
	timer := &ActivityTimer{
		timeout: make(chan time.Duration, 1),
		updated: make(chan bool, 1),
	}
	timer.timeout <- timeout
	go timer.run(ctx, cancel)
	return timer
}

{'fraction_non_alphanumeric': 0.12919896640826872, 'fraction_numerical': 0.0034453057708871662, 'mean_word_length': 3.383018867924528, 'pattern_counts': {'":': 0, '<': 20, '<?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': '5157871', 'n_tokens_mistral': 900, 'n_tokens_neox': 798, 'n_words': 209}