package maildir import ( "context" "runtime" "sync" "git.sr.ht/~rjarry/aerc/lib/log" "git.sr.ht/~rjarry/aerc/models" "git.sr.ht/~rjarry/aerc/worker/lib" "git.sr.ht/~rjarry/aerc/worker/types" ) func (w *Worker) search(ctx context.Context, criteria *types.SearchCriteria) ([]models.UID, error) { criteria.PrepareHeader() requiredParts := lib.GetRequiredParts(criteria) w.worker.Debugf("Required parts bitmask for search: %b", requiredParts) keys, err := w.c.UIDs(*w.selected) if err != nil { return nil, err } var matchedUids []models.UID mu := sync.Mutex{} wg := sync.WaitGroup{} // Hard limit at 2x CPU cores max := runtime.NumCPU() * 2 limit := make(chan struct{}, max) for _, key := range keys { select { case <-ctx.Done(): return nil, context.Canceled default: limit <- struct{}{} wg.Add(1) go func(key models.UID) { defer log.PanicHandler() defer wg.Done() success, err := w.searchKey(key, criteria, requiredParts) if err != nil { // don't return early so that we can still get some results w.worker.Errorf("Failed to search key %d: %v", key, err) } else if success { mu.Lock() matchedUids = append(matchedUids, key) mu.Unlock() } <-limit }(key) } } wg.Wait() return matchedUids, nil } // Execute the search criteria for the given key, returns true if search succeeded func (w *Worker) searchKey(key models.UID, criteria *types.SearchCriteria, parts lib.MsgParts, ) (bool, error) { message, err := w.c.Message(*w.selected, key) if err != nil { return false, err } return lib.SearchMessage(message, criteria, parts) }