diff --git a/internal/imap/backend.go b/internal/imap/backend.go index 1cfcc4b6..697f4fa7 100644 --- a/internal/imap/backend.go +++ b/internal/imap/backend.go @@ -32,6 +32,7 @@ import ( ) const ( + // NOTE: Each fetch worker has its own set of attach workers so there can up to 20*5=100 API requests at once. fetchWorkers = 20 // In how many workers to fetch message (group list on IMAP). attachWorkers = 5 // In how many workers to fetch attachments (for one message). buildWorkers = 20 // In how many workers to build messages. diff --git a/pkg/message/build.go b/pkg/message/build.go index c141cd00..a3c7e78f 100644 --- a/pkg/message/build.go +++ b/pkg/message/build.go @@ -45,6 +45,17 @@ type Fetcher interface { KeyRingForAddressID(string) (*crypto.KeyRing, error) } +// NewBuilder creates a new builder which manages the given number of fetch/attach/build workers. +// - fetchWorkers: the number of workers which fetch messages from API +// - attachWorkers: the number of workers which fetch attachments from API. +// - buildWorkers: the number of workers which decrypt/build RFC822 message literals. +// +// NOTE: Each fetch worker spawns a unique set of attachment workers! +// There can therefore be up to fetchWorkers*attachWorkers simultaneous API connections. +// +// The returned builder is ready to handle jobs -- see (*Builder).NewJob for more information. +// +// Call (*Builder).Done to shut down the builder and stop all workers. func NewBuilder(fetchWorkers, attachWorkers, buildWorkers int) *Builder { b := newBuilder() @@ -98,10 +109,13 @@ func newBuilder() *Builder { } } +// NewJob tells the builder to begin building the message with the given ID. +// The result (or any error which occurred during building) can be retrieved from the returned job when available. func (b *Builder) NewJob(ctx context.Context, api Fetcher, messageID string) *BuildJob { return b.NewJobWithOptions(ctx, api, messageID, JobOptions{}) } +// NewJobWithOptions creates a new job with custom options. See NewJob for more information. func (b *Builder) NewJobWithOptions(ctx context.Context, api Fetcher, messageID string, opts JobOptions) *BuildJob { b.locker.Lock() defer b.locker.Unlock() @@ -117,6 +131,7 @@ func (b *Builder) NewJobWithOptions(ctx context.Context, api Fetcher, messageID return b.jobs[messageID] } +// Done shuts down the builder and stops all workers. func (b *Builder) Done() { b.locker.Lock() defer b.locker.Unlock() diff --git a/pkg/message/build_build.go b/pkg/message/build_build.go index 9639c7cb..c8d386c0 100644 --- a/pkg/message/build_build.go +++ b/pkg/message/build_build.go @@ -43,6 +43,11 @@ func newBuildResFailure(messageID string, err error) buildRes { } } +// startBuildWorkers starts the given number of build workers. +// These workers decrypt and build messages into RFC822 literals. +// Two channels are returned: +// - buildReqCh: used to send work items to the worker pool +// - buildResCh: used to receive work results from the worker pool func startBuildWorkers(buildWorkers int) (chan fetchRes, chan buildRes) { buildReqCh := make(chan fetchRes) buildResCh := make(chan buildRes) diff --git a/pkg/message/build_fetch.go b/pkg/message/build_fetch.go index 7807be92..69e3e3da 100644 --- a/pkg/message/build_fetch.go +++ b/pkg/message/build_fetch.go @@ -56,6 +56,12 @@ func newFetchResFailure(req fetchReq, err error) fetchRes { } } +// startFetchWorkers starts the given number of fetch workers. +// These workers download message and attachment data from API. +// Each fetch worker will use up to the given number of attachment workers to download attachments. +// Two channels are returned: +// - fetchReqCh: used to send work items to the worker pool +// - fetchResCh: used to receive work results from the worker pool func startFetchWorkers(fetchWorkers, attachWorkers int) (chan fetchReq, chan fetchRes) { fetchReqCh := make(chan fetchReq) fetchResCh := make(chan fetchRes) diff --git a/pkg/message/build_job.go b/pkg/message/build_job.go index 15cd74f2..b800afa7 100644 --- a/pkg/message/build_job.go +++ b/pkg/message/build_job.go @@ -41,6 +41,8 @@ func newBuildJob(messageID string) *BuildJob { } } +// GetResult returns the build result or any error which occurred during building. +// If the result is not ready yet, it blocks. func (job *BuildJob) GetResult() ([]byte, error) { <-job.done return job.literal, job.err diff --git a/pkg/message/build_rfc822_custom.go b/pkg/message/build_rfc822_custom.go index bcedf5e8..477f0e27 100644 --- a/pkg/message/build_rfc822_custom.go +++ b/pkg/message/build_rfc822_custom.go @@ -27,6 +27,7 @@ import ( "github.com/emersion/go-message" ) +// writeCustomTextPart writes an armored-PGP text part for a message body that couldn't be decrypted. func writeCustomTextPart( w *message.Writer, msg *pmapi.Message, @@ -61,6 +62,7 @@ func writeCustomTextPart( return nil } +// writeCustomTextPart writes an armored-PGP data part for an attachment that couldn't be decrypted. func writeCustomAttachmentPart( w *message.Writer, att *pmapi.Attachment,