<commit_before>package mpb

import (
	"bytes"
	"container/heap"
	"context"
	"fmt"
	"io"
	"math"
	"os"
	"sync"
	"time"

	"github.com/vbauerster/mpb/v8/cwriter"
)

const (
	prr = 150 * time.Millisecond // default RefreshRate
)

// DoneError represents an error when `*mpb.Progress` is done but its functionality is requested.
var DoneError = fmt.Errorf("%T instance can't be reused after it's done!", (*Progress)(nil))

// Progress represents a container that renders one or more progress bars.
type Progress struct {
	ctx          context.Context
	uwg          *sync.WaitGroup
	cwg          *sync.WaitGroup
	bwg          *sync.WaitGroup
	operateState chan func(*pState)
	interceptIo  chan func(io.Writer)
	done         chan struct{}
	once         sync.Once
	cancel       func()
}

// pState holds bars in its priorityQueue, it gets passed to (*Progress).serve monitor goroutine.
type pState struct {
	bHeap       priorityQueue
	heapUpdated bool
	pMatrix     map[int][]chan int
	aMatrix     map[int][]chan int

	// for reuse purposes
	rows []io.Reader
	pool []*Bar

	// following are provided/overrided by user
	rr                 time.Duration
	idCount            int
	reqWidth           int
	popPriority        int
	popCompleted       bool
	outputDiscarded    bool
	disableAutoRefresh bool
	manualRefresh      chan interface{}
	renderDelay        <-chan struct{}
	shutdownNotifier   chan struct{}
	queueBars          map[*Bar]*Bar
	output             io.Writer
	debugOut           io.Writer
	uwg                *sync.WaitGroup
}

// New creates new Progress container instance. It's not possible to
// reuse instance after (*Progress).Wait method has been called.
func New(options ...ContainerOption) *Progress {
	return NewWithContext(context.Background(), options...)
}

// NewWithContext creates new Progress container instance with provided
// context. It's not possible to reuse instance after (*Progress).Wait
// method has been called.
func NewWithContext(ctx context.Context, options ...ContainerOption) *Progress {
	ctx, cancel := context.WithCancel(ctx)
	s := &pState{
		rr:            prr,
		bHeap:         priorityQueue{},
		rows:          make([]io.Reader, 0, 64),
		pool:          make([]*Bar, 0, 64),
		manualRefresh: make(chan interface{}),
		queueBars:     make(map[*Bar]*Bar),
		popPriority:   math.MinInt32,
		output:        os.Stdout,
		debugOut:      io.Discard,
	}

	for _, opt := range options {
		if opt != nil {
			opt(s)
		}
	}

	if s.shutdownNotifier == nil {
		s.shutdownNotifier = make(chan struct{})
	}

	p := &Progress{
		ctx:          ctx,
		uwg:          s.uwg,
		cwg:          new(sync.WaitGroup),
		bwg:          new(sync.WaitGroup),
		operateState: make(chan func(*pState)),
		interceptIo:  make(chan func(io.Writer)),
		done:         make(chan struct{}),
		cancel:       cancel,
	}

	p.cwg.Add(1)
	go p.serve(s, cwriter.New(s.output))
	return p
}

// AddBar creates a bar with default bar filler.
func (p *Progress) AddBar(total int64, options ...BarOption) *Bar {
	return p.New(total, BarStyle(), options...)
}

// AddSpinner creates a bar with default spinner filler.
func (p *Progress) AddSpinner(total int64, options ...BarOption) *Bar {
	return p.New(total, SpinnerStyle(), options...)
}

// New creates a bar by calling `Build` method on provided `BarFillerBuilder`.
func (p *Progress) New(total int64, builder BarFillerBuilder, options ...BarOption) *Bar {
	return p.AddFiller(total, builder.Build(), options...)
}

// AddFiller creates a bar which renders itself by provided filler.
// If `total <= 0` triggering complete event by increment methods is disabled.
// Panics if *Progress instance is done, i.e. called after (*Progress).Wait().
func (p *Progress) AddFiller(total int64, filler BarFiller, options ...BarOption) *Bar {
	if filler == nil {
		filler = NopStyle().Build()
	}
	p.bwg.Add(1)
	result := make(chan *Bar)
	select {
	case p.operateState <- func(ps *pState) {
		bs := ps.makeBarState(total, filler, options...)
		bar := newBar(p, bs)
		if bs.wait.bar != nil {
			ps.queueBars[bs.wait.bar] = bar
		} else {
			heap.Push(&ps.bHeap, bar)
			ps.heapUpdated = true
		}
		ps.idCount++
		result <- bar
	}:
		bar := <-result
		return bar
	case <-p.done:
		p.bwg.Done()
		panic(DoneError)
	}
}

func (p *Progress) traverseBars(cb func(b *Bar) bool) {
	sync := make(chan struct{})
	select {
	case p.operateState <- func(s *pState) {
		for i := 0; i < s.bHeap.Len(); i++ {
			bar := s.bHeap[i]
			if !cb(bar) {
				break
			}
		}
		close(sync)
	}:
		<-sync
	case <-p.done:
	}
}

// UpdateBarPriority same as *Bar.SetPriority(int).
func (p *Progress) UpdateBarPriority(b *Bar, priority int) {
	select {
	case p.operateState <- func(s *pState) {
		if b.index < 0 {
			return
		}
		b.priority = priority
		heap.Fix(&s.bHeap, b.index)
	}:
	case <-p.done:
	}
}

// BarCount returns bars count.
func (p *Progress) BarCount() int {
	result := make(chan int)
	select {
	case p.operateState <- func(s *pState) { result <- s.bHeap.Len() }:
		return <-result
	case <-p.done:
		return 0
	}
}

// Write is implementation of io.Writer.
// Writing to `*mpb.Progress` will print lines above a running bar.
// Writes aren't flushed immediately, but at next refresh cycle.
// If Write is called after `*mpb.Progress` is done, `mpb.DoneError`
// is returned.
func (p *Progress) Write(b []byte) (int, error) {
	type result struct {
		n   int
		err error
	}
	ch := make(chan *result)
	select {
	case p.interceptIo <- func(w io.Writer) {
		n, err := w.Write(b)
		ch <- &result{n, err}
	}:
		res := <-ch
		return res.n, res.err
	case <-p.done:
		return 0, DoneError
	}
}

// Wait waits for all bars to complete and finally shutdowns container. After
// this method has been called, there is no way to reuse (*Progress) instance.
func (p *Progress) Wait() {
	// wait for user wg, if any
	if p.uwg != nil {
		p.uwg.Wait()
	}

	// wait for bars to quit, if any
	p.bwg.Wait()
	// shutdown
	p.once.Do(p.shutdown)
	// wait for container to quit
	p.cwg.Wait()
}

// Shutdown cancels any running bar immediately and then shutdowns (*Progress)
// instance. Normally this method shouldn't be called unless you know what you
// are doing. Proper way to shutdown is to call (*Progress).Wait() instead.
func (p *Progress) Shutdown() {
	p.cancel()
	p.bwg.Wait()
	p.once.Do(p.shutdown)
	p.cwg.Wait()
}

func (p *Progress) shutdown() {
	close(p.done)
}

func (p *Progress) serve(s *pState, cw *cwriter.Writer) {
	defer p.cwg.Done()

	render := func() error { return s.render(cw) }

	refreshCh := s.newTicker(p.done)

	for {
		select {
		case op := <-p.operateState:
			op(s)
		case fn := <-p.interceptIo:
			fn(cw)
		case <-refreshCh:
			err := render()
			if err != nil {
				p.cancel() // cancel all bars
				_, _ = fmt.Fprintln(s.debugOut, err)
				render = func() error { return nil }
				go func() {
					p.bwg.Wait()
					p.once.Do(p.shutdown)
				}()
			}
		case <-s.shutdownNotifier:
			for s.heapUpdated {
				err := render()
				if err != nil {
					_, _ = fmt.Fprintln(s.debugOut, err)
					return
				}
			}
			return
		}
	}
}

func (s *pState) render(cw *cwriter.Writer) error {
	if s.heapUpdated {
		s.updateSyncMatrix()
		s.heapUpdated = false
	}
	syncWidth(s.pMatrix)
	syncWidth(s.aMatrix)

	width, height, err := cw.GetTermSize()
	if err != nil {
		width = s.reqWidth
		height = s.bHeap.Len()
	}
	for i := 0; i < s.bHeap.Len(); i++ {
		bar := s.bHeap[i]
		go bar.render(width)
	}

	return s.flush(cw, height)
}

func (s *pState) flush(cw *cwriter.Writer, height int) (err error) {
	var wg sync.WaitGroup
	var popCount int

	for s.bHeap.Len() > 0 {
		b := heap.Pop(&s.bHeap).(*Bar)
		frame := <-b.frameCh
		if frame.err != nil {
			if err == nil {
				err = frame.err
			}
			continue
		}
		var usedRows int
		for i := len(frame.rows) - 1; i >= 0; i-- {
			if row := frame.rows[i]; len(s.rows) < height {
				s.rows = append(s.rows, row)
				usedRows++
			} else {
				wg.Add(1)
				go func() {
					_, _ = io.Copy(io.Discard, row)
					wg.Done()
				}()
			}
		}
		if frame.shutdown != 0 {
			b.Wait() // waiting for b.done, so it's safe to read b.bs
			drop := b.bs.dropOnComplete
			if qb, ok := s.queueBars[b]; ok {
				delete(s.queueBars, b)
				qb.priority = b.priority
				s.pool = append(s.pool, qb)
				drop = true
			} else if s.popCompleted && !b.bs.noPop {
				if frame.shutdown > 1 {
					popCount += usedRows
					drop = true
				} else {
					s.popPriority++
					b.priority = s.popPriority
				}
			}
			if drop {
				s.heapUpdated = true
				continue
			}
		}
		s.pool = append(s.pool, b)
	}

	if err != nil {
		return err
	}

	wg.Add(1)
	go func() {
		for _, b := range s.pool {
			heap.Push(&s.bHeap, b)
		}
		wg.Done()
	}()

	for i := len(s.rows) - 1; i >= 0; i-- {
		_, err := cw.ReadFrom(s.rows[i])
		if err != nil {
			wg.Wait()
			return err
		}
	}

	err = cw.Flush(len(s.rows) - popCount)
	wg.Wait()
	s.rows = s.rows[:0]
	s.pool = s.pool[:0]
	return err
}

func (s *pState) newTicker(done <-chan struct{}) chan time.Time {
	ch := make(chan time.Time)
	go func() {
		var autoRefresh <-chan time.Time
		if !s.disableAutoRefresh {
			if !s.outputDiscarded {
				if s.renderDelay != nil {
					<-s.renderDelay
				}
				ticker := time.NewTicker(s.rr)
				defer ticker.Stop()
				autoRefresh = ticker.C
			}
		}
		for {
			select {
			case t := <-autoRefresh:
				ch <- t
			case x := <-s.manualRefresh:
				if t, ok := x.(time.Time); ok {
					ch <- t
				} else {
					ch <- time.Now()
				}
			case <-done:
				close(s.shutdownNotifier)
				return
			}
		}
	}()
	return ch
}

func (s *pState) updateSyncMatrix() {
	s.pMatrix = make(map[int][]chan int)
	s.aMatrix = make(map[int][]chan int)
	for i := 0; i < s.bHeap.Len(); i++ {
		bar := s.bHeap[i]
		table := bar.wSyncTable()
		pRow, aRow := table[0], table[1]

		for i, ch := range pRow {
			s.pMatrix[i] = append(s.pMatrix[i], ch)
		}

		for i, ch := range aRow {
			s.aMatrix[i] = append(s.aMatrix[i], ch)
		}
	}
}

func (s *pState) makeBarState(total int64, filler BarFiller, options ...BarOption) *bState {
	bs := &bState{
		id:            s.idCount,
		priority:      s.idCount,
		reqWidth:      s.reqWidth,
		total:         total,
		filler:        filler,
		manualRefresh: s.manualRefresh,
	}

	if total > 0 {
		bs.triggerComplete = true
	}

	for _, opt := range options {
		if opt != nil {
			opt(bs)
		}
	}

	if bs.middleware != nil {
		bs.filler = bs.middleware(filler)
		bs.middleware = nil
	}

	for i := 0; i < len(bs.buffers); i++ {
		bs.buffers[i] = bytes.NewBuffer(make([]byte, 0, 512))
	}

	bs.subscribeDecorators()

	return bs
}

func syncWidth(matrix map[int][]chan int) {
	for _, column := range matrix {
		go maxWidthDistributor(column)
	}
}

func maxWidthDistributor(column []chan int) {
	var maxWidth int
	for _, ch := range column {
		if w := <-ch; w > maxWidth {
			maxWidth = w
		}
	}
	for _, ch := range column {
		ch <- maxWidth
	}
}
<commit_msg>set heapUpdated to false in case of error<commit_after>package mpb

import (
	"bytes"
	"container/heap"
	"context"
	"fmt"
	"io"
	"math"
	"os"
	"sync"
	"time"

	"github.com/vbauerster/mpb/v8/cwriter"
)

const (
	prr = 150 * time.Millisecond // default RefreshRate
)

// DoneError represents an error when `*mpb.Progress` is done but its functionality is requested.
var DoneError = fmt.Errorf("%T instance can't be reused after it's done!", (*Progress)(nil))

// Progress represents a container that renders one or more progress bars.
type Progress struct {
	ctx          context.Context
	uwg          *sync.WaitGroup
	cwg          *sync.WaitGroup
	bwg          *sync.WaitGroup
	operateState chan func(*pState)
	interceptIo  chan func(io.Writer)
	done         chan struct{}
	once         sync.Once
	cancel       func()
}

// pState holds bars in its priorityQueue, it gets passed to (*Progress).serve monitor goroutine.
type pState struct {
	bHeap       priorityQueue
	heapUpdated bool
	pMatrix     map[int][]chan int
	aMatrix     map[int][]chan int

	// for reuse purposes
	rows []io.Reader
	pool []*Bar

	// following are provided/overrided by user
	rr                 time.Duration
	idCount            int
	reqWidth           int
	popPriority        int
	popCompleted       bool
	outputDiscarded    bool
	disableAutoRefresh bool
	manualRefresh      chan interface{}
	renderDelay        <-chan struct{}
	shutdownNotifier   chan struct{}
	queueBars          map[*Bar]*Bar
	output             io.Writer
	debugOut           io.Writer
	uwg                *sync.WaitGroup
}

// New creates new Progress container instance. It's not possible to
// reuse instance after (*Progress).Wait method has been called.
func New(options ...ContainerOption) *Progress {
	return NewWithContext(context.Background(), options...)
}

// NewWithContext creates new Progress container instance with provided
// context. It's not possible to reuse instance after (*Progress).Wait
// method has been called.
func NewWithContext(ctx context.Context, options ...ContainerOption) *Progress {
	ctx, cancel := context.WithCancel(ctx)
	s := &pState{
		rr:            prr,
		bHeap:         priorityQueue{},
		rows:          make([]io.Reader, 0, 64),
		pool:          make([]*Bar, 0, 64),
		manualRefresh: make(chan interface{}),
		queueBars:     make(map[*Bar]*Bar),
		popPriority:   math.MinInt32,
		output:        os.Stdout,
		debugOut:      io.Discard,
	}

	for _, opt := range options {
		if opt != nil {
			opt(s)
		}
	}

	if s.shutdownNotifier == nil {
		s.shutdownNotifier = make(chan struct{})
	}

	p := &Progress{
		ctx:          ctx,
		uwg:          s.uwg,
		cwg:          new(sync.WaitGroup),
		bwg:          new(sync.WaitGroup),
		operateState: make(chan func(*pState)),
		interceptIo:  make(chan func(io.Writer)),
		done:         make(chan struct{}),
		cancel:       cancel,
	}

	p.cwg.Add(1)
	go p.serve(s, cwriter.New(s.output))
	return p
}

// AddBar creates a bar with default bar filler.
func (p *Progress) AddBar(total int64, options ...BarOption) *Bar {
	return p.New(total, BarStyle(), options...)
}

// AddSpinner creates a bar with default spinner filler.
func (p *Progress) AddSpinner(total int64, options ...BarOption) *Bar {
	return p.New(total, SpinnerStyle(), options...)
}

// New creates a bar by calling `Build` method on provided `BarFillerBuilder`.
func (p *Progress) New(total int64, builder BarFillerBuilder, options ...BarOption) *Bar {
	return p.AddFiller(total, builder.Build(), options...)
}

// AddFiller creates a bar which renders itself by provided filler.
// If `total <= 0` triggering complete event by increment methods is disabled.
// Panics if *Progress instance is done, i.e. called after (*Progress).Wait().
func (p *Progress) AddFiller(total int64, filler BarFiller, options ...BarOption) *Bar {
	if filler == nil {
		filler = NopStyle().Build()
	}
	p.bwg.Add(1)
	result := make(chan *Bar)
	select {
	case p.operateState <- func(ps *pState) {
		bs := ps.makeBarState(total, filler, options...)
		bar := newBar(p, bs)
		if bs.wait.bar != nil {
			ps.queueBars[bs.wait.bar] = bar
		} else {
			heap.Push(&ps.bHeap, bar)
			ps.heapUpdated = true
		}
		ps.idCount++
		result <- bar
	}:
		bar := <-result
		return bar
	case <-p.done:
		p.bwg.Done()
		panic(DoneError)
	}
}

func (p *Progress) traverseBars(cb func(b *Bar) bool) {
	sync := make(chan struct{})
	select {
	case p.operateState <- func(s *pState) {
		for i := 0; i < s.bHeap.Len(); i++ {
			bar := s.bHeap[i]
			if !cb(bar) {
				break
			}
		}
		close(sync)
	}:
		<-sync
	case <-p.done:
	}
}

// UpdateBarPriority same as *Bar.SetPriority(int).
func (p *Progress) UpdateBarPriority(b *Bar, priority int) {
	select {
	case p.operateState <- func(s *pState) {
		if b.index < 0 {
			return
		}
		b.priority = priority
		heap.Fix(&s.bHeap, b.index)
	}:
	case <-p.done:
	}
}

// BarCount returns bars count.
func (p *Progress) BarCount() int {
	result := make(chan int)
	select {
	case p.operateState <- func(s *pState) { result <- s.bHeap.Len() }:
		return <-result
	case <-p.done:
		return 0
	}
}

// Write is implementation of io.Writer.
// Writing to `*mpb.Progress` will print lines above a running bar.
// Writes aren't flushed immediately, but at next refresh cycle.
// If Write is called after `*mpb.Progress` is done, `mpb.DoneError`
// is returned.
func (p *Progress) Write(b []byte) (int, error) {
	type result struct {
		n   int
		err error
	}
	ch := make(chan *result)
	select {
	case p.interceptIo <- func(w io.Writer) {
		n, err := w.Write(b)
		ch <- &result{n, err}
	}:
		res := <-ch
		return res.n, res.err
	case <-p.done:
		return 0, DoneError
	}
}

// Wait waits for all bars to complete and finally shutdowns container. After
// this method has been called, there is no way to reuse (*Progress) instance.
func (p *Progress) Wait() {
	// wait for user wg, if any
	if p.uwg != nil {
		p.uwg.Wait()
	}

	// wait for bars to quit, if any
	p.bwg.Wait()
	// shutdown
	p.once.Do(p.shutdown)
	// wait for container to quit
	p.cwg.Wait()
}

// Shutdown cancels any running bar immediately and then shutdowns (*Progress)
// instance. Normally this method shouldn't be called unless you know what you
// are doing. Proper way to shutdown is to call (*Progress).Wait() instead.
func (p *Progress) Shutdown() {
	p.cancel()
	p.bwg.Wait()
	p.once.Do(p.shutdown)
	p.cwg.Wait()
}

func (p *Progress) shutdown() {
	close(p.done)
}

func (p *Progress) serve(s *pState, cw *cwriter.Writer) {
	defer p.cwg.Done()

	render := func() error { return s.render(cw) }

	refreshCh := s.newTicker(p.done)

	for {
		select {
		case op := <-p.operateState:
			op(s)
		case fn := <-p.interceptIo:
			fn(cw)
		case <-refreshCh:
			err := render()
			if err != nil {
				go func() {
					p.bwg.Wait()
					p.once.Do(p.shutdown)
				}()
				render = func() error {
					s.heapUpdated = false
					return nil
				}
				_, _ = fmt.Fprintln(s.debugOut, err)
				p.cancel() // cancel all bars
			}
		case <-s.shutdownNotifier:
			for s.heapUpdated {
				err := render()
				if err != nil {
					_, _ = fmt.Fprintln(s.debugOut, err)
					return
				}
			}
			return
		}
	}
}

func (s *pState) render(cw *cwriter.Writer) error {
	if s.heapUpdated {
		s.updateSyncMatrix()
		s.heapUpdated = false
	}
	syncWidth(s.pMatrix)
	syncWidth(s.aMatrix)

	width, height, err := cw.GetTermSize()
	if err != nil {
		width = s.reqWidth
		height = s.bHeap.Len()
	}
	for i := 0; i < s.bHeap.Len(); i++ {
		bar := s.bHeap[i]
		go bar.render(width)
	}

	return s.flush(cw, height)
}

func (s *pState) flush(cw *cwriter.Writer, height int) (err error) {
	var wg sync.WaitGroup
	var popCount int

	for s.bHeap.Len() > 0 {
		b := heap.Pop(&s.bHeap).(*Bar)
		frame := <-b.frameCh
		if frame.err != nil {
			if err == nil {
				err = frame.err
			}
			continue
		}
		var usedRows int
		for i := len(frame.rows) - 1; i >= 0; i-- {
			if row := frame.rows[i]; len(s.rows) < height {
				s.rows = append(s.rows, row)
				usedRows++
			} else {
				wg.Add(1)
				go func() {
					_, _ = io.Copy(io.Discard, row)
					wg.Done()
				}()
			}
		}
		if frame.shutdown != 0 {
			b.Wait() // waiting for b.done, so it's safe to read b.bs
			drop := b.bs.dropOnComplete
			if qb, ok := s.queueBars[b]; ok {
				delete(s.queueBars, b)
				qb.priority = b.priority
				s.pool = append(s.pool, qb)
				drop = true
			} else if s.popCompleted && !b.bs.noPop {
				if frame.shutdown > 1 {
					popCount += usedRows
					drop = true
				} else {
					s.popPriority++
					b.priority = s.popPriority
				}
			}
			if drop {
				s.heapUpdated = true
				continue
			}
		}
		s.pool = append(s.pool, b)
	}

	if err != nil {
		return err
	}

	wg.Add(1)
	go func() {
		for _, b := range s.pool {
			heap.Push(&s.bHeap, b)
		}
		wg.Done()
	}()

	for i := len(s.rows) - 1; i >= 0; i-- {
		_, err := cw.ReadFrom(s.rows[i])
		if err != nil {
			wg.Wait()
			return err
		}
	}

	err = cw.Flush(len(s.rows) - popCount)
	wg.Wait()
	s.rows = s.rows[:0]
	s.pool = s.pool[:0]
	return err
}

func (s *pState) newTicker(done <-chan struct{}) chan time.Time {
	ch := make(chan time.Time)
	go func() {
		var autoRefresh <-chan time.Time
		if !s.disableAutoRefresh {
			if !s.outputDiscarded {
				if s.renderDelay != nil {
					<-s.renderDelay
				}
				ticker := time.NewTicker(s.rr)
				defer ticker.Stop()
				autoRefresh = ticker.C
			}
		}
		for {
			select {
			case t := <-autoRefresh:
				ch <- t
			case x := <-s.manualRefresh:
				if t, ok := x.(time.Time); ok {
					ch <- t
				} else {
					ch <- time.Now()
				}
			case <-done:
				close(s.shutdownNotifier)
				return
			}
		}
	}()
	return ch
}

func (s *pState) updateSyncMatrix() {
	s.pMatrix = make(map[int][]chan int)
	s.aMatrix = make(map[int][]chan int)
	for i := 0; i < s.bHeap.Len(); i++ {
		bar := s.bHeap[i]
		table := bar.wSyncTable()
		pRow, aRow := table[0], table[1]

		for i, ch := range pRow {
			s.pMatrix[i] = append(s.pMatrix[i], ch)
		}

		for i, ch := range aRow {
			s.aMatrix[i] = append(s.aMatrix[i], ch)
		}
	}
}

func (s *pState) makeBarState(total int64, filler BarFiller, options ...BarOption) *bState {
	bs := &bState{
		id:            s.idCount,
		priority:      s.idCount,
		reqWidth:      s.reqWidth,
		total:         total,
		filler:        filler,
		manualRefresh: s.manualRefresh,
	}

	if total > 0 {
		bs.triggerComplete = true
	}

	for _, opt := range options {
		if opt != nil {
			opt(bs)
		}
	}

	if bs.middleware != nil {
		bs.filler = bs.middleware(filler)
		bs.middleware = nil
	}

	for i := 0; i < len(bs.buffers); i++ {
		bs.buffers[i] = bytes.NewBuffer(make([]byte, 0, 512))
	}

	bs.subscribeDecorators()

	return bs
}

func syncWidth(matrix map[int][]chan int) {
	for _, column := range matrix {
		go maxWidthDistributor(column)
	}
}

func maxWidthDistributor(column []chan int) {
	var maxWidth int
	for _, ch := range column {
		if w := <-ch; w > maxWidth {
			maxWidth = w
		}
	}
	for _, ch := range column {
		ch <- maxWidth
	}
}
<|endoftext|>


<commit_before>package fstree_test

import (
	"os"
	"runtime"
	"strconv"
	"strings"
	"testing"

	"github.com/caelifer/dups/fstree"
	"github.com/caelifer/scheduler"
)

func TestTreeWalk(t *testing.T) {
	// Recover from panic
	defer func() {
		if r := recover(); r != nil {
			t.Fatal(r)
		}
	}()

	testRoot := "/Users/timour/golang/src/github.com/caelifer/dups/t/"

	nprocs := runtime.NumCPU()
	t.Logf("Using %d CPU threads\n", nprocs)

	for i := 10; i < 20; i++ {
		results := make([]string, 0, 256)
		testdir := testRoot + strconv.Itoa(i)

		err := fstree.Walk(scheduler.New(nprocs, 1024), testdir, func(path string, info os.FileInfo, err error) error {
			results = append(results, path)
			return nil
		})

		if err != nil {
			t.Fatal(err)
		}

		t.Logf("Results for %d entries:\n%s\n", i+1, strings.Join(results, "\n"))
		if n := len(results); n != i+1 {
			t.Errorf("Expected %d nodes, got %d\n", i+1, n)
		}
	}
}

func TestTreeWalk11(t *testing.T) {
	// Recover from panic
	defer func() {
		if r := recover(); r != nil {
			t.Fatal(r)
		}
	}()

	testRoot := "/Users/timour/golang/src/github.com/caelifer/dups/t/"
	i := 11

	results := make([]string, 0, 256)
	testdir := testRoot + strconv.Itoa(i)

	nprocs := runtime.NumCPU()
	t.Logf("Using %d CPU threads\n", nprocs)

	err := fstree.Walk(scheduler.New(nprocs, 1024), testdir, func(path string, info os.FileInfo, err error) error {
		results = append(results, path)
		return nil
	})

	if err != nil {
		t.Fatal(err)
	}

	t.Logf("Results for %d entries:\n%s\n", i+1, strings.Join(results, "\n"))
	if n := len(results); n != i+1 {
		t.Errorf("Expected %d nodes, got %d\n", i+1, n)
	}
}

func TestTreeWalk12(t *testing.T) {
	// Recover from panic
	defer func() {
		if r := recover(); r != nil {
			t.Fatal(r)
		}
	}()

	testRoot := "/Users/timour/golang/src/github.com/caelifer/dups/t/"
	i := 12

	results := make([]string, 0, 256)
	testdir := testRoot + strconv.Itoa(i)

	nprocs := runtime.NumCPU()
	t.Logf("Using %d CPU threads\n", nprocs)

	err := fstree.Walk(scheduler.New(nprocs, 1024), testdir, func(path string, info os.FileInfo, err error) error {
		results = append(results, path)
		return nil
	})

	if err != nil {
		t.Fatal(err)
	}

	t.Logf("Results for %d entries:\n%s\n", i+1, strings.Join(results, "\n"))
	if n := len(results); n != i+1 {
		t.Errorf("Expected %d nodes, got %d\n", i+1, n)
	}
}

func TestTreeWalk13(t *testing.T) {
	// Recover from panic
	defer func() {
		if r := recover(); r != nil {
			t.Fatal(r)
		}
	}()

	testRoot := "/Users/timour/golang/src/github.com/caelifer/dups/t/"
	i := 13

	results := make([]string, 0, 256)
	testdir := testRoot + strconv.Itoa(i)

	nprocs := runtime.NumCPU()
	t.Logf("Using %d CPU threads\n", nprocs)

	err := fstree.Walk(scheduler.New(nprocs, 1024), testdir, func(path string, info os.FileInfo, err error) error {
		results = append(results, path)
		return nil
	})

	if err != nil {
		t.Fatal(err)
	}

	t.Logf("Results for %d entries:\n%s\n", i+1, strings.Join(results, "\n"))
	if n := len(results); n != i+1 {
		t.Errorf("Expected %d nodes, got %d\n", i+1, n)
	}
}

func TestTreeWalkEmpty(t *testing.T) {
	// Recover from panic
	defer func() {
		if r := recover(); r != nil {
			t.Fatal(r)
		}
	}()

	testRoot := "/Users/timour/golang/src/github.com/caelifer/dups/t/c/a"
	i := 0

	results := make([]string, 0, 256)
	testdir := testRoot

	nprocs := runtime.NumCPU()
	t.Logf("Using %d CPU threads\n", nprocs)

	err := fstree.Walk(scheduler.New(nprocs, 1024), testdir, func(path string, info os.FileInfo, err error) error {
		results = append(results, path)
		return nil
	})

	if err != nil {
		t.Fatal(err)
	}

	t.Logf("Results for %d entries:\n%s\n", i+1, strings.Join(results, "\n"))
	if n := len(results); n != i+1 {
		t.Errorf("Expected %d nodes, got %d\n", i+1, n)
	}
}
<commit_msg>Removed conflicting test file<commit_after><|endoftext|>


<commit_before>/*
Copyright 2019 The Knative Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
	"time"

	"github.com/golang/protobuf/proto"
	tpb "github.com/google/mako/clients/proto/analyzers/threshold_analyzer_go_proto"
	mpb "github.com/google/mako/spec/proto/mako_go_proto"
	"knative.dev/pkg/test/mako"
)

// This analyzer validates that the p95 latency over the 0->3k stepped burst
// falls in the +15ms range.   This includes a mix of cold-starts and steady
// state (once the autoscaling decisions have leveled off).
func newLoadTest95PercentileLatency(tags ...string) *tpb.ThresholdAnalyzerInput {
	return &tpb.ThresholdAnalyzerInput{
		Name: proto.String("95p latency"),
		Configs: []*tpb.ThresholdConfig{{
			Min: bound(100 * time.Millisecond),
			Max: bound(115 * time.Millisecond),
			DataFilter: &mpb.DataFilter{
				DataType:            mpb.DataFilter_METRIC_AGGREGATE_PERCENTILE.Enum(),
				PercentileMilliRank: proto.Int32(95000),
				ValueKey:            proto.String("l"),
			},
		}},
		CrossRunConfig: mako.NewCrossRunConfig(10, tags...),
	}
}

// This analyzer validates that the maximum request latency observed over the 0->3k
// stepped burst is no more than +15 seconds.  This is not strictly a cold-start
// metric, but it is a superset that includes steady state latency and the latency
// of non-cold-start overload requests.
func newLoadTestMaximumLatency(tags ...string) *tpb.ThresholdAnalyzerInput {
	return &tpb.ThresholdAnalyzerInput{
		Name: proto.String("Maximum latency"),
		Configs: []*tpb.ThresholdConfig{{
			Min: bound(100 * time.Millisecond),
			Max: bound(100*time.Millisecond + 15*time.Second),
			DataFilter: &mpb.DataFilter{
				DataType: mpb.DataFilter_METRIC_AGGREGATE_MAX.Enum(),
				ValueKey: proto.String("l"),
			},
		}},
		CrossRunConfig: mako.NewCrossRunConfig(10, tags...),
	}
}

// bound is a helper for making the inline SLOs more readable by expressing
// them as durations.
func bound(d time.Duration) *float64 {
	return proto.Float64(d.Seconds())
}
<commit_msg>Revise our cold start latency SLAs to 10s (from 15s) (#5420)<commit_after>/*
Copyright 2019 The Knative Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
	"time"

	"github.com/golang/protobuf/proto"
	tpb "github.com/google/mako/clients/proto/analyzers/threshold_analyzer_go_proto"
	mpb "github.com/google/mako/spec/proto/mako_go_proto"
	"knative.dev/pkg/test/mako"
)

// This analyzer validates that the p95 latency over the 0->3k stepped burst
// falls in the +15ms range.   This includes a mix of cold-starts and steady
// state (once the autoscaling decisions have leveled off).
func newLoadTest95PercentileLatency(tags ...string) *tpb.ThresholdAnalyzerInput {
	return &tpb.ThresholdAnalyzerInput{
		Name: proto.String("95p latency"),
		Configs: []*tpb.ThresholdConfig{{
			Min: bound(100 * time.Millisecond),
			Max: bound(115 * time.Millisecond),
			DataFilter: &mpb.DataFilter{
				DataType:            mpb.DataFilter_METRIC_AGGREGATE_PERCENTILE.Enum(),
				PercentileMilliRank: proto.Int32(95000),
				ValueKey:            proto.String("l"),
			},
		}},
		CrossRunConfig: mako.NewCrossRunConfig(10, tags...),
	}
}

// This analyzer validates that the maximum request latency observed over the 0->3k
// stepped burst is no more than +10 seconds.  This is not strictly a cold-start
// metric, but it is a superset that includes steady state latency and the latency
// of non-cold-start overload requests.
func newLoadTestMaximumLatency(tags ...string) *tpb.ThresholdAnalyzerInput {
	return &tpb.ThresholdAnalyzerInput{
		Name: proto.String("Maximum latency"),
		Configs: []*tpb.ThresholdConfig{{
			Min: bound(100 * time.Millisecond),
			Max: bound(100*time.Millisecond + 10*time.Second),
			DataFilter: &mpb.DataFilter{
				DataType: mpb.DataFilter_METRIC_AGGREGATE_MAX.Enum(),
				ValueKey: proto.String("l"),
			},
		}},
		CrossRunConfig: mako.NewCrossRunConfig(10, tags...),
	}
}

// bound is a helper for making the inline SLOs more readable by expressing
// them as durations.
func bound(d time.Duration) *float64 {
	return proto.Float64(d.Seconds())
}
<|endoftext|>


<commit_before>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

// Package crossmodel provides an API server facade for managing
// cross model relations.
package crossmodel

import (
	"strings"

	"github.com/juju/errors"
	"github.com/juju/names"

	"github.com/juju/juju/apiserver/common"
	"github.com/juju/juju/apiserver/params"
	"github.com/juju/juju/model/crossmodel"
	"github.com/juju/juju/state"
)

func init() {
	common.RegisterStandardFacade("CrossModelRelations", 1, NewAPI)
}

// API implements the cross model interface and is the concrete
// implementation of the api end point.
type API struct {
	authorizer common.Authorizer
	exporter   crossmodel.Exporter
}

// createAPI returns a new cross model API facade.
func createAPI(
	exporter crossmodel.Exporter,
	resources *common.Resources,
	authorizer common.Authorizer,
) (*API, error) {
	if !authorizer.AuthClient() {
		return nil, common.ErrPerm
	}

	return &API{
		authorizer: authorizer,
		exporter:   exporter,
	}, nil
}

// NewAPI returns a new cross model API facade.
func NewAPI(
	st *state.State,
	resources *common.Resources,
	authorizer common.Authorizer,
) (*API, error) {
	return createAPI(exporter(st), resources, authorizer)
}

func exporter(st *state.State) crossmodel.Exporter {
	return crossmodel.ExporterStub{}
}

// Offer makes service endpoints available for consumption.
func (api *API) Offer(all params.CrossModelOffers) (params.ErrorResults, error) {
	// export service offers
	offers := make([]params.ErrorResult, len(all.Offers))
	for i, one := range all.Offers {
		offer, err := parseOffer(one)
		if err != nil {
			offers[i].Error = common.ServerError(err)
			continue
		}

		if err := api.exporter.ExportOffer(offer); err != nil {
			offers[i].Error = common.ServerError(err)
		}
	}
	return params.ErrorResults{Results: offers}, nil
}

// Show gets details about remote services that match given URLs.
func (api *API) Show(urls []string) (params.RemoteServiceInfoResults, error) {
	found, err := api.exporter.Search(urls)
	if err != nil {
		return params.RemoteServiceInfoResults{}, errors.Trace(err)
	}
	if len(found) == 0 {
		return params.RemoteServiceInfoResults{}, errors.NotFoundf("endpoints with urls %v", strings.Join(urls, ","))
	}

	results := make([]params.RemoteServiceInfoResult, len(found))
	for i, one := range found {
		results[i].RemoteService = convertRemoteServiceDetails(one)
		// TODO (anastasiamac 2015-11-12) once back-end is done in separate branch,
		// fix values for results[i].URL and results[i].Error
	}
	return params.RemoteServiceInfoResults{results}, nil
}

// parseOffer is a helper function that translates from params
// structure into internal service layer one.
func parseOffer(p params.CrossModelOffer) (crossmodel.Offer, error) {
	offer := crossmodel.Offer{}

	serviceTag, err := names.ParseServiceTag(p.Service)
	if err != nil {
		return offer, errors.Annotatef(err, "cannot parse service tag %q", p.Service)
	}

	users := make([]names.UserTag, len(p.Users))
	for i, user := range p.Users {
		users[i], err = names.ParseUserTag(user)
		if err != nil {
			return offer, errors.Annotatef(err, "cannot parse user tag %q", user)
		}
	}
	offer.Service = serviceTag
	offer.URL = p.URL
	offer.Users = users
	offer.Endpoints = p.Endpoints
	return offer, nil
}

// convertRemoteServiceDetails is a helper function that translates from internal service layer
// structure into params one.
func convertRemoteServiceDetails(c crossmodel.RemoteServiceEndpoints) params.RemoteServiceInfo {
	endpoints := make([]params.RemoteEndpoint, len(c.Endpoints))

	for i, endpoint := range c.Endpoints {
		endpoints[i] = params.RemoteEndpoint{
			Name:      endpoint.Name,
			Interface: endpoint.Interface,
			Role:      endpoint.Role,
		}
	}
	return params.RemoteServiceInfo{
		Service:     c.Service.String(),
		Endpoints:   endpoints,
		Description: c.Description,
	}
}
<commit_msg>Added omitempty and removed URL.<commit_after>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

// Package crossmodel provides an API server facade for managing
// cross model relations.
package crossmodel

import (
	"strings"

	"github.com/juju/errors"
	"github.com/juju/names"

	"github.com/juju/juju/apiserver/common"
	"github.com/juju/juju/apiserver/params"
	"github.com/juju/juju/model/crossmodel"
	"github.com/juju/juju/state"
)

func init() {
	common.RegisterStandardFacade("CrossModelRelations", 1, NewAPI)
}

// API implements the cross model interface and is the concrete
// implementation of the api end point.
type API struct {
	authorizer common.Authorizer
	exporter   crossmodel.Exporter
}

// createAPI returns a new cross model API facade.
func createAPI(
	exporter crossmodel.Exporter,
	resources *common.Resources,
	authorizer common.Authorizer,
) (*API, error) {
	if !authorizer.AuthClient() {
		return nil, common.ErrPerm
	}

	return &API{
		authorizer: authorizer,
		exporter:   exporter,
	}, nil
}

// NewAPI returns a new cross model API facade.
func NewAPI(
	st *state.State,
	resources *common.Resources,
	authorizer common.Authorizer,
) (*API, error) {
	return createAPI(exporter(st), resources, authorizer)
}

func exporter(st *state.State) crossmodel.Exporter {
	return crossmodel.ExporterStub{}
}

// Offer makes service endpoints available for consumption.
func (api *API) Offer(all params.CrossModelOffers) (params.ErrorResults, error) {
	// export service offers
	offers := make([]params.ErrorResult, len(all.Offers))
	for i, one := range all.Offers {
		offer, err := parseOffer(one)
		if err != nil {
			offers[i].Error = common.ServerError(err)
			continue
		}

		if err := api.exporter.ExportOffer(offer); err != nil {
			offers[i].Error = common.ServerError(err)
		}
	}
	return params.ErrorResults{Results: offers}, nil
}

// Show gets details about remote services that match given URLs.
func (api *API) Show(urls []string) (params.RemoteServiceInfoResults, error) {
	found, err := api.exporter.Search(urls)
	if err != nil {
		return params.RemoteServiceInfoResults{}, errors.Trace(err)
	}
	if len(found) == 0 {
		return params.RemoteServiceInfoResults{}, errors.NotFoundf("endpoints with urls %v", strings.Join(urls, ","))
	}

	results := make([]params.RemoteServiceInfoResult, len(found))
	for i, one := range found {
		results[i].RemoteService = convertRemoteServiceDetails(one)
		// TODO (anastasiamac 2015-11-12) once back-end is done in separate branch,
		// fix values for results[i].Error
	}
	return params.RemoteServiceInfoResults{results}, nil
}

// parseOffer is a helper function that translates from params
// structure into internal service layer one.
func parseOffer(p params.CrossModelOffer) (crossmodel.Offer, error) {
	offer := crossmodel.Offer{}

	serviceTag, err := names.ParseServiceTag(p.Service)
	if err != nil {
		return offer, errors.Annotatef(err, "cannot parse service tag %q", p.Service)
	}

	users := make([]names.UserTag, len(p.Users))
	for i, user := range p.Users {
		users[i], err = names.ParseUserTag(user)
		if err != nil {
			return offer, errors.Annotatef(err, "cannot parse user tag %q", user)
		}
	}
	offer.Service = serviceTag
	offer.URL = p.URL
	offer.Users = users
	offer.Endpoints = p.Endpoints
	return offer, nil
}

// convertRemoteServiceDetails is a helper function that translates from internal service layer
// structure into params one.
func convertRemoteServiceDetails(c crossmodel.RemoteServiceEndpoints) params.RemoteServiceInfo {
	endpoints := make([]params.RemoteEndpoint, len(c.Endpoints))

	for i, endpoint := range c.Endpoints {
		endpoints[i] = params.RemoteEndpoint{
			Name:      endpoint.Name,
			Interface: endpoint.Interface,
			Role:      endpoint.Role,
		}
	}
	return params.RemoteServiceInfo{
		Service:     c.Service.String(),
		Endpoints:   endpoints,
		Description: c.Description,
	}
}
<|endoftext|>


<commit_before>// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://gitee.com/johng/gf.

// 单进程高速缓存.
package gcache

import (
    "sync"
    "time"
    "math"
    "gitee.com/johng/gf/g/os/gtime"
    "gitee.com/johng/gf/g/container/gset"
    "gitee.com/johng/gf/g/container/gqueue"
)

// 缓存对象
type Cache struct {
    dmu        sync.RWMutex              // data锁
    emu        sync.RWMutex              // ekmap锁
    data       map[string]CacheItem      // 缓存数据
    ekmap      map[string]int64          // 键名对应的分组过期时间
    eksets     map[int64]*gset.StringSet // 分组过期时间对应的键名列表
    eventQueue *gqueue.Queue             // 异步处理队列
    stopEvents chan struct{}             // 关闭时间通知
}

// 缓存数据项
type CacheItem struct {
    v interface{} // 缓存键值
    e int64       // 过期时间
}

// 异步队列数据项
type EventItem struct {
    k string      // 键名
    e int64       // 过期时间
}

// 全局缓存管理对象
var cache *Cache = New()

// Cache对象按照缓存键名首字母做了分组
func New() *Cache {
    c := &Cache {
        data       : make(map[string]CacheItem),
        ekmap      : make(map[string]int64),
        eksets     : make(map[int64]*gset.StringSet),
        eventQueue : gqueue.New(),
        stopEvents : make(chan struct{}, 2),
    }
    go c.autoSyncLoop()
    go c.autoClearLoop()
    return c
}

// (使用全局KV缓存对象)设置kv缓存键值对，过期时间单位为毫秒
func Set(key string, value interface{}, expire int64)  {
    cache.Set(key, value, expire)
}

// (使用全局KV缓存对象)批量设置kv缓存键值对，过期时间单位为毫秒
func BatchSet(data map[string]interface{}, expire int64)  {
    cache.BatchSet(data, expire)
}

// (使用全局KV缓存对象)获取指定键名的值
func Get(key string) interface{} {
    return cache.Get(key)
}

// (使用全局KV缓存对象)删除指定键值对
func Remove(key string) {
    cache.Remove(key)
}

// (使用全局KV缓存对象)批量删除指定键值对
func BatchRemove(keys []string) {
    cache.BatchRemove(keys)
}

// 计算过期缓存的键名
func (c *Cache) makeExpireKey(expire int64) int64 {
    return int64(math.Ceil(float64(expire/1000) + 1)*1000)
}

// 获取一个过期键名存放Set,如果没有则返回nil
func (c *Cache) getExpireSet(expire int64) *gset.StringSet {
    c.emu.RLock()
    if ekset, ok := c.eksets[expire]; ok {
        c.emu.RUnlock()
        return ekset
    }
    c.emu.RUnlock()
    return nil
}

// 获取或者创建一个过期键名存放Set(由于是异步单线程执行，因此不会出现创建set时的覆盖问题)
func (c *Cache) getOrNewExpireSet(expire int64) *gset.StringSet {
    if ekset := c.getExpireSet(expire); ekset == nil {
        set := gset.NewStringSet()
        c.emu.Lock()
        c.eksets[expire] = set
        c.emu.Unlock()
        return set
    } else {
        return ekset
    }
}

// 设置kv缓存键值对，过期时间单位为毫秒，expire<=0表示不过期
func (c *Cache) Set(key string, value interface{}, expire int64) {
    e := int64(math.MaxInt64)
    if expire > 0 {
        e = gtime.Millisecond() + int64(expire)
    }
    c.dmu.Lock()
    c.data[key] = CacheItem{v: value, e: e}
    c.dmu.Unlock()
    c.eventQueue.PushBack(EventItem{k: key, e:e})
}

// 批量设置
func (c *Cache) BatchSet(data map[string]interface{}, expire int64)  {
    e := int64(math.MaxInt64)
    if expire > 0 {
        e = gtime.Millisecond() + int64(expire)
    }
    for k, v := range data {
        c.dmu.Lock()
        c.data[k] = CacheItem{v: v, e: e}
        c.dmu.Unlock()
        c.eventQueue.PushBack(EventItem{k: k, e:e})
    }
}

// 获取指定键名的值
func (c *Cache) Get(key string) interface{} {
    c.dmu.RLock()
    item, ok := c.data[key]
    c.dmu.RUnlock()
    if ok {
        if item.e > gtime.Millisecond() {
            return item.v
        }
    }
    return nil
}

// 删除指定键值对
func (c *Cache) Remove(key string) {
    c.Set(key, nil, -1)
}

// 批量删除键值对
func (c *Cache) BatchRemove(keys []string) {
    for _, key := range keys {
        c.dmu.Lock()
        c.data[key] = CacheItem{v: nil, e: -1}
        c.dmu.Unlock()
        c.eventQueue.PushBack(EventItem{k: key, e: -1})
    }
}

// 获得所有的键名，组成字符串数组返回
func (c *Cache) Keys() []string {
    keys := make([]string, 0)
    c.dmu.RLock()
    for k, _ := range c.data {
        keys = append(keys, k)
    }
    c.dmu.RUnlock()
    return keys
}

// 获得所有的值，组成数组返回
func (c *Cache) Values() []interface{} {
    values := make([]interface{}, 0)
    c.dmu.RLock()
    for _, v := range c.data {
        values = append(values, v)
    }
    c.dmu.RUnlock()
    return values
}

// 获得缓存对象的键值对数量
func (c *Cache) Size() int {
    c.dmu.RLock()
    length := len(c.data)
    c.dmu.RUnlock()
    return length
}

// 删除缓存对象
func (c *Cache) Close()  {
    c.stopEvents <- struct{}{}
    c.eventQueue.Close()
}

// 数据自动同步循环
func (c *Cache) autoSyncLoop() {
    for {
        if r := c.eventQueue.PopFront(); r != nil {
            item := r.(EventItem)
            newe := c.makeExpireKey(item.e)
            if olde, ok := c.ekmap[item.k]; ok {
                if newe != olde {
                    if ekset := c.getExpireSet(olde); ekset != nil {
                        ekset.Remove(item.k)
                    }
                }
            }
            c.getOrNewExpireSet(newe).Add(item.k)
            c.ekmap[item.k] = newe
        } else {
            break
        }
    }
}

// 自动清理过期键值对
// 每隔1秒清除过去3秒的键值对数据
func (c *Cache) autoClearLoop() {
    for {
        select {
            case <- c.stopEvents:
                return
            default:
                ek  := c.makeExpireKey(gtime.Millisecond())
                eks := []int64{ek - 2000, ek - 3000, ek - 4000}
                for _, v := range eks {
                    if ekset := c.getExpireSet(v); ekset != nil {
                        c.dmu.Lock()
                        ekset.Iterator(func(key string) {
                            delete(c.data,  key)
                            delete(c.ekmap, key)
                        })
                        c.dmu.Unlock()
                    }
                    c.emu.Lock()
                    delete(c.eksets, v)
                    c.emu.Unlock()
                }
                time.Sleep(time.Second)
        }
    }
}
<commit_msg>修复gcache不同map的锁机制问题<commit_after>// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://gitee.com/johng/gf.

// 单进程高速缓存.
package gcache

import (
    "sync"
    "time"
    "math"
    "gitee.com/johng/gf/g/os/gtime"
    "gitee.com/johng/gf/g/container/gset"
    "gitee.com/johng/gf/g/container/gqueue"
)

// 缓存对象
type Cache struct {
    dmu        sync.RWMutex              // data锁
    emu        sync.RWMutex              // ekmap锁
    smu        sync.RWMutex              // eksets锁
    data       map[string]CacheItem      // 缓存数据
    ekmap      map[string]int64          // 键名对应的分组过期时间
    eksets     map[int64]*gset.StringSet // 分组过期时间对应的键名列表
    eventQueue *gqueue.Queue             // 异步处理队列
    stopEvents chan struct{}             // 关闭时间通知
}

// 缓存数据项
type CacheItem struct {
    v interface{} // 缓存键值
    e int64       // 过期时间
}

// 异步队列数据项
type EventItem struct {
    k string      // 键名
    e int64       // 过期时间
}

// 全局缓存管理对象
var cache *Cache = New()

// Cache对象按照缓存键名首字母做了分组
func New() *Cache {
    c := &Cache {
        data       : make(map[string]CacheItem),
        ekmap      : make(map[string]int64),
        eksets     : make(map[int64]*gset.StringSet),
        eventQueue : gqueue.New(),
        stopEvents : make(chan struct{}, 2),
    }
    go c.autoSyncLoop()
    go c.autoClearLoop()
    return c
}

// (使用全局KV缓存对象)设置kv缓存键值对，过期时间单位为毫秒
func Set(key string, value interface{}, expire int64)  {
    cache.Set(key, value, expire)
}

// (使用全局KV缓存对象)批量设置kv缓存键值对，过期时间单位为毫秒
func BatchSet(data map[string]interface{}, expire int64)  {
    cache.BatchSet(data, expire)
}

// (使用全局KV缓存对象)获取指定键名的值
func Get(key string) interface{} {
    return cache.Get(key)
}

// (使用全局KV缓存对象)删除指定键值对
func Remove(key string) {
    cache.Remove(key)
}

// (使用全局KV缓存对象)批量删除指定键值对
func BatchRemove(keys []string) {
    cache.BatchRemove(keys)
}

// 计算过期缓存的键名
func (c *Cache) makeExpireKey(expire int64) int64 {
    return int64(math.Ceil(float64(expire/1000) + 1)*1000)
}

// 获取一个过期键名存放Set,如果没有则返回nil
func (c *Cache) getExpireSet(expire int64) *gset.StringSet {
    c.smu.RLock()
    if ekset, ok := c.eksets[expire]; ok {
        c.smu.RUnlock()
        return ekset
    }
    c.smu.RUnlock()
    return nil
}

// 获取或者创建一个过期键名存放Set(由于是异步单线程执行，因此不会出现创建set时的覆盖问题)
func (c *Cache) getOrNewExpireSet(expire int64) *gset.StringSet {
    if ekset := c.getExpireSet(expire); ekset == nil {
        set := gset.NewStringSet()
        c.smu.Lock()
        c.eksets[expire] = set
        c.smu.Unlock()
        return set
    } else {
        return ekset
    }
}

// 设置kv缓存键值对，过期时间单位为毫秒，expire<=0表示不过期
func (c *Cache) Set(key string, value interface{}, expire int64) {
    e := int64(math.MaxInt64)
    if expire > 0 {
        e = gtime.Millisecond() + int64(expire)
    }
    c.dmu.Lock()
    c.data[key] = CacheItem{v: value, e: e}
    c.dmu.Unlock()
    c.eventQueue.PushBack(EventItem{k: key, e:e})
}

// 批量设置
func (c *Cache) BatchSet(data map[string]interface{}, expire int64)  {
    e := int64(math.MaxInt64)
    if expire > 0 {
        e = gtime.Millisecond() + int64(expire)
    }
    for k, v := range data {
        c.dmu.Lock()
        c.data[k] = CacheItem{v: v, e: e}
        c.dmu.Unlock()
        c.eventQueue.PushBack(EventItem{k: k, e:e})
    }
}

// 获取指定键名的值
func (c *Cache) Get(key string) interface{} {
    c.dmu.RLock()
    item, ok := c.data[key]
    c.dmu.RUnlock()
    if ok {
        if item.e > gtime.Millisecond() {
            return item.v
        }
    }
    return nil
}

// 删除指定键值对
func (c *Cache) Remove(key string) {
    c.Set(key, nil, -1)
}

// 批量删除键值对
func (c *Cache) BatchRemove(keys []string) {
    for _, key := range keys {
        c.dmu.Lock()
        c.data[key] = CacheItem{v: nil, e: -1}
        c.dmu.Unlock()
        c.eventQueue.PushBack(EventItem{k: key, e: -1})
    }
}

// 获得所有的键名，组成字符串数组返回
func (c *Cache) Keys() []string {
    keys := make([]string, 0)
    c.dmu.RLock()
    for k, _ := range c.data {
        keys = append(keys, k)
    }
    c.dmu.RUnlock()
    return keys
}

// 获得所有的值，组成数组返回
func (c *Cache) Values() []interface{} {
    values := make([]interface{}, 0)
    c.dmu.RLock()
    for _, v := range c.data {
        values = append(values, v)
    }
    c.dmu.RUnlock()
    return values
}

// 获得缓存对象的键值对数量
func (c *Cache) Size() int {
    c.dmu.RLock()
    length := len(c.data)
    c.dmu.RUnlock()
    return length
}

// 删除缓存对象
func (c *Cache) Close()  {
    c.stopEvents <- struct{}{}
    c.eventQueue.Close()
}

// 数据自动同步循环
func (c *Cache) autoSyncLoop() {
    for {
        if r := c.eventQueue.PopFront(); r != nil {
            item := r.(EventItem)
            newe := c.makeExpireKey(item.e)
            c.emu.RLock()
            olde, ok := c.ekmap[item.k];
            c.emu.RUnlock()
            if ok && newe != olde {
                if ekset := c.getExpireSet(olde); ekset != nil {
                    ekset.Remove(item.k)
                }
            }
            c.getOrNewExpireSet(newe).Add(item.k)
            c.emu.Lock()
            c.ekmap[item.k] = newe
            c.emu.Unlock()
        } else {
            break
        }
    }
}

// 自动清理过期键值对
// 每隔1秒清除过去3秒的键值对数据
func (c *Cache) autoClearLoop() {
    for {
        select {
            case <- c.stopEvents:
                return
            default:
                ek  := c.makeExpireKey(gtime.Millisecond())
                eks := []int64{ek - 2000, ek - 3000, ek - 4000}
                for _, v := range eks {
                    if ekset := c.getExpireSet(v); ekset != nil {

                        ekset.Iterator(func(key string) {
                            c.dmu.Lock()
                            delete(c.data,  key)
                            c.dmu.Unlock()

                            c.emu.Lock()
                            delete(c.ekmap, key)
                            c.emu.Unlock()
                        })

                    }
                    c.smu.Lock()
                    delete(c.eksets, v)
                    c.smu.Unlock()
                }
                time.Sleep(time.Second)
        }
    }
}
<|endoftext|>


<commit_before>//go:build go1.18
// +build go1.18

package identify

import (
	"fmt"
	"runtime/debug"
)

func init() {
	bi, ok := debug.ReadBuildInfo()
	if !ok {
		return
	}
	version := bi.Main.Version
	// version will only be non-empty if built as a dependency of another module
	if version == "" {
		return
	}

	if version != "(devel)" {
		defaultUserAgent = fmt.Sprintf("%s@%s", bi.Main.Path, bi.Main.Version)
		return
	}

	var revision string
	var dirty bool
	for _, bs := range bi.Settings {
		switch bs.Key {
		case "vcs.revision":
			revision = bs.Value
		case "vcs.modified":
			if bs.Value == "true" {
				dirty = true
			}
		}
	}
	defaultUserAgent = fmt.Sprintf("%s@%s", bi.Main.Path, revision)
	if dirty {
		defaultUserAgent += " (dirty)"
	}
}
<commit_msg>truncate the versoin hash after 9 characters, use -dirty suffix<commit_after>//go:build go1.18
// +build go1.18

package identify

import (
	"fmt"
	"runtime/debug"
)

func init() {
	bi, ok := debug.ReadBuildInfo()
	if !ok {
		return
	}
	version := bi.Main.Version
	// version will only be non-empty if built as a dependency of another module
	if version == "" {
		return
	}

	if version != "(devel)" {
		defaultUserAgent = fmt.Sprintf("%s@%s", bi.Main.Path, bi.Main.Version)
		return
	}

	var revision string
	var dirty bool
	for _, bs := range bi.Settings {
		switch bs.Key {
		case "vcs.revision":
			revision = bs.Value
			if len(revision) > 9 {
				revision = revision[:9]
			}
		case "vcs.modified":
			if bs.Value == "true" {
				dirty = true
			}
		}
	}
	defaultUserAgent = fmt.Sprintf("%s@%s", bi.Main.Path, revision)
	if dirty {
		defaultUserAgent += "-dirty"
	}
}
<|endoftext|>


<commit_before>package mathgl

import (
	"errors"
	"math"
)

func Orthod(left, right, bottom, top, near, far float64) Mat4d {
	rml, tmb, fmn := (right - left), (top - bottom), (far - near)

	return Mat4d{float64(2. / rml), 0, 0, 0, 0, float64(2. / tmb), 0, 0, 0, 0, float64(-2. / fmn), 0, float64(-(right + left) / rml), float64(-(top + bottom) / tmb), float64(-(far + near) / fmn), 1}
}

// Equivalent to Ortho with the near and far planes being -1 and 1, respectively
func Ortho2Dd(left, right, top, bottom float64) Mat4d {
	return Orthod(left, right, top, bottom, -1, 1)
}

func Perspectived(fovy, aspect, near, far float64) Mat4d {
	fovy = (fovy * math.Pi) / 180.0 // convert from degrees to radians
	nmf, f := near-far, float64(1./math.Tan(float64(fovy)/2.0))

	return Mat4d{float64(f / aspect), 0, 0, 0, 0, float64(f), 0, 0, 0, 0, float64((near + far) / nmf), -1, 0, 0, float64((2. * far * near) / nmf), 0}
}

func Frustumd(left, right, bottom, top, near, far float64) Mat4d {
	rml, tmb, fmn := (right - left), (top - bottom), (far - near)
	A, B, C, D := (right+left)/rml, (top+bottom)/tmb, -(far+near)/fmn, (2*far*near)/fmn

	return Mat4d{float64((2. * near) / rml), 0, 0, 0, 0, float64((2. * near) / tmb), 0, 0, float64(A), float64(B), float64(C), -1, 0, 0, float64(D), 0}
}

func LookAtd(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ float64) Mat4d {
	F := Vec3d{
		float64(centerX - eyeX),
		float64(centerY - eyeY),
		float64(centerZ - eyeZ)}

	f := F.Normalize()

	Up := Vec3d{
		float64(upX),
		float64(upY),
		float64(upZ)}

	Upp := Up.Normalize()

	s := f.Cross(Upp)
	u := s.Cross(f)

	M := Mat4d{s[0], u[0], -f[0], 0, s[1], u[1], -f[1], 0, s[2], u[2], -f[2], 0, 0, 0, 0, 1}

	return M.Mul4(Translate3Dd(-eyeX, -eyeY, -eyeZ))
}

func LookAtVd(eye, center, up Vec3d) Mat4d {
	F := center.Sub(eye)

	f := F.Normalize()

	Upp := up.Normalize()

	s := f.Cross(Upp)
	u := s.Cross(f)

	M := Mat4d{s[0], u[0], -f[0], 0, s[1], u[1], -f[1], 0, s[2], u[2], -f[2], 0, 0, 0, 0, 1}

	return M.Mul4(Translate3Dd(float64(-eye[0]), float64(-eye[1]), float64(-eye[2])))
}

// Transform a set of coordinates from object space (in obj) to window coordinates (with depth)
//
// Window coordinates are continuous, not discrete (well, as continuous as an IEEE Floating Point can be), so you won't get exact pixel locations
// without rounding or similar
func Projectd(obj Vec3d, modelview, projection Mat4d, initialX, initialY, width, height int) (win Vec3d) {
	obj4 := Vec4d{obj[0], obj[1], obj[2], 1.0}

	vpp := projection.Mul4(modelview).Mul4x1(obj4)
	win[0] = float64(initialX) + (float64(width)*(vpp[0]+1))/2
	win[1] = float64(initialY) + (float64(height)*(vpp[1]+1))/2
	win[2] = (vpp[2] + 1) / 2

	return win
}

// Transform a set of window coordinates to object space. If your MVP (projection.Mul(modelview) matrix is not invertible, this will return an error
//
// Note that the projection may not be perfect if you use strict pixel locations rather than the exact values given by Projectf.
// (It's still unlikely to be perfect due to precision errors, but it will be closer)
func UnProjectd(win Vec3d, modelview, projection Mat4d, initialX, initialY, width, height int) (obj Vec3d, err error) {
	inv := projection.Mul4(modelview).Inv()
	blank := Mat4d{}
	if inv == blank {
		return Vec3d{}, errors.New("Could not find matrix inverse (projection times modelview is probably non-singular)")
	}

	obj[0] = (2 * (win[0] - float64(initialX)) / float64(width)) - 1
	obj[1] = (2 * (win[1] - float64(initialY)) / float64(height)) - 1
	obj[2] = 2*win[2] - 1

	return obj, nil
}
<commit_msg>Applies previous changes to double version as well<commit_after>package mathgl

import (
	"errors"
	"math"
)

func Orthod(left, right, bottom, top, near, far float64) Mat4d {
	rml, tmb, fmn := (right - left), (top - bottom), (far - near)

	return Mat4d{float64(2. / rml), 0, 0, 0, 0, float64(2. / tmb), 0, 0, 0, 0, float64(-2. / fmn), 0, float64(-(right + left) / rml), float64(-(top + bottom) / tmb), float64(-(far + near) / fmn), 1}
}

// Equivalent to Ortho with the near and far planes being -1 and 1, respectively
func Ortho2Dd(left, right, top, bottom float64) Mat4d {
	return Orthod(left, right, top, bottom, -1, 1)
}

func Perspectived(fovy, aspect, near, far float64) Mat4d {
	fovy = (fovy * math.Pi) / 180.0 // convert from degrees to radians
	nmf, f := near-far, float64(1./math.Tan(float64(fovy)/2.0))

	return Mat4d{float64(f / aspect), 0, 0, 0, 0, float64(f), 0, 0, 0, 0, float64((near + far) / nmf), -1, 0, 0, float64((2. * far * near) / nmf), 0}
}

func Frustumd(left, right, bottom, top, near, far float64) Mat4d {
	rml, tmb, fmn := (right - left), (top - bottom), (far - near)
	A, B, C, D := (right+left)/rml, (top+bottom)/tmb, -(far+near)/fmn, (2*far*near)/fmn

	return Mat4d{float64((2. * near) / rml), 0, 0, 0, 0, float64((2. * near) / tmb), 0, 0, float64(A), float64(B), float64(C), -1, 0, 0, float64(D), 0}
}

func LookAtd(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ float64) Mat4d {
	F := Vec3d{
		float64(centerX - eyeX),
		float64(centerY - eyeY),
		float64(centerZ - eyeZ)}

	f := F.Normalize()

	Up := Vec3d{
		float64(upX),
		float64(upY),
		float64(upZ)}

	Upp := Up.Normalize()

	s := f.Cross(Upp).Normalize()
	u := s.Cross(f)

	M := Mat4d{s[0], u[0], -f[0], 0, s[1], u[1], -f[1], 0, s[2], u[2], -f[2], 0, 0, 0, 0, 1}

	return M.Mul4(Translate3Dd(-eyeX, -eyeY, -eyeZ))
}

func LookAtVd(eye, center, up Vec3d) Mat4d {
	F := center.Sub(eye)

	f := F.Normalize()

	Upp := up.Normalize()

	s := f.Cross(Upp)
	u := s.Cross(f)

	M := Mat4d{s[0], u[0], -f[0], 0, s[1], u[1], -f[1], 0, s[2], u[2], -f[2], 0, 0, 0, 0, 1}

	return M.Mul4(Translate3Dd(float64(-eye[0]), float64(-eye[1]), float64(-eye[2])))
}

// Transform a set of coordinates from object space (in obj) to window coordinates (with depth)
//
// Window coordinates are continuous, not discrete (well, as continuous as an IEEE Floating Point can be), so you won't get exact pixel locations
// without rounding or similar
func Projectd(obj Vec3d, modelview, projection Mat4d, initialX, initialY, width, height int) (win Vec3d) {
	obj4 := Vec4d{obj[0], obj[1], obj[2], 1.0}

	vpp := projection.Mul4(modelview).Mul4x1(obj4)
	win[0] = float64(initialX) + (float64(width)*(vpp[0]+1))/2
	win[1] = float64(initialY) + (float64(height)*(vpp[1]+1))/2
	win[2] = (vpp[2] + 1) / 2

	return win
}

// Transform a set of window coordinates to object space. If your MVP (projection.Mul(modelview) matrix is not invertible, this will return an error
//
// Note that the projection may not be perfect if you use strict pixel locations rather than the exact values given by Projectf.
// (It's still unlikely to be perfect due to precision errors, but it will be closer)
func UnProjectd(win Vec3d, modelview, projection Mat4d, initialX, initialY, width, height int) (obj Vec3d, err error) {
	inv := projection.Mul4(modelview).Inv()
	blank := Mat4d{}
	if inv == blank {
		return Vec3d{}, errors.New("Could not find matrix inverse (projection times modelview is probably non-singular)")
	}

	obj[0] = (2 * (win[0] - float64(initialX)) / float64(width)) - 1
	obj[1] = (2 * (win[1] - float64(initialY)) / float64(height)) - 1
	obj[2] = 2*win[2] - 1

	return obj, nil
}
<|endoftext|>


<commit_before>package nilness

import (
	"fmt"
	"go/token"
	"go/types"
	"reflect"

	"honnef.co/go/tools/go/ir"
	"honnef.co/go/tools/go/types/typeutil"
	"honnef.co/go/tools/internal/passes/buildir"

	"golang.org/x/tools/go/analysis"
)

// neverReturnsNilFact denotes that a function's return value will never
// be nil (typed or untyped). The analysis errs on the side of false
// negatives.
type neverReturnsNilFact struct {
	Rets []neverNilness
}

func (*neverReturnsNilFact) AFact() {}
func (fact *neverReturnsNilFact) String() string {
	return fmt.Sprintf("never returns nil: %v", fact.Rets)
}

type Result struct {
	m map[*types.Func][]neverNilness
}

var Analysis = &analysis.Analyzer{
	Name:       "nilness",
	Doc:        "Annotates return values that will never be nil (typed or untyped)",
	Run:        run,
	Requires:   []*analysis.Analyzer{buildir.Analyzer},
	FactTypes:  []analysis.Fact{(*neverReturnsNilFact)(nil)},
	ResultType: reflect.TypeOf((*Result)(nil)),
}

// MayReturnNil reports whether the ret's return value of fn might be
// a typed or untyped nil value. The value of ret is zero-based. When
// globalOnly is true, the only possible nil values are global
// variables.
//
// The analysis has false positives: MayReturnNil can incorrectly
// report true, but never incorrectly reports false.
func (r *Result) MayReturnNil(fn *types.Func, ret int) (yes bool, globalOnly bool) {
	if !typeutil.IsPointerLike(fn.Type().(*types.Signature).Results().At(ret).Type()) {
		return false, false
	}
	if len(r.m[fn]) == 0 {
		return true, false
	}

	v := r.m[fn][ret]
	return v != neverNil, v == onlyGlobal
}

func run(pass *analysis.Pass) (interface{}, error) {
	seen := map[*ir.Function]struct{}{}
	out := &Result{
		m: map[*types.Func][]neverNilness{},
	}
	for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
		impl(pass, fn, seen)
	}

	for _, fact := range pass.AllObjectFacts() {
		out.m[fact.Object.(*types.Func)] = fact.Fact.(*neverReturnsNilFact).Rets
	}

	return out, nil
}

type neverNilness uint8

const (
	neverNil   neverNilness = 1
	onlyGlobal neverNilness = 2
	nilly      neverNilness = 3
)

func (n neverNilness) String() string {
	switch n {
	case neverNil:
		return "never"
	case onlyGlobal:
		return "global"
	case nilly:
		return "nil"
	default:
		return "BUG"
	}
}

func impl(pass *analysis.Pass, fn *ir.Function, seenFns map[*ir.Function]struct{}) (out []neverNilness) {
	if fn.Object() == nil {
		// TODO(dh): support closures
		return nil
	}
	if fact := new(neverReturnsNilFact); pass.ImportObjectFact(fn.Object(), fact) {
		return fact.Rets
	}
	if fn.Pkg != pass.ResultOf[buildir.Analyzer].(*buildir.IR).Pkg {
		return nil
	}
	if fn.Blocks == nil {
		return nil
	}
	if _, ok := seenFns[fn]; ok {
		// break recursion
		return nil
	}

	seenFns[fn] = struct{}{}
	defer func() {
		for i, v := range out {
			if typeutil.IsPointerLike(fn.Signature.Results().At(i).Type()) && v != nilly {
				pass.ExportObjectFact(fn.Object(), &neverReturnsNilFact{out})
				break
			}
		}
	}()

	seen := map[ir.Value]struct{}{}

	var mightReturnNil func(v ir.Value) neverNilness
	mightReturnNil = func(v ir.Value) neverNilness {
		if _, ok := seen[v]; ok {
			// break cycle
			return nilly
		}
		if !typeutil.IsPointerLike(v.Type()) {
			return neverNil
		}
		seen[v] = struct{}{}
		switch v := v.(type) {
		case *ir.MakeInterface:
			return mightReturnNil(v.X)
		case *ir.Convert:
			return mightReturnNil(v.X)
		case *ir.Slice:
			return mightReturnNil(v.X)
		case *ir.Phi:
			ret := neverNil
			for _, e := range v.Edges {
				if n := mightReturnNil(e); n > ret {
					ret = n
				}
			}
			return ret
		case *ir.Extract:
			switch d := v.Tuple.(type) {
			case *ir.Call:
				if callee := d.Call.StaticCallee(); callee != nil {
					ret := impl(pass, callee, seenFns)
					if len(ret) == 0 {
						return nilly
					}
					return ret[v.Index]
				} else {
					return nilly
				}
			case *ir.TypeAssert, *ir.Next, *ir.Select, *ir.MapLookup, *ir.TypeSwitch, *ir.Recv:
				// we don't need to look at the Extract's index
				// because we've already checked its type.
				return nilly
			default:
				panic(fmt.Sprintf("internal error: unhandled type %T", d))
			}
		case *ir.Call:
			if callee := v.Call.StaticCallee(); callee != nil {
				ret := impl(pass, callee, seenFns)
				if len(ret) == 0 {
					return nilly
				}
				return ret[0]
			} else {
				return nilly
			}
		case *ir.BinOp, *ir.UnOp, *ir.Alloc, *ir.FieldAddr, *ir.IndexAddr, *ir.Global, *ir.MakeSlice, *ir.MakeClosure, *ir.Function, *ir.MakeMap, *ir.MakeChan:
			return neverNil
		case *ir.Sigma:
			iff, ok := v.From.Control().(*ir.If)
			if !ok {
				return nilly
			}
			binop, ok := iff.Cond.(*ir.BinOp)
			if !ok {
				return nilly
			}
			isNil := func(v ir.Value) bool {
				k, ok := v.(*ir.Const)
				if !ok {
					return false
				}
				return k.Value == nil
			}
			if binop.X == v.X && isNil(binop.Y) || binop.Y == v.X && isNil(binop.X) {
				op := binop.Op
				if v.From.Succs[0] != v.Block() {
					// we're in the false branch, negate op
					switch op {
					case token.EQL:
						op = token.NEQ
					case token.NEQ:
						op = token.EQL
					default:
						panic(fmt.Sprintf("internal error: unhandled token %v", op))
					}
				}
				switch op {
				case token.EQL:
					return nilly
				case token.NEQ:
					return neverNil
				default:
					panic(fmt.Sprintf("internal error: unhandled token %v", op))
				}
			}
			return nilly
		case *ir.ChangeType:
			return mightReturnNil(v.X)
		case *ir.Load:
			if _, ok := v.X.(*ir.Global); ok {
				return onlyGlobal
			}
			return nilly
		case *ir.TypeAssert, *ir.ChangeInterface, *ir.Field, *ir.Const, *ir.Index, *ir.MapLookup, *ir.Parameter, *ir.Recv, *ir.TypeSwitch:
			return nilly
		default:
			panic(fmt.Sprintf("internal error: unhandled type %T", v))
		}
	}
	ret := fn.Exit.Control().(*ir.Return)
	out = make([]neverNilness, len(ret.Results))
	for i, v := range ret.Results {
		out[i] = mightReturnNil(v)
	}
	return out
}
<commit_msg>nilness: avoid named return and defer<commit_after>package nilness

import (
	"fmt"
	"go/token"
	"go/types"
	"reflect"

	"honnef.co/go/tools/go/ir"
	"honnef.co/go/tools/go/types/typeutil"
	"honnef.co/go/tools/internal/passes/buildir"

	"golang.org/x/tools/go/analysis"
)

// neverReturnsNilFact denotes that a function's return value will never
// be nil (typed or untyped). The analysis errs on the side of false
// negatives.
type neverReturnsNilFact struct {
	Rets []neverNilness
}

func (*neverReturnsNilFact) AFact() {}
func (fact *neverReturnsNilFact) String() string {
	return fmt.Sprintf("never returns nil: %v", fact.Rets)
}

type Result struct {
	m map[*types.Func][]neverNilness
}

var Analysis = &analysis.Analyzer{
	Name:       "nilness",
	Doc:        "Annotates return values that will never be nil (typed or untyped)",
	Run:        run,
	Requires:   []*analysis.Analyzer{buildir.Analyzer},
	FactTypes:  []analysis.Fact{(*neverReturnsNilFact)(nil)},
	ResultType: reflect.TypeOf((*Result)(nil)),
}

// MayReturnNil reports whether the ret's return value of fn might be
// a typed or untyped nil value. The value of ret is zero-based. When
// globalOnly is true, the only possible nil values are global
// variables.
//
// The analysis has false positives: MayReturnNil can incorrectly
// report true, but never incorrectly reports false.
func (r *Result) MayReturnNil(fn *types.Func, ret int) (yes bool, globalOnly bool) {
	if !typeutil.IsPointerLike(fn.Type().(*types.Signature).Results().At(ret).Type()) {
		return false, false
	}
	if len(r.m[fn]) == 0 {
		return true, false
	}

	v := r.m[fn][ret]
	return v != neverNil, v == onlyGlobal
}

func run(pass *analysis.Pass) (interface{}, error) {
	seen := map[*ir.Function]struct{}{}
	out := &Result{
		m: map[*types.Func][]neverNilness{},
	}
	for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
		impl(pass, fn, seen)
	}

	for _, fact := range pass.AllObjectFacts() {
		out.m[fact.Object.(*types.Func)] = fact.Fact.(*neverReturnsNilFact).Rets
	}

	return out, nil
}

type neverNilness uint8

const (
	neverNil   neverNilness = 1
	onlyGlobal neverNilness = 2
	nilly      neverNilness = 3
)

func (n neverNilness) String() string {
	switch n {
	case neverNil:
		return "never"
	case onlyGlobal:
		return "global"
	case nilly:
		return "nil"
	default:
		return "BUG"
	}
}

func impl(pass *analysis.Pass, fn *ir.Function, seenFns map[*ir.Function]struct{}) []neverNilness {
	if fn.Object() == nil {
		// TODO(dh): support closures
		return nil
	}
	if fact := new(neverReturnsNilFact); pass.ImportObjectFact(fn.Object(), fact) {
		return fact.Rets
	}
	if fn.Pkg != pass.ResultOf[buildir.Analyzer].(*buildir.IR).Pkg {
		return nil
	}
	if fn.Blocks == nil {
		return nil
	}
	if _, ok := seenFns[fn]; ok {
		// break recursion
		return nil
	}

	seenFns[fn] = struct{}{}

	seen := map[ir.Value]struct{}{}

	var mightReturnNil func(v ir.Value) neverNilness
	mightReturnNil = func(v ir.Value) neverNilness {
		if _, ok := seen[v]; ok {
			// break cycle
			return nilly
		}
		if !typeutil.IsPointerLike(v.Type()) {
			return neverNil
		}
		seen[v] = struct{}{}
		switch v := v.(type) {
		case *ir.MakeInterface:
			return mightReturnNil(v.X)
		case *ir.Convert:
			return mightReturnNil(v.X)
		case *ir.Slice:
			return mightReturnNil(v.X)
		case *ir.Phi:
			ret := neverNil
			for _, e := range v.Edges {
				if n := mightReturnNil(e); n > ret {
					ret = n
				}
			}
			return ret
		case *ir.Extract:
			switch d := v.Tuple.(type) {
			case *ir.Call:
				if callee := d.Call.StaticCallee(); callee != nil {
					ret := impl(pass, callee, seenFns)
					if len(ret) == 0 {
						return nilly
					}
					return ret[v.Index]
				} else {
					return nilly
				}
			case *ir.TypeAssert, *ir.Next, *ir.Select, *ir.MapLookup, *ir.TypeSwitch, *ir.Recv:
				// we don't need to look at the Extract's index
				// because we've already checked its type.
				return nilly
			default:
				panic(fmt.Sprintf("internal error: unhandled type %T", d))
			}
		case *ir.Call:
			if callee := v.Call.StaticCallee(); callee != nil {
				ret := impl(pass, callee, seenFns)
				if len(ret) == 0 {
					return nilly
				}
				return ret[0]
			} else {
				return nilly
			}
		case *ir.BinOp, *ir.UnOp, *ir.Alloc, *ir.FieldAddr, *ir.IndexAddr, *ir.Global, *ir.MakeSlice, *ir.MakeClosure, *ir.Function, *ir.MakeMap, *ir.MakeChan:
			return neverNil
		case *ir.Sigma:
			iff, ok := v.From.Control().(*ir.If)
			if !ok {
				return nilly
			}
			binop, ok := iff.Cond.(*ir.BinOp)
			if !ok {
				return nilly
			}
			isNil := func(v ir.Value) bool {
				k, ok := v.(*ir.Const)
				if !ok {
					return false
				}
				return k.Value == nil
			}
			if binop.X == v.X && isNil(binop.Y) || binop.Y == v.X && isNil(binop.X) {
				op := binop.Op
				if v.From.Succs[0] != v.Block() {
					// we're in the false branch, negate op
					switch op {
					case token.EQL:
						op = token.NEQ
					case token.NEQ:
						op = token.EQL
					default:
						panic(fmt.Sprintf("internal error: unhandled token %v", op))
					}
				}
				switch op {
				case token.EQL:
					return nilly
				case token.NEQ:
					return neverNil
				default:
					panic(fmt.Sprintf("internal error: unhandled token %v", op))
				}
			}
			return nilly
		case *ir.ChangeType:
			return mightReturnNil(v.X)
		case *ir.Load:
			if _, ok := v.X.(*ir.Global); ok {
				return onlyGlobal
			}
			return nilly
		case *ir.TypeAssert, *ir.ChangeInterface, *ir.Field, *ir.Const, *ir.Index, *ir.MapLookup, *ir.Parameter, *ir.Recv, *ir.TypeSwitch:
			return nilly
		default:
			panic(fmt.Sprintf("internal error: unhandled type %T", v))
		}
	}
	ret := fn.Exit.Control().(*ir.Return)
	out := make([]neverNilness, len(ret.Results))
	export := false
	for i, v := range ret.Results {
		v := mightReturnNil(v)
		out[i] = v
		if v != nilly && typeutil.IsPointerLike(fn.Signature.Results().At(i).Type()) {
			export = true
		}
	}
	if export {
		pass.ExportObjectFact(fn.Object(), &neverReturnsNilFact{out})
	}
	return out
}
<|endoftext|>


<commit_before>// An implementation of the "Santa Claus problem" as defined in 'Beautiful
// concurrency', found here: http://research.microsoft.com/en-us/um/people/simonpj/papers/stm/beautiful.pdf
//
// The problem is given as:
//
//   Santa repeatedly sleeps until wakened by either all of his nine reindeer,
//   back from their holidays, or by a group of three of his ten elves. If
//   awakened by the reindeer, he harnesses each of them to his sleigh,
//   delivers toys with them and finally unharnesses them (allowing them to
//   go off on holiday). If awakened by a group of elves, he shows each of the
//   group into his study, consults with them on toy R&D and finally shows
//   them each out (allowing them to go back to work). Santa should give
//   priority to the reindeer in the case that there is both a group of elves
//   and a group of reindeer waiting.
//
// Here we follow the solution given in the paper, described as such:
//
//   Santa makes one "Group" for the elves and one for the reindeer. Each elf
//   (or reindeer) tries to join its Group. If it succeeds, it gets two
//   "Gates" in return. The first Gate allows Santa to control when the elf
//   can enter the study, and also lets Santa know when they are all inside.
//   Similarly, the second Gate controls the elves leaving the study. Santa,
//   for his part, waits for either of his two Groups to be ready, and then
//   uses that Group's Gates to marshal his helpers (elves or reindeer)
//   through their task. Thus the helpers spend their lives in an infinite
//   loop: try to join a group, move through the gates under Santa's control,
//   and then delay for a random interval before trying to join a group again.
//
// See the paper for more details regarding the solution's implementation.
package stm_test

import (
	"fmt"
	"math/rand"
	"time"

	"github.com/lukechampine/stm"
)

type gate struct {
	capacity  int
	remaining *stm.Var
}

func (g *gate) pass() {
	// decrement remaining capacity
	stm.Atomically(func(tx *stm.Tx) {
		rem := tx.Get(g.remaining).(int)
		tx.Assert(rem > 0)
		tx.Set(g.remaining, rem-1)
	})
}

func (g *gate) operate() {
	// open gate, reseting capacity
	stm.Atomically(func(tx *stm.Tx) {
		tx.Set(g.remaining, g.capacity)
	})
	// wait for gate to be full
	stm.Atomically(func(tx *stm.Tx) {
		rem := tx.Get(g.remaining).(int)
		tx.Assert(rem == 0)
	})
}

func newGate(capacity int) *gate {
	return &gate{
		capacity: capacity,
		// gate starts out closed
		remaining: stm.NewVar(0),
	}
}

type group struct {
	capacity     int
	remaining    *stm.Var
	gate1, gate2 *stm.Var
}

func newGroup(capacity int) *group {
	return &group{
		capacity: capacity,
		// group starts out with full capacity
		remaining: stm.NewVar(capacity),
		gate1:     stm.NewVar(newGate(capacity)),
		gate2:     stm.NewVar(newGate(capacity)),
	}
}

func (g *group) join() (g1, g2 *gate) {
	stm.Atomically(func(tx *stm.Tx) {
		rem := tx.Get(g.remaining).(int)
		g1 = tx.Get(g.gate1).(*gate)
		g2 = tx.Get(g.gate2).(*gate)
		tx.Assert(rem > 0)
		tx.Set(g.remaining, rem-1)
	})
	return
}

func (g *group) await(tx *stm.Tx) (g1, g2 *gate) {
	// wait for group to be empty
	rem := tx.Get(g.remaining).(int)
	tx.Assert(rem == 0)
	// return the group's gates
	g1 = tx.Get(g.gate1).(*gate)
	g2 = tx.Get(g.gate2).(*gate)
	// reset group
	tx.Set(g.remaining, g.capacity)
	tx.Set(g.gate1, newGate(g.capacity))
	tx.Set(g.gate2, newGate(g.capacity))
	return
}

func spawnElf(g *group, id int) {
	for {
		in, out := g.join()
		in.pass()
		fmt.Printf("Elf %v meeting in the study\n", id)
		out.pass()
		// sleep for a random interval <5s
		time.Sleep(time.Duration(rand.Intn(5000)) * time.Millisecond)
	}
}

func spawnReindeer(g *group, id int) {
	for {
		in, out := g.join()
		in.pass()
		fmt.Printf("Reindeer %v delivering toys\n", id)
		out.pass()
		// sleep for a random interval <5s
		time.Sleep(time.Duration(rand.Intn(5000)) * time.Millisecond)
	}
}

type selection struct {
	task         string
	gate1, gate2 *gate
}

func chooseGroup(g *group, task string, s *selection) func(*stm.Tx) {
	return func(tx *stm.Tx) {
		s.gate1, s.gate2 = g.await(tx)
		s.task = task
	}
}

func spawnSanta(elves, reindeer *group) {
	for {
		fmt.Println("-------------")
		var s selection
		stm.Atomically(stm.Select(
			// prefer reindeer to elves
			chooseGroup(reindeer, "deliver toys", &s),
			chooseGroup(elves, "meet in my study", &s),
		))
		fmt.Println("Ho! Ho! Ho! Let's " + s.task + "!")
		s.gate1.operate()
		// helpers do their work here...
		s.gate2.operate()
	}
}

func Example() {
	elfGroup := newGroup(3)
	for i := 0; i < 10; i++ {
		go spawnElf(elfGroup, i)
	}
	reinGroup := newGroup(9)
	for i := 0; i < 9; i++ {
		go spawnReindeer(reinGroup, i)
	}
	// blocks forever
	spawnSanta(elfGroup, reinGroup)
}
<commit_msg>tweak example<commit_after>// An implementation of the "Santa Claus problem" as defined in 'Beautiful
// concurrency', found here: http://research.microsoft.com/en-us/um/people/simonpj/papers/stm/beautiful.pdf
//
// The problem is given as:
//
//   Santa repeatedly sleeps until wakened by either all of his nine reindeer,
//   back from their holidays, or by a group of three of his ten elves. If
//   awakened by the reindeer, he harnesses each of them to his sleigh,
//   delivers toys with them and finally unharnesses them (allowing them to
//   go off on holiday). If awakened by a group of elves, he shows each of the
//   group into his study, consults with them on toy R&D and finally shows
//   them each out (allowing them to go back to work). Santa should give
//   priority to the reindeer in the case that there is both a group of elves
//   and a group of reindeer waiting.
//
// Here we follow the solution given in the paper, described as such:
//
//   Santa makes one "Group" for the elves and one for the reindeer. Each elf
//   (or reindeer) tries to join its Group. If it succeeds, it gets two
//   "Gates" in return. The first Gate allows Santa to control when the elf
//   can enter the study, and also lets Santa know when they are all inside.
//   Similarly, the second Gate controls the elves leaving the study. Santa,
//   for his part, waits for either of his two Groups to be ready, and then
//   uses that Group's Gates to marshal his helpers (elves or reindeer)
//   through their task. Thus the helpers spend their lives in an infinite
//   loop: try to join a group, move through the gates under Santa's control,
//   and then delay for a random interval before trying to join a group again.
//
// See the paper for more details regarding the solution's implementation.
package stm_test

import (
	"fmt"
	"math/rand"
	"time"

	"github.com/lukechampine/stm"
)

type gate struct {
	capacity  int
	remaining *stm.Var
}

func (g *gate) pass() {
	stm.Atomically(func(tx *stm.Tx) {
		rem := tx.Get(g.remaining).(int)
		// wait until gate can hold us
		tx.Assert(rem > 0)
		tx.Set(g.remaining, rem-1)
	})
}

func (g *gate) operate() {
	// open gate, reseting capacity
	stm.AtomicSet(g.remaining, g.capacity)
	// wait for gate to be full
	stm.Atomically(func(tx *stm.Tx) {
		rem := tx.Get(g.remaining).(int)
		tx.Assert(rem == 0)
	})
}

func newGate(capacity int) *gate {
	return &gate{
		capacity:  capacity,
		remaining: stm.NewVar(0), // gate starts out closed
	}
}

type group struct {
	capacity     int
	remaining    *stm.Var
	gate1, gate2 *stm.Var
}

func newGroup(capacity int) *group {
	return &group{
		capacity:  capacity,
		remaining: stm.NewVar(capacity), // group starts out with full capacity
		gate1:     stm.NewVar(newGate(capacity)),
		gate2:     stm.NewVar(newGate(capacity)),
	}
}

func (g *group) join() (g1, g2 *gate) {
	stm.Atomically(func(tx *stm.Tx) {
		rem := tx.Get(g.remaining).(int)
		// wait until the group can hold us
		tx.Assert(rem > 0)
		tx.Set(g.remaining, rem-1)
		// return the group's gates
		g1 = tx.Get(g.gate1).(*gate)
		g2 = tx.Get(g.gate2).(*gate)
	})
	return
}

func (g *group) await(tx *stm.Tx) (*gate, *gate) {
	// wait for group to be empty
	rem := tx.Get(g.remaining).(int)
	tx.Assert(rem == 0)
	// get the group's gates
	g1 := tx.Get(g.gate1).(*gate)
	g2 := tx.Get(g.gate2).(*gate)
	// reset group
	tx.Set(g.remaining, g.capacity)
	tx.Set(g.gate1, newGate(g.capacity))
	tx.Set(g.gate2, newGate(g.capacity))
	return g1, g2
}

func spawnElf(g *group, id int) {
	for {
		in, out := g.join()
		in.pass()
		fmt.Printf("Elf %v meeting in the study\n", id)
		out.pass()
		// sleep for a random interval <5s
		time.Sleep(time.Duration(rand.Intn(5000)) * time.Millisecond)
	}
}

func spawnReindeer(g *group, id int) {
	for {
		in, out := g.join()
		in.pass()
		fmt.Printf("Reindeer %v delivering toys\n", id)
		out.pass()
		// sleep for a random interval <5s
		time.Sleep(time.Duration(rand.Intn(5000)) * time.Millisecond)
	}
}

type selection struct {
	task         string
	gate1, gate2 *gate
}

func chooseGroup(g *group, task string, s *selection) func(*stm.Tx) {
	return func(tx *stm.Tx) {
		s.gate1, s.gate2 = g.await(tx)
		s.task = task
	}
}

func spawnSanta(elves, reindeer *group) {
	for {
		fmt.Println("-------------")
		var s selection
		stm.Atomically(stm.Select(
			// prefer reindeer to elves
			chooseGroup(reindeer, "deliver toys", &s),
			chooseGroup(elves, "meet in my study", &s),
		))
		fmt.Printf("Ho! Ho! Ho! Let's %s!\n", s.task)
		s.gate1.operate()
		// helpers do their work here...
		s.gate2.operate()
	}
}

func Example() {
	elfGroup := newGroup(3)
	for i := 0; i < 10; i++ {
		go spawnElf(elfGroup, i)
	}
	reinGroup := newGroup(9)
	for i := 0; i < 9; i++ {
		go spawnReindeer(reinGroup, i)
	}
	// blocks forever
	spawnSanta(elfGroup, reinGroup)
}
<|endoftext|>


<commit_before>package paxos

import "log"

func NewProposer(id int, val string, nt nodeNetwork, accetors ...int) *proposer {
	pro := proposer{id: id, proposeVal: val, seq: 0, nt: nt}
	pro.acceptors = make(map[int]message, len(accetors))
	log.Println("proposer has ", len(accetors), " acceptors, val:", pro.proposeVal)
	for _, acceptor := range accetors {
		pro.acceptors[acceptor] = message{}
	}
	return &pro
}

type proposer struct {
	id         int
	seq        int
	proposeNum int
	proposeVal string
	acceptors  map[int]message
	nt         nodeNetwork
}

//Detail process for Proposor.
func (p *proposer) run() {
	log.Println("Proposer start run... val:", p.proposeVal)
	//Stage1: Proposor send prepare message to acceptor to reach accept from majority.
	for !p.majorityReached() {
		log.Println("[Proposer:Prepare]")
		outMsgs := p.prepare()
		log.Println("[Proposer: prepare ", len(outMsgs), "msg")
		for _, msg := range outMsgs {
			p.nt.send(msg)
			log.Println("[Proposer: send", msg)
		}

		log.Println("[Proposer: prepare recev..")
		m := p.nt.recev()
		for m == nil {
			log.Println("[Proposer: no msg... ")
			continue
		}
		log.Println("[Proposer: recev", m)
		switch m.typ {
		case Promise:
			log.Println(" proposer recev a promise from ", m.from)
			p.checkRecvPromise(*m)
		default:
			panic("Unsupport message.")
		}
	}

	log.Println("[Proposer:Propose]")
	//Stage2: Proposor send propose value to acceptor to learn.
	log.Println("Proposor propose seq:", p.getProposeNum(), " value:", p.proposeVal)
	proposeMsgs := p.propose()
	for _, msg := range proposeMsgs {
		p.nt.send(msg)
	}
}

// After receipt the promise from acceptor and reach majority.
// Proposor will propose value to those accpetors and let them know the consusence alreay ready.
func (p *proposer) propose() []message {
	sendMsgCount := 0
	var msgList []message
	log.Println("proposer: propose msg:", len(p.acceptors))
	for acepId, acepMsg := range p.acceptors {
		log.Println("check promise id:", acepMsg.getProposeSeq(), p.getProposeNum())
		if acepMsg.getProposeSeq() == p.getProposeNum() {
			msg := message{from: p.id, to: acepId, typ: Propose, seq: p.getProposeNum()}
			msg.val = p.proposeVal
			log.Println("Propose val:", msg.val)
			msgList = append(msgList, msg)
		}
		sendMsgCount++
		if sendMsgCount > p.majority() {
			break
		}
	}
	log.Println(" proposer propose msg list:", msgList)
	return msgList
}

// Stage 1:
// Prepare will prepare message to send to majority of acceptors.
// According to spec, we only send our prepare msg to the "majority" not all acceptors.
func (p *proposer) prepare() []message {
	p.seq++

	sendMsgCount := 0
	var msgList []message
	log.Println("proposer: prepare major msg:", len(p.acceptors))
	for acepId, _ := range p.acceptors {
		msg := message{from: p.id, to: acepId, typ: Prepare, seq: p.getProposeNum(), val: p.proposeVal}
		msgList = append(msgList, msg)
		sendMsgCount++
		if sendMsgCount > p.majority() {
			break
		}
	}
	return msgList
}

func (p *proposer) checkRecvPromise(promise message) {
	previousPromise := p.acceptors[promise.from]
	log.Println(" prevMsg:", previousPromise, " promiseMsg:", promise)
	log.Println(previousPromise.getProposeSeq(), promise.getProposeSeq())
	if previousPromise.getProposeSeq() < promise.getProposeSeq() {
		log.Println("Proposor:", p.id, " get new promise:", promise)
		p.acceptors[promise.from] = promise
		if promise.getProposeSeq() > p.getProposeNum() {
			p.proposeNum = promise.getProposeSeq()
			p.proposeVal = promise.getProposeVal()
		}
	}
}

func (p *proposer) majority() int {
	return len(p.acceptors)/2 + 1
}

func (p *proposer) getRecevPromiseCount() int {
	recvCount := 0
	for _, acepMsg := range p.acceptors {
		log.Println(" proposer has total ", len(p.acceptors), " acceptor ", acepMsg, " current Num:", p.getProposeNum(), " msgNum:", acepMsg.getProposeSeq())
		if acepMsg.getProposeSeq() == p.getProposeNum() {
			log.Println("recv ++", recvCount)
			recvCount++
		}
	}
	log.Println("Current proposer recev promise count=", recvCount)
	return recvCount
}

func (p *proposer) majorityReached() bool {
	return p.getRecevPromiseCount() > p.majority()
}

func (p *proposer) getProposeNum() int {
	p.proposeNum = p.seq<<4 | p.id
	return p.proposeNum
}
<commit_msg>Fixed #1: avoid infinite loop<commit_after>package paxos

import "log"

func NewProposer(id int, val string, nt nodeNetwork, accetors ...int) *proposer {
	pro := proposer{id: id, proposeVal: val, seq: 0, nt: nt}
	pro.acceptors = make(map[int]message, len(accetors))
	log.Println("proposer has ", len(accetors), " acceptors, val:", pro.proposeVal)
	for _, acceptor := range accetors {
		pro.acceptors[acceptor] = message{}
	}
	return &pro
}

type proposer struct {
	id         int
	seq        int
	proposeNum int
	proposeVal string
	acceptors  map[int]message
	nt         nodeNetwork
}

//Detail process for Proposor.
func (p *proposer) run() {
	log.Println("Proposer start run... val:", p.proposeVal)
	//Stage1: Proposor send prepare message to acceptor to reach accept from majority.
	for !p.majorityReached() {
		log.Println("[Proposer:Prepare]")
		outMsgs := p.prepare()
		log.Println("[Proposer: prepare ", len(outMsgs), "msg")
		for _, msg := range outMsgs {
			p.nt.send(msg)
			log.Println("[Proposer: send", msg)
		}

		log.Println("[Proposer: prepare recev..")
		m := p.nt.recev()
		if m == nil {
			log.Println("[Proposer: no msg... ")
			continue
		}
		log.Println("[Proposer: recev", m)
		switch m.typ {
		case Promise:
			log.Println(" proposer recev a promise from ", m.from)
			p.checkRecvPromise(*m)
		default:
			panic("Unsupport message.")
		}
	}

	log.Println("[Proposer:Propose]")
	//Stage2: Proposor send propose value to acceptor to learn.
	log.Println("Proposor propose seq:", p.getProposeNum(), " value:", p.proposeVal)
	proposeMsgs := p.propose()
	for _, msg := range proposeMsgs {
		p.nt.send(msg)
	}
}

// After receipt the promise from acceptor and reach majority.
// Proposor will propose value to those accpetors and let them know the consusence alreay ready.
func (p *proposer) propose() []message {
	sendMsgCount := 0
	var msgList []message
	log.Println("proposer: propose msg:", len(p.acceptors))
	for acepId, acepMsg := range p.acceptors {
		log.Println("check promise id:", acepMsg.getProposeSeq(), p.getProposeNum())
		if acepMsg.getProposeSeq() == p.getProposeNum() {
			msg := message{from: p.id, to: acepId, typ: Propose, seq: p.getProposeNum()}
			msg.val = p.proposeVal
			log.Println("Propose val:", msg.val)
			msgList = append(msgList, msg)
		}
		sendMsgCount++
		if sendMsgCount > p.majority() {
			break
		}
	}
	log.Println(" proposer propose msg list:", msgList)
	return msgList
}

// Stage 1:
// Prepare will prepare message to send to majority of acceptors.
// According to spec, we only send our prepare msg to the "majority" not all acceptors.
func (p *proposer) prepare() []message {
	p.seq++

	sendMsgCount := 0
	var msgList []message
	log.Println("proposer: prepare major msg:", len(p.acceptors))
	for acepId, _ := range p.acceptors {
		msg := message{from: p.id, to: acepId, typ: Prepare, seq: p.getProposeNum(), val: p.proposeVal}
		msgList = append(msgList, msg)
		sendMsgCount++
		if sendMsgCount > p.majority() {
			break
		}
	}
	return msgList
}

func (p *proposer) checkRecvPromise(promise message) {
	previousPromise := p.acceptors[promise.from]
	log.Println(" prevMsg:", previousPromise, " promiseMsg:", promise)
	log.Println(previousPromise.getProposeSeq(), promise.getProposeSeq())
	if previousPromise.getProposeSeq() < promise.getProposeSeq() {
		log.Println("Proposor:", p.id, " get new promise:", promise)
		p.acceptors[promise.from] = promise
		if promise.getProposeSeq() > p.getProposeNum() {
			p.proposeNum = promise.getProposeSeq()
			p.proposeVal = promise.getProposeVal()
		}
	}
}

func (p *proposer) majority() int {
	return len(p.acceptors)/2 + 1
}

func (p *proposer) getRecevPromiseCount() int {
	recvCount := 0
	for _, acepMsg := range p.acceptors {
		log.Println(" proposer has total ", len(p.acceptors), " acceptor ", acepMsg, " current Num:", p.getProposeNum(), " msgNum:", acepMsg.getProposeSeq())
		if acepMsg.getProposeSeq() == p.getProposeNum() {
			log.Println("recv ++", recvCount)
			recvCount++
		}
	}
	log.Println("Current proposer recev promise count=", recvCount)
	return recvCount
}

func (p *proposer) majorityReached() bool {
	return p.getRecevPromiseCount() > p.majority()
}

func (p *proposer) getProposeNum() int {
	p.proposeNum = p.seq<<4 | p.id
	return p.proposeNum
}
<|endoftext|>


<commit_before>package elasticthought

import (
	"io"
	"os"
)

type FileSystemBlobStore struct {

	// the root path where blobs will be stored.  all paths will
	// be calculated relative to this path.
	rootPath string
}

func NewFileSystemBlobStore(rootPath string) (*FileSystemBlobStore, error) {

	// create rootPath if it doesn't exist
	err := os.MkdirAll(rootPath, os.FileMode(0755))
	if err != nil {
		return nil, err
	}

	fsBlobStore := &FileSystemBlobStore{
		rootPath: rootPath,
	}

	return fsBlobStore, nil

}

func (f *FileSystemBlobStore) Get(path string) (io.ReadCloser, error) {
	return nil, nil
}

func (f *FileSystemBlobStore) Put(srcname, dest string, r io.Reader, opts BlobPutOptions) error {
	return nil
}

func (f *FileSystemBlobStore) Rm(fn string) error {
	return nil
}

func (f *FileSystemBlobStore) OpenFile(path string) (BlobHandle, error) {
	return nil, nil
}
<commit_msg>implement FileSystemBlobStore<commit_after>package elasticthought

import (
	"io"
	"os"
	"path/filepath"
	"time"
)

type FileSystemBlobStore struct {

	// the root path where blobs will be stored.  all paths will
	// be calculated relative to this path.
	rootPath string
}

type FileSystemBlobHandle struct{}

func (h FileSystemBlobHandle) Nodes() map[string]time.Time {
	return map[string]time.Time{
		"filesystem": time.Now(),
	}
}

func NewFileSystemBlobStore(rootPath string) (*FileSystemBlobStore, error) {

	// create rootPath if it doesn't exist
	err := os.MkdirAll(rootPath, os.FileMode(0755))
	if err != nil {
		return nil, err
	}

	fsBlobStore := &FileSystemBlobStore{
		rootPath: rootPath,
	}

	return fsBlobStore, nil

}

func (f FileSystemBlobStore) Get(path string) (io.ReadCloser, error) {

	// build full path to file
	path = f.absolutePath(path)

	// open the file for reading and return
	return os.Open(path)

}

func (f FileSystemBlobStore) Put(srcname, dest string, r io.Reader, opts BlobPutOptions) error {

	// build full path to file
	path := f.absolutePath(dest)

	// open the file for writing
	file, err := os.Create(path)
	if err != nil {
		return err
	}

	// copy from reader -> file
	_, err = io.Copy(file, r)

	return err
}

func (f FileSystemBlobStore) Rm(path string) error {

	// build full path to file
	path = f.absolutePath(path)

	return os.Remove(path)

}

func (f FileSystemBlobStore) OpenFile(path string) (BlobHandle, error) {
	return FileSystemBlobHandle{}, nil
}

func (f FileSystemBlobStore) absolutePath(relativePath string) string {
	return filepath.Join(f.rootPath, relativePath)
}
<|endoftext|>


<commit_before>package cod

import (
	"database/sql"
  "fmt"
	"github.com/adabei/goldenbot/events"
	"github.com/adabei/goldenbot/events/cod"
  integrated "github.com/adabei/goldenbot-integrated/cod"
  rcon "github.com/adabei/goldenbot/rcon"
	"log"
  "strings"
	"time"
)

type Stats struct {
  requests chan rcon.RCONQuery
	events chan interface{}
	db     *sql.DB
}

func NewStats(requests chan rcon.RCONQuery, ea events.Aggregator, db *sql.DB) *Stats {
	s := new(Stats)
  s.requests = requests
	s.events = ea.Subscribe(s)
	s.db = db
	return s
}

const gamesSchema = `
create table games (
  started_at text primary key,
  ended_at text,
  mapname text
);`

const statsSchema = `
create table stats (
  games_started_at text,
  players_id text,
  kills integer,
  deaths integer,
  assists integer,
  primary key(games_started_at, players_id)
);`

func (s *Stats) Setup() error {
	_, err := s.db.Exec(gamesSchema)
	if err != nil {
		log.Println(err)
		return err
	}

	_, err = s.db.Exec(statsSchema)
	if err != nil {
		log.Println(err)
	}
	return err
}

type playerStats struct {
	Kills   int
	Deaths  int
	Assists int
}

func (s *Stats) Start() {
	currentStats := make(map[string]*playerStats)
	currentStartedAt := time.Now().Unix()
	var currentMap string
	for {
		ev := <-s.events
		switch ev := ev.(type) {
		case cod.InitGame:
			currentStats = make(map[string]*playerStats)
			currentStartedAt = ev.Unix
			currentMap = "mp_backlot" // TODO extract from initgame
		case cod.ExitLevel:
			if len(currentStats) > 0 {
				// write to db
				for k, v := range currentStats {
					log.Println("stats: inserting game", currentStartedAt, "into database")
					_, err := s.db.Exec("insert into games(started_at, ended_at, mapname) values (?, ?, ?);", currentStartedAt, ev.Unix, currentMap)
					if err != nil {
						log.Fatal("stats: failed to insert games", err)
					}

					log.Println("stats: inserting stats for player", k, "into database")
					_, err = s.db.Exec("insert into stats(games_started_at, players_id, kills, deaths, assists) values(?, ?, ?, ?, ?);", currentStartedAt, k, v.Kills, v.Deaths, v.Assists)

					if err != nil {
						log.Fatal("stats: failed to insert stats for player", k, err)
					}
				}
			}
		case cod.ShutdownGame:
			// shutdowngame vs exitlevel?
		case cod.Kill:
			// TODO suicide
			if s, ok := currentStats[ev.GUIDA]; ok {
				s.Kills = s.Kills + 1
			} else {
				s = &playerStats{Kills: 1, Deaths: 0, Assists: 0}
				currentStats[ev.GUIDA] = s
			}

			if r, ok := currentStats[ev.GUIDB]; ok {
				r.Deaths = r.Deaths + 1
			} else {
				r := &playerStats{Kills: 0, Deaths: 1, Assists: 0}
				currentStats[ev.GUIDB] = r
			}

		case cod.Damage:
			// not yet implemented (used for assists)
    case cod.Say:
      if strings.HasPrefix(ev.Message, "!stats") {
        var kills int
        var deaths int
        var assists int
        log.Println("stats: calculating stats for player", ev.GUID)
        err := s.db.QueryRow("select sum(s.kills), sum(s.deaths), sum(s.assists) " +
          "from stats s where s.players_id = ?", ev.GUID).Scan(&kills, &deaths, &assists)
        if err != nil {
          log.Println("stats: could not sum up stats for player", ev.GUID)
        }
        num, _ := integrated.Num(ev.GUID)
        s.requests <- rcon.RCONQuery{Command: "tell " + string(num) + " " +
          fmt.Sprintf("Kills: %d Deaths: %d Assists: %d", kills, deaths, assists),
          Response: nil}
      }
		}
	}
}
<commit_msg>updates to new integrated.Num api<commit_after>package cod

import (
	"database/sql"
	"fmt"
	integrated "github.com/adabei/goldenbot-integrated/cod"
	"github.com/adabei/goldenbot/events"
	"github.com/adabei/goldenbot/events/cod"
	rcon "github.com/adabei/goldenbot/rcon"
	"log"
	"strconv"
	"strings"
	"time"
)

type Stats struct {
	requests chan rcon.RCONQuery
	events   chan interface{}
	db       *sql.DB
}

func NewStats(requests chan rcon.RCONQuery, ea events.Aggregator, db *sql.DB) *Stats {
	s := new(Stats)
	s.requests = requests
	s.events = ea.Subscribe(s)
	s.db = db
	return s
}

const gamesSchema = `
create table games (
  started_at text primary key,
  ended_at text,
  mapname text
);`

const statsSchema = `
create table stats (
  games_started_at text,
  players_id text,
  kills integer,
  deaths integer,
  assists integer,
  primary key(games_started_at, players_id)
);`

func (s *Stats) Setup() error {
	_, err := s.db.Exec(gamesSchema)
	if err != nil {
		log.Println(err)
		return err
	}

	_, err = s.db.Exec(statsSchema)
	if err != nil {
		log.Println(err)
	}
	return err
}

type playerStats struct {
	Kills   int
	Deaths  int
	Assists int
}

func (s *Stats) Start() {
	currentStats := make(map[string]*playerStats)
	currentStartedAt := time.Now().Unix()
	var currentMap string
	for {
		ev := <-s.events
		switch ev := ev.(type) {
		case cod.InitGame:
			currentStats = make(map[string]*playerStats)
			currentStartedAt = ev.Unix
			currentMap = "mp_backlot" // TODO extract from initgame
		case cod.ExitLevel:
			if len(currentStats) > 0 {
				// write to db
				for k, v := range currentStats {
					log.Println("stats: inserting game", currentStartedAt, "into database")
					_, err := s.db.Exec("insert into games(started_at, ended_at, mapname) values (?, ?, ?);", currentStartedAt, ev.Unix, currentMap)
					if err != nil {
						log.Fatal("stats: failed to insert games", err)
					}

					log.Println("stats: inserting stats for player", k, "into database")
					_, err = s.db.Exec("insert into stats(games_started_at, players_id, kills, deaths, assists) values(?, ?, ?, ?, ?);", currentStartedAt, k, v.Kills, v.Deaths, v.Assists)

					if err != nil {
						log.Fatal("stats: failed to insert stats for player", k, err)
					}
				}
			}
		case cod.ShutdownGame:
			// shutdowngame vs exitlevel?
		case cod.Kill:
			// TODO suicide
			if s, ok := currentStats[ev.GUIDA]; ok {
				s.Kills = s.Kills + 1
			} else {
				s = &playerStats{Kills: 1, Deaths: 0, Assists: 0}
				currentStats[ev.GUIDA] = s
			}

			if r, ok := currentStats[ev.GUIDB]; ok {
				r.Deaths = r.Deaths + 1
			} else {
				r := &playerStats{Kills: 0, Deaths: 1, Assists: 0}
				currentStats[ev.GUIDB] = r
			}

		case cod.Damage:
			// not yet implemented (used for assists)
		case cod.Say:
			if strings.HasPrefix(ev.Message, "!stats") {
				var kills int
				var deaths int
				var assists int
				log.Println("stats: calculating stats for player", ev.GUID)
				err := s.db.QueryRow("select sum(s.kills), sum(s.deaths), sum(s.assists) "+
					"from stats s where s.players_id = ?", ev.GUID).Scan(&kills, &deaths, &assists)
				if err != nil {
					log.Println("stats: could not sum up stats for player", ev.GUID)
				}

				if num, ok := integrated.Num(ev.GUID); ok {
					log.Println("stats: showing stats to player with guid", ev.GUID, "and num", num)
					s.requests <- rcon.RCONQuery{Command: "tell " + strconv.Itoa(num) + " " +
						fmt.Sprintf("Kills: %d Deaths: %d Assists: %d", kills, deaths, assists),
						Response: nil}
				} else {
					log.Println("stats: could not resolve num for player", ev.GUID)
				}

			}
		}
	}
}
<|endoftext|>