1/*2Maddy Mail Server - Composable all-in-one email server.3Copyright © 2019-2020 Max Mazurov <fox.cpp@disroot.org>, Maddy Mail Server contributors45This program is free software: you can redistribute it and/or modify6it under the terms of the GNU General Public License as published by7the Free Software Foundation, either version 3 of the License, or8(at your option) any later version.910This program is distributed in the hope that it will be useful,11but WITHOUT ANY WARRANTY; without even the implied warranty of12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the13GNU General Public License for more details.1415You should have received a copy of the GNU General Public License16along with this program. If not, see <https://www.gnu.org/licenses/>.17*/1819package future2021import (22 "context"23 "runtime/debug"24 "sync"2526 "github.com/foxcpp/maddy/framework/log"27)2829// The Future object implements a container for (value, error) pair that "will30// be populated later" and allows multiple users to wait for it to be set.31//32// It should not be copied after first use.33type Future struct {34 mu sync.RWMutex35 set bool36 val interface{}37 err error3839 notify chan struct{}40}4142func New() *Future {43 return &Future{notify: make(chan struct{})}44}4546// Set sets the Future (value, error) pair. All currently blocked and future47// Get calls will return it.48func (f *Future) Set(val interface{}, err error) {49 if f == nil {50 panic("nil future used")51 }5253 f.mu.Lock()54 defer f.mu.Unlock()5556 if f.set {57 stack := debug.Stack()58 log.Println("Future.Set called multiple times", stack)59 log.Println("value=", val, "err=", err)60 return61 }6263 f.set = true64 f.val = val65 f.err = err6667 close(f.notify)68}6970func (f *Future) Get() (interface{}, error) {71 if f == nil {72 panic("nil future used")73 }7475 return f.GetContext(context.Background())76}7778func (f *Future) GetContext(ctx context.Context) (interface{}, error) {79 if f == nil {80 panic("nil future used")81 }8283 f.mu.RLock()84 if f.set {85 val := f.val86 err := f.err87 f.mu.RUnlock()88 return val, err89 }9091 f.mu.RUnlock()92 select {93 case <-f.notify:94 case <-ctx.Done():95 return nil, ctx.Err()96 }9798 f.mu.RLock()99 defer f.mu.RUnlock()100 if !f.set {101 panic("future: Notification received, but value is not set")102 }103104 return f.val, f.err105}