// This is a backported implementation of sync.Pool from gotip pre-1.3
// http://tip.golang.org/src/pkg/sync/pool.go?m=text
//
// Extended to drain pool periodically.

// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package pool

import (
	"sync"
	"time"
)

// A Pool is a set of temporary objects that may be individually saved
// and retrieved.
//
// Any item stored in the Pool may be removed automatically by the
// implementation at any time without notification.
// If the Pool holds the only reference when this happens, the item
// might be deallocated.
//
// A Pool is safe for use by multiple goroutines simultaneously.
//
// Pool's intended use is for free lists maintained in global variables,
// typically accessed by multiple goroutines simultaneously. Using a
// Pool instead of a custom free list allows the runtime to reclaim
// entries from the pool when it makes sense to do so. An
// appropriate use of sync.Pool is to create a pool of temporary buffers
// shared between independent clients of a global resource. On the
// other hand, if a free list is maintained as part of an object used
// only by a single client and freed when the client completes,
// implementing that free list as a Pool is not appropriate.
//
// This is an experimental type and might not be released.
type Pool struct {
	next *Pool         // for use by runtime. must be first.
	list []interface{} // offset known to runtime
	mu   sync.Mutex    // guards list

	// New optionally specifies a function to generate
	// a value when Get would otherwise return nil.
	// It may not be changed concurrently with calls to Get.
	New func() interface{}

	DrainPeriod time.Duration // Period between drains
	drainTicker *time.Ticker  // ticker used by the drainer goroutine
}

// Put adds x to the pool.
func (p *Pool) Put(x interface{}) {
	if x == nil {
		return
	}
	p.mu.Lock()
	if p.list == nil {
		if p.drainTicker == nil && p.DrainPeriod != 0 {
			p.drainTicker = time.NewTicker(p.DrainPeriod)
			go func() {
				for _ = range p.drainTicker.C {
					p.Drain()
				}
			}()
		}
	}
	p.list = append(p.list, x)
	p.mu.Unlock()
}

// Get selects an arbitrary item from the Pool, removes it from the
// Pool, and returns it to the caller.
// Get may choose to ignore the pool and treat it as empty.
// Callers should not assume any relation between values passed to Put and
// the values returned by Get.
//
// If Get would otherwise return nil and p.New is non-nil, Get returns
// the result of calling p.New.
func (p *Pool) Get() interface{} {
	p.mu.Lock()
	var x interface{}
	if n := len(p.list); n > 0 {
		x = p.list[n-1]
		p.list[n-1] = nil // Just to be safe
		p.list = p.list[:n-1]
	}
	p.mu.Unlock()
	if x == nil && p.New != nil {
		x = p.New()
	}
	return x
}

func (p *Pool) Drain() {
	p.mu.Lock()
	p.list = make([]interface{}, 0, cap(p.list)/2)
	p.mu.Unlock()
}

func (p *Pool) Close() {
	p.Drain()
	p.mu.Lock()
	p.list = make([]interface{}, 0)
	if p.drainTicker != nil {
		p.drainTicker.Stop()
		p.drainTicker = nil
	}
	p.mu.Unlock()
}
