2018-02-02 00:42:03 +01:00
|
|
|
package types
|
|
|
|
|
|
|
|
import (
|
2022-09-28 05:43:53 +02:00
|
|
|
"container/list"
|
2022-09-25 21:38:45 +02:00
|
|
|
"sync"
|
2019-05-19 11:50:19 +02:00
|
|
|
"sync/atomic"
|
2022-07-19 22:31:51 +02:00
|
|
|
|
2022-10-06 18:46:41 +02:00
|
|
|
"git.sr.ht/~rjarry/aerc/lib/ui"
|
2022-07-19 22:31:51 +02:00
|
|
|
"git.sr.ht/~rjarry/aerc/logging"
|
2022-09-21 18:44:12 +02:00
|
|
|
"git.sr.ht/~rjarry/aerc/models"
|
2018-02-02 00:42:03 +01:00
|
|
|
)
|
|
|
|
|
2019-05-19 11:50:19 +02:00
|
|
|
var lastId int64 = 1 // access via atomic
|
2019-05-16 01:41:21 +02:00
|
|
|
|
2018-02-02 00:42:03 +01:00
|
|
|
type Backend interface {
|
|
|
|
Run()
|
|
|
|
}
|
|
|
|
|
|
|
|
type Worker struct {
|
2022-10-06 18:46:41 +02:00
|
|
|
Backend Backend
|
|
|
|
Actions chan WorkerMessage
|
|
|
|
Name string
|
2019-04-27 17:56:38 +02:00
|
|
|
|
2019-05-19 11:50:21 +02:00
|
|
|
actionCallbacks map[int64]func(msg WorkerMessage)
|
|
|
|
messageCallbacks map[int64]func(msg WorkerMessage)
|
2022-09-28 05:43:53 +02:00
|
|
|
actionQueue *list.List
|
|
|
|
status int32
|
2022-09-25 21:38:45 +02:00
|
|
|
|
|
|
|
sync.Mutex
|
2019-04-27 17:56:38 +02:00
|
|
|
}
|
|
|
|
|
2022-10-06 18:46:41 +02:00
|
|
|
func NewWorker(name string) *Worker {
|
2019-04-27 17:56:38 +02:00
|
|
|
return &Worker{
|
2022-09-28 05:43:53 +02:00
|
|
|
Actions: make(chan WorkerMessage),
|
2022-10-06 18:46:41 +02:00
|
|
|
Name: name,
|
2019-05-19 11:50:21 +02:00
|
|
|
actionCallbacks: make(map[int64]func(msg WorkerMessage)),
|
|
|
|
messageCallbacks: make(map[int64]func(msg WorkerMessage)),
|
2022-09-28 05:43:53 +02:00
|
|
|
actionQueue: list.New(),
|
2019-04-27 17:56:38 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-19 11:50:17 +02:00
|
|
|
func (worker *Worker) setId(msg WorkerMessage) {
|
2019-05-19 11:50:19 +02:00
|
|
|
id := atomic.AddInt64(&lastId, 1)
|
|
|
|
msg.setId(id)
|
2019-05-19 11:50:17 +02:00
|
|
|
}
|
|
|
|
|
2022-09-28 05:43:53 +02:00
|
|
|
const (
|
|
|
|
idle int32 = iota
|
|
|
|
busy
|
|
|
|
)
|
|
|
|
|
|
|
|
// Add a new task to the action queue without blocking. Start processing the
|
|
|
|
// queue in the background if needed.
|
|
|
|
func (worker *Worker) queue(msg WorkerMessage) {
|
|
|
|
worker.Lock()
|
|
|
|
defer worker.Unlock()
|
|
|
|
worker.actionQueue.PushBack(msg)
|
|
|
|
if atomic.LoadInt32(&worker.status) == idle {
|
|
|
|
atomic.StoreInt32(&worker.status, busy)
|
|
|
|
go worker.processQueue()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Start processing the action queue and write all messages to the Actions
|
|
|
|
// channel, one by one. Stop when the action queue is empty.
|
|
|
|
func (worker *Worker) processQueue() {
|
|
|
|
for {
|
|
|
|
worker.Lock()
|
|
|
|
e := worker.actionQueue.Front()
|
|
|
|
if e == nil {
|
|
|
|
atomic.StoreInt32(&worker.status, idle)
|
|
|
|
worker.Unlock()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
msg := worker.actionQueue.Remove(e).(WorkerMessage)
|
|
|
|
worker.Unlock()
|
|
|
|
worker.Actions <- msg
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-16 21:41:05 +02:00
|
|
|
// PostAction posts an action to the worker. This method should not be called
|
|
|
|
// from the same goroutine that the worker runs in or deadlocks may occur
|
2022-07-19 22:31:51 +02:00
|
|
|
func (worker *Worker) PostAction(msg WorkerMessage, cb func(msg WorkerMessage)) {
|
2019-05-19 11:50:17 +02:00
|
|
|
worker.setId(msg)
|
|
|
|
|
2018-02-02 00:59:13 +01:00
|
|
|
if resp := msg.InResponseTo(); resp != nil {
|
2022-07-19 22:31:51 +02:00
|
|
|
logging.Debugf("PostAction %T:%T", msg, resp)
|
2018-02-02 00:59:13 +01:00
|
|
|
} else {
|
2022-07-19 22:31:51 +02:00
|
|
|
logging.Debugf("PostAction %T", msg)
|
2018-02-02 00:59:13 +01:00
|
|
|
}
|
2022-09-28 05:43:53 +02:00
|
|
|
// write to Actions channel without blocking
|
|
|
|
worker.queue(msg)
|
2018-02-02 00:42:03 +01:00
|
|
|
|
2019-05-19 11:50:21 +02:00
|
|
|
if cb != nil {
|
2022-09-25 21:38:45 +02:00
|
|
|
worker.Lock()
|
2019-05-19 11:50:21 +02:00
|
|
|
worker.actionCallbacks[msg.getId()] = cb
|
2022-09-25 21:38:45 +02:00
|
|
|
worker.Unlock()
|
2019-05-19 11:50:21 +02:00
|
|
|
}
|
2018-02-02 00:42:03 +01:00
|
|
|
}
|
|
|
|
|
2022-09-16 21:41:05 +02:00
|
|
|
// PostMessage posts an message to the UI. This method should not be called
|
|
|
|
// from the same goroutine that the UI runs in or deadlocks may occur
|
2018-02-02 00:42:03 +01:00
|
|
|
func (worker *Worker) PostMessage(msg WorkerMessage,
|
2022-07-31 22:16:40 +02:00
|
|
|
cb func(msg WorkerMessage),
|
|
|
|
) {
|
2019-05-19 11:50:17 +02:00
|
|
|
worker.setId(msg)
|
2022-10-06 18:46:41 +02:00
|
|
|
msg.setAccount(worker.Name)
|
2019-05-19 11:50:17 +02:00
|
|
|
|
2018-02-02 00:59:13 +01:00
|
|
|
if resp := msg.InResponseTo(); resp != nil {
|
2022-07-19 22:31:51 +02:00
|
|
|
logging.Debugf("PostMessage %T:%T", msg, resp)
|
2018-02-02 00:59:13 +01:00
|
|
|
} else {
|
2022-07-19 22:31:51 +02:00
|
|
|
logging.Debugf("PostMessage %T", msg)
|
2018-02-02 00:59:13 +01:00
|
|
|
}
|
2022-10-06 18:46:41 +02:00
|
|
|
ui.MsgChannel <- msg
|
2018-02-02 00:42:03 +01:00
|
|
|
|
2019-05-19 11:50:21 +02:00
|
|
|
if cb != nil {
|
2022-09-25 21:38:45 +02:00
|
|
|
worker.Lock()
|
2019-05-19 11:50:21 +02:00
|
|
|
worker.messageCallbacks[msg.getId()] = cb
|
2022-09-25 21:38:45 +02:00
|
|
|
worker.Unlock()
|
2019-05-19 11:50:21 +02:00
|
|
|
}
|
2018-02-02 00:42:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (worker *Worker) ProcessMessage(msg WorkerMessage) WorkerMessage {
|
2018-02-02 00:59:13 +01:00
|
|
|
if resp := msg.InResponseTo(); resp != nil {
|
2022-07-19 22:31:51 +02:00
|
|
|
logging.Debugf("ProcessMessage %T(%d):%T(%d)", msg, msg.getId(), resp, resp.getId())
|
2018-02-02 00:59:13 +01:00
|
|
|
} else {
|
2022-07-19 22:31:51 +02:00
|
|
|
logging.Debugf("ProcessMessage %T(%d)", msg, msg.getId())
|
2018-02-02 00:59:13 +01:00
|
|
|
}
|
2019-05-19 11:50:21 +02:00
|
|
|
if inResponseTo := msg.InResponseTo(); inResponseTo != nil {
|
2022-09-25 21:38:45 +02:00
|
|
|
worker.Lock()
|
2022-09-29 14:02:58 +02:00
|
|
|
f, ok := worker.actionCallbacks[inResponseTo.getId()]
|
|
|
|
worker.Unlock()
|
|
|
|
if ok {
|
2019-05-19 11:50:21 +02:00
|
|
|
f(msg)
|
2019-06-02 19:20:02 +02:00
|
|
|
if _, ok := msg.(*Done); ok {
|
2022-09-29 14:02:58 +02:00
|
|
|
worker.Lock()
|
2019-06-02 19:20:02 +02:00
|
|
|
delete(worker.actionCallbacks, inResponseTo.getId())
|
2022-09-29 14:02:58 +02:00
|
|
|
worker.Unlock()
|
2019-06-02 19:20:02 +02:00
|
|
|
}
|
2019-05-19 11:50:21 +02:00
|
|
|
}
|
2018-02-02 00:42:03 +01:00
|
|
|
}
|
|
|
|
return msg
|
|
|
|
}
|
|
|
|
|
|
|
|
func (worker *Worker) ProcessAction(msg WorkerMessage) WorkerMessage {
|
2018-02-02 00:59:13 +01:00
|
|
|
if resp := msg.InResponseTo(); resp != nil {
|
2022-07-19 22:31:51 +02:00
|
|
|
logging.Debugf("ProcessAction %T(%d):%T(%d)", msg, msg.getId(), resp, resp.getId())
|
2018-02-02 00:59:13 +01:00
|
|
|
} else {
|
2022-07-19 22:31:51 +02:00
|
|
|
logging.Debugf("ProcessAction %T(%d)", msg, msg.getId())
|
2018-02-02 00:59:13 +01:00
|
|
|
}
|
2019-05-19 11:50:21 +02:00
|
|
|
if inResponseTo := msg.InResponseTo(); inResponseTo != nil {
|
2022-09-25 21:38:45 +02:00
|
|
|
worker.Lock()
|
2022-09-29 14:02:58 +02:00
|
|
|
f, ok := worker.messageCallbacks[inResponseTo.getId()]
|
|
|
|
worker.Unlock()
|
|
|
|
if ok {
|
2019-05-19 11:50:21 +02:00
|
|
|
f(msg)
|
2019-06-02 19:20:02 +02:00
|
|
|
if _, ok := msg.(*Done); ok {
|
2022-09-29 14:02:58 +02:00
|
|
|
worker.Lock()
|
2019-06-02 19:20:02 +02:00
|
|
|
delete(worker.messageCallbacks, inResponseTo.getId())
|
2022-09-29 14:02:58 +02:00
|
|
|
worker.Unlock()
|
2019-06-02 19:20:02 +02:00
|
|
|
}
|
2019-05-19 11:50:21 +02:00
|
|
|
}
|
2018-02-02 00:42:03 +01:00
|
|
|
}
|
|
|
|
return msg
|
|
|
|
}
|
2022-09-21 18:44:12 +02:00
|
|
|
|
|
|
|
// PostMessageInfoError posts a MessageInfo message to the worker when an
|
|
|
|
// error was encountered fetching the message header
|
|
|
|
func (worker *Worker) PostMessageInfoError(msg WorkerMessage, uid uint32, err error) {
|
|
|
|
worker.PostMessage(&MessageInfo{
|
|
|
|
Info: &models.MessageInfo{
|
|
|
|
Envelope: &models.Envelope{},
|
|
|
|
Flags: []models.Flag{models.SeenFlag},
|
|
|
|
Uid: uid,
|
|
|
|
Error: err,
|
|
|
|
},
|
|
|
|
Message: RespondTo(msg),
|
|
|
|
}, nil)
|
|
|
|
}
|