maddy

Fork https://github.com/foxcpp/maddy

git clone git://git.lin.moe/go/maddy.git

  1/*
  2Maddy Mail Server - Composable all-in-one email server.
  3Copyright © 2019-2020 Max Mazurov <fox.cpp@disroot.org>, Maddy Mail Server contributors
  4
  5This program is free software: you can redistribute it and/or modify
  6it under the terms of the GNU General Public License as published by
  7the Free Software Foundation, either version 3 of the License, or
  8(at your option) any later version.
  9
 10This program is distributed in the hope that it will be useful,
 11but WITHOUT ANY WARRANTY; without even the implied warranty of
 12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 13GNU General Public License for more details.
 14
 15You should have received a copy of the GNU General Public License
 16along with this program.  If not, see <https://www.gnu.org/licenses/>.
 17*/
 18
 19package future
 20
 21import (
 22	"context"
 23	"runtime/debug"
 24	"sync"
 25
 26	"github.com/foxcpp/maddy/framework/log"
 27)
 28
 29// The Future object implements a container for (value, error) pair that "will
 30// 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.RWMutex
 35	set bool
 36	val interface{}
 37	err error
 38
 39	notify chan struct{}
 40}
 41
 42func New() *Future {
 43	return &Future{notify: make(chan struct{})}
 44}
 45
 46// Set sets the Future (value, error) pair. All currently blocked and future
 47// Get calls will return it.
 48func (f *Future) Set(val interface{}, err error) {
 49	if f == nil {
 50		panic("nil future used")
 51	}
 52
 53	f.mu.Lock()
 54	defer f.mu.Unlock()
 55
 56	if f.set {
 57		stack := debug.Stack()
 58		log.Println("Future.Set called multiple times", stack)
 59		log.Println("value=", val, "err=", err)
 60		return
 61	}
 62
 63	f.set = true
 64	f.val = val
 65	f.err = err
 66
 67	close(f.notify)
 68}
 69
 70func (f *Future) Get() (interface{}, error) {
 71	if f == nil {
 72		panic("nil future used")
 73	}
 74
 75	return f.GetContext(context.Background())
 76}
 77
 78func (f *Future) GetContext(ctx context.Context) (interface{}, error) {
 79	if f == nil {
 80		panic("nil future used")
 81	}
 82
 83	f.mu.RLock()
 84	if f.set {
 85		val := f.val
 86		err := f.err
 87		f.mu.RUnlock()
 88		return val, err
 89	}
 90
 91	f.mu.RUnlock()
 92	select {
 93	case <-f.notify:
 94	case <-ctx.Done():
 95		return nil, ctx.Err()
 96	}
 97
 98	f.mu.RLock()
 99	defer f.mu.RUnlock()
100	if !f.set {
101		panic("future: Notification received, but value is not set")
102	}
103
104	return f.val, f.err
105}