init: pristine aerc 0.20.0 source
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
package imap
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"git.sr.ht/~rjarry/aerc/lib/log"
|
||||
"git.sr.ht/~rjarry/aerc/worker/types"
|
||||
"github.com/emersion/go-imap"
|
||||
)
|
||||
|
||||
var errIdleTimeout = fmt.Errorf("idle timeout")
|
||||
|
||||
// idler manages the idle mode of the imap server. Enter idle mode if there's
|
||||
// no other task and leave idle mode when a new task arrives. Idle mode is only
|
||||
// used when the client is ready and connected. After a connection loss, make
|
||||
// sure that idling returns gracefully and the worker remains responsive.
|
||||
type idler struct {
|
||||
client *imapClient
|
||||
debouncer *time.Timer
|
||||
debounce time.Duration
|
||||
timeout time.Duration
|
||||
worker types.WorkerInteractor
|
||||
stop chan struct{}
|
||||
start chan struct{}
|
||||
done chan error
|
||||
}
|
||||
|
||||
func newIdler(cfg imapConfig, w types.WorkerInteractor, startIdler chan struct{}) *idler {
|
||||
return &idler{
|
||||
debouncer: nil,
|
||||
debounce: cfg.idle_debounce,
|
||||
timeout: cfg.idle_timeout,
|
||||
worker: w,
|
||||
stop: make(chan struct{}),
|
||||
start: startIdler,
|
||||
done: make(chan error),
|
||||
}
|
||||
}
|
||||
|
||||
func (i *idler) SetClient(c *imapClient) {
|
||||
i.client = c
|
||||
}
|
||||
|
||||
func (i *idler) ready() bool {
|
||||
return (i.client != nil && i.client.State() == imap.SelectedState)
|
||||
}
|
||||
|
||||
func (i *idler) Start() {
|
||||
if !i.ready() {
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case <-i.stop:
|
||||
// stop channel is nil (probably after a debounce), we don't
|
||||
// want to close it
|
||||
default:
|
||||
close(i.stop)
|
||||
}
|
||||
|
||||
// create new stop channel
|
||||
i.stop = make(chan struct{})
|
||||
|
||||
// clear done channel
|
||||
clearing := true
|
||||
for clearing {
|
||||
select {
|
||||
case <-i.done:
|
||||
continue
|
||||
default:
|
||||
clearing = false
|
||||
}
|
||||
}
|
||||
|
||||
i.worker.Tracef("idler (start): start idle after debounce")
|
||||
i.debouncer = time.AfterFunc(i.debounce, func() {
|
||||
i.start <- struct{}{}
|
||||
i.worker.Tracef("idler (start): started")
|
||||
})
|
||||
}
|
||||
|
||||
func (i *idler) Execute() {
|
||||
if !i.ready() {
|
||||
return
|
||||
}
|
||||
|
||||
// we need to call client.Idle in a goroutine since it is blocking call
|
||||
// and we still want to receive messages
|
||||
go func() {
|
||||
defer log.PanicHandler()
|
||||
|
||||
start := time.Now()
|
||||
err := i.client.Idle(i.stop, nil)
|
||||
if err != nil {
|
||||
i.worker.Errorf("idle returned error: %v", err)
|
||||
}
|
||||
i.worker.Tracef("idler (execute): idleing for %s", time.Since(start))
|
||||
|
||||
i.done <- err
|
||||
}()
|
||||
}
|
||||
|
||||
func (i *idler) Stop() error {
|
||||
if !i.ready() {
|
||||
return nil
|
||||
}
|
||||
|
||||
select {
|
||||
case <-i.stop:
|
||||
i.worker.Debugf("idler (stop): idler already stopped?")
|
||||
return nil
|
||||
default:
|
||||
close(i.stop)
|
||||
}
|
||||
|
||||
if i.debouncer != nil {
|
||||
if i.debouncer.Stop() {
|
||||
i.worker.Tracef("idler (stop): debounced")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
case err := <-i.done:
|
||||
i.worker.Tracef("idler (stop): idle stopped: %v", err)
|
||||
return err
|
||||
case <-time.After(i.timeout):
|
||||
i.worker.Errorf("idler (stop): cannot stop idle (timeout)")
|
||||
return errIdleTimeout
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user