mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-31 15:52:25 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			97 lines
		
	
	
	
		
			2.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			97 lines
		
	
	
	
		
			2.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package backoff
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"sync"
 | |
| 	"time"
 | |
| )
 | |
| 
 | |
| // Ticker holds a channel that delivers `ticks' of a clock at times reported by a BackOff.
 | |
| //
 | |
| // Ticks will continue to arrive when the previous operation is still running,
 | |
| // so operations that take a while to fail could run in quick succession.
 | |
| type Ticker struct {
 | |
| 	C        <-chan time.Time
 | |
| 	c        chan time.Time
 | |
| 	b        BackOff
 | |
| 	ctx      context.Context
 | |
| 	timer    Timer
 | |
| 	stop     chan struct{}
 | |
| 	stopOnce sync.Once
 | |
| }
 | |
| 
 | |
| // NewTicker returns a new Ticker containing a channel that will send
 | |
| // the time at times specified by the BackOff argument. Ticker is
 | |
| // guaranteed to tick at least once.  The channel is closed when Stop
 | |
| // method is called or BackOff stops. It is not safe to manipulate the
 | |
| // provided backoff policy (notably calling NextBackOff or Reset)
 | |
| // while the ticker is running.
 | |
| func NewTicker(b BackOff) *Ticker {
 | |
| 	return NewTickerWithTimer(b, &defaultTimer{})
 | |
| }
 | |
| 
 | |
| // NewTickerWithTimer returns a new Ticker with a custom timer.
 | |
| // A default timer that uses system timer is used when nil is passed.
 | |
| func NewTickerWithTimer(b BackOff, timer Timer) *Ticker {
 | |
| 	if timer == nil {
 | |
| 		timer = &defaultTimer{}
 | |
| 	}
 | |
| 	c := make(chan time.Time)
 | |
| 	t := &Ticker{
 | |
| 		C:     c,
 | |
| 		c:     c,
 | |
| 		b:     b,
 | |
| 		ctx:   getContext(b),
 | |
| 		timer: timer,
 | |
| 		stop:  make(chan struct{}),
 | |
| 	}
 | |
| 	t.b.Reset()
 | |
| 	go t.run()
 | |
| 	return t
 | |
| }
 | |
| 
 | |
| // Stop turns off a ticker. After Stop, no more ticks will be sent.
 | |
| func (t *Ticker) Stop() {
 | |
| 	t.stopOnce.Do(func() { close(t.stop) })
 | |
| }
 | |
| 
 | |
| func (t *Ticker) run() {
 | |
| 	c := t.c
 | |
| 	defer close(c)
 | |
| 
 | |
| 	// Ticker is guaranteed to tick at least once.
 | |
| 	afterC := t.send(time.Now())
 | |
| 
 | |
| 	for {
 | |
| 		if afterC == nil {
 | |
| 			return
 | |
| 		}
 | |
| 
 | |
| 		select {
 | |
| 		case tick := <-afterC:
 | |
| 			afterC = t.send(tick)
 | |
| 		case <-t.stop:
 | |
| 			t.c = nil // Prevent future ticks from being sent to the channel.
 | |
| 			return
 | |
| 		case <-t.ctx.Done():
 | |
| 			return
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (t *Ticker) send(tick time.Time) <-chan time.Time {
 | |
| 	select {
 | |
| 	case t.c <- tick:
 | |
| 	case <-t.stop:
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	next := t.b.NextBackOff()
 | |
| 	if next == Stop {
 | |
| 		t.Stop()
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	t.timer.Start(next)
 | |
| 	return t.timer.C()
 | |
| }
 |