<commit_before>package lib

import (
	"log"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/fsnotify/fsnotify"
)

// 监听器实例
var watcher *fsnotify.Watcher

// 添加监听
func addWatch(path string) {
	log.Println("Watching path: ", path)
	err := watcher.Add(path)
	if err != nil {
		log.Printf("Watch "+path+" error: %v\n", err)
	}
}

// 删除监听
func removeWatch(path string) {
	log.Println("Remove watch path: ", path)
	err := watcher.Remove(path)
	if err != nil {
		// log.Printf("Remove watch "+path+" error: %v\n", err)
	}
}

// 是否需要处理资源
var needHandleRes = false

// 资源处理器
func startResHandler() {
	go func() {
		for {
			if needHandleRes {
				needHandleRes = false
				log.Println("Handling res...")
				HandleImages()
				log.Println("Handle res done")
			}
			time.Sleep(time.Second * 1)
		}
	}()
}

// 是否需要构建
var needBuild = false

// 已构建次数
var buildCount = 0

// 构建处理器
func startBuildHandler() {
	go func() {
		for {
			if needBuild {
				needBuild = false
				buildCount++
				log.Println("Building...", buildCount)
				Build()
				log.Println("Build done")
			}
			time.Sleep(time.Second * 1)
		}
	}()
}

// 处理新建监听
func watchCreate(path string) {
	file, err := os.Stat(path)
	if err != nil {
		return
	}
	srcPath := "src"
	resPath := Config.ResourceDir
	if file.IsDir() {
		// 建立目录
		addWatch(path)
	} else if resPath != "" && strings.HasPrefix(path, resPath) {
		// 在资源目录中建立文件
		needHandleRes = true
	} else if strings.HasPrefix(path, srcPath) {
		// 在源码目录中建立文件
		needBuild = true
	}
}

// 处理删除监听
func watchRemove(path string) {
	file, err := os.Stat(path)
	if err != nil {
		return
	}
	srcPath := "src"
	resPath := Config.ResourceDir
	if file.IsDir() {
		// 删除目录
		removeWatch(path)
	} else if resPath != "" && strings.HasPrefix(path, resPath) {
		// 在资源目录中删除文件
		needHandleRes = true
	} else if strings.HasPrefix(path, srcPath) {
		// 在源码目录中删除文件
		needBuild = true
	}
}

// 处理重命名监听
func watchRename(path string) {
	file, err := os.Stat(path)
	if err != nil {
		return
	}
	srcPath := "src"
	resPath := Config.ResourceDir
	if file.IsDir() {
		// 修改目录名称
		addWatch(path)
	} else if resPath != "" && strings.HasPrefix(path, resPath) {
		// 在资源目录中修改文件名
		needHandleRes = true
	} else if strings.HasPrefix(path, srcPath) {
		// 在源码目录中修改文件名
		needBuild = true
	}
}

// 处理修改监听
func watchWrite(path string) {
	srcPath := "src"
	if strings.HasPrefix(path, srcPath) {
		// 源码修改
		needBuild = true
	}
}

// 处理监听
func handleWatch(event fsnotify.Event) {
	path := strings.Replace(event.Name, "\\", "/", -1)
	if strings.Contains(path, configFilename) {
		// 配置文件改变
		needBuild = true
	} else if event.Op&fsnotify.Create == fsnotify.Create {
		watchCreate(path)
	} else if event.Op&fsnotify.Remove == fsnotify.Remove {
		watchRemove(path)
	} else if event.Op&fsnotify.Rename == fsnotify.Rename {
		watchRename(path)
	} else if event.Op&fsnotify.Write == fsnotify.Write {
		watchWrite(path)
	}
}

// Watch 开始监听
func Watch() {
	// 先读取配置文件
	ReadConfig()

	w, err := fsnotify.NewWatcher()
	if err != nil {
		log.Printf("Watch new error: %v\n", err)
		return
	}
	watcher = w
	// defer watcher.Close()

	// done := make(chan bool)
	go func() {
		for {
			select {
			case event := <-watcher.Events:
				if event.Name != "" {
					// log.Println("Watch event:", event)
					handleWatch(event)
				}
			case err := <-watcher.Errors:
				if err != nil {
					log.Printf("Watch error: %v\n", err)
				}
			}
		}
	}()

	// 监听的目录列表，包含子目录
	list := []string{"src"}
	if Config.ResourceDir != "" {
		list = append(list, Config.ResourceDir)
	}
	// log.Printf("%v\n", list)
	var allList []string
	for i := 0; i < len(list); i++ {
		item := list[i]
		filepath.Walk(item, func(path1 string, f os.FileInfo, err1 error) error {
			if f == nil {
				return err1
			}
			if f.IsDir() {
				allList = append(allList, path1)
				return nil
			}
			return nil
		})
	}
	// 添加配置文件监听
	allList = append(allList, configFilename)
	for i := 0; i < len(allList); i++ {
		item := allList[i]
		addWatch(item)
	}
	// 开启各个处理器
	startBuildHandler()
	startResHandler()

	// <-done
}
<commit_msg>add building in period<commit_after>package lib

import (
	"log"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/fsnotify/fsnotify"
)

// 监听器实例
var watcher *fsnotify.Watcher

// 添加监听
func addWatch(path string) {
	log.Println("Watching path: ", path)
	err := watcher.Add(path)
	if err != nil {
		log.Printf("Watch "+path+" error: %v\n", err)
	}
}

// 删除监听
func removeWatch(path string) {
	log.Println("Remove watch path: ", path)
	err := watcher.Remove(path)
	if err != nil {
		// log.Printf("Remove watch "+path+" error: %v\n", err)
	}
}

// 是否需要处理资源
var needHandleRes = false

// 资源处理器
func startResHandler() {
	go func() {
		for {
			if needHandleRes {
				needHandleRes = false
				log.Println("Handling res...")
				HandleImages()
				log.Println("Handle res done")
			}
			time.Sleep(time.Second * 1)
		}
	}()
}

// 是否需要构建
var needBuild = false

// 已构建次数
var buildCount = 0

// 构建处理器
func startBuildHandler() {
	go func() {
		for {
			if needBuild {
				needBuild = false
				buildCount++
				log.Println("Building...", buildCount)
				Build()
				log.Println("Build done")
				startTick()
			}
			time.Sleep(time.Second * 1)
		}
	}()
}

// 计时器，每隔一分钟，如果没有动静则会构建一次
var timer *time.Timer

func startTick() {
	if timer != nil {
		timer.Stop()
	}
	timer = time.NewTimer(time.Second * 60)
	go func() {
		<-timer.C
		needBuild = true
		log.Println("Building in period")
	}()
}

// 处理新建监听
func watchCreate(path string) {
	file, err := os.Stat(path)
	if err != nil {
		return
	}
	srcPath := "src"
	resPath := Config.ResourceDir
	if file.IsDir() {
		// 建立目录
		addWatch(path)
	} else if resPath != "" && strings.HasPrefix(path, resPath) {
		// 在资源目录中建立文件
		needHandleRes = true
	} else if strings.HasPrefix(path, srcPath) {
		// 在源码目录中建立文件
		needBuild = true
	}
}

// 处理删除监听
func watchRemove(path string) {
	file, err := os.Stat(path)
	if err != nil {
		return
	}
	srcPath := "src"
	resPath := Config.ResourceDir
	if file.IsDir() {
		// 删除目录
		removeWatch(path)
	} else if resPath != "" && strings.HasPrefix(path, resPath) {
		// 在资源目录中删除文件
		needHandleRes = true
	} else if strings.HasPrefix(path, srcPath) {
		// 在源码目录中删除文件
		needBuild = true
	}
}

// 处理重命名监听
func watchRename(path string) {
	file, err := os.Stat(path)
	if err != nil {
		return
	}
	srcPath := "src"
	resPath := Config.ResourceDir
	if file.IsDir() {
		// 修改目录名称
		addWatch(path)
	} else if resPath != "" && strings.HasPrefix(path, resPath) {
		// 在资源目录中修改文件名
		needHandleRes = true
	} else if strings.HasPrefix(path, srcPath) {
		// 在源码目录中修改文件名
		needBuild = true
	}
}

// 处理修改监听
func watchWrite(path string) {
	srcPath := "src"
	if strings.HasPrefix(path, srcPath) {
		// 源码修改
		needBuild = true
	}
}

// 处理监听
func handleWatch(event fsnotify.Event) {
	path := strings.Replace(event.Name, "\\", "/", -1)
	if strings.Contains(path, configFilename) {
		// 配置文件改变
		needBuild = true
	} else if event.Op&fsnotify.Create == fsnotify.Create {
		watchCreate(path)
	} else if event.Op&fsnotify.Remove == fsnotify.Remove {
		watchRemove(path)
	} else if event.Op&fsnotify.Rename == fsnotify.Rename {
		watchRename(path)
	} else if event.Op&fsnotify.Write == fsnotify.Write {
		watchWrite(path)
	}
}

// Watch 开始监听
func Watch() {
	// 先读取配置文件
	ReadConfig()

	w, err := fsnotify.NewWatcher()
	if err != nil {
		log.Printf("Watch new error: %v\n", err)
		return
	}
	watcher = w
	// defer watcher.Close()

	// done := make(chan bool)
	go func() {
		for {
			select {
			case event := <-watcher.Events:
				if event.Name != "" {
					// log.Println("Watch event:", event)
					handleWatch(event)
				}
			case err := <-watcher.Errors:
				if err != nil {
					log.Printf("Watch error: %v\n", err)
				}
			}
		}
	}()

	// 监听的目录列表，包含子目录
	list := []string{"src"}
	if Config.ResourceDir != "" {
		list = append(list, Config.ResourceDir)
	}
	// log.Printf("%v\n", list)
	var allList []string
	for i := 0; i < len(list); i++ {
		item := list[i]
		filepath.Walk(item, func(path1 string, f os.FileInfo, err1 error) error {
			if f == nil {
				return err1
			}
			if f.IsDir() {
				allList = append(allList, path1)
				return nil
			}
			return nil
		})
	}
	// 添加配置文件监听
	allList = append(allList, configFilename)
	for i := 0; i < len(allList); i++ {
		item := allList[i]
		addWatch(item)
	}
	// 开启各个处理器
	startBuildHandler()
	startResHandler()

	// <-done
}
<|endoftext|>


<commit_before>/*
Copyright 2015 Google Inc. All rights reserved.

Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file or at
https://developers.google.com/open-source/licenses/bsd
*/

package privet

import (
	"os"
	"sync"

	"github.com/google/cups-connector/lib"
)

// Privet managers local discovery and printing.
type Privet struct {
	xsrf      xsrfSecret
	apis      map[string]*privetAPI
	apisMutex sync.RWMutex // Protects apis
	zc        *zeroconf

	jobs chan *lib.Job
	jc   jobCache

	gcpBaseURL        string
	getProximityToken func(string, string) ([]byte, int, error)
	createTempFile    func() (*os.File, error)
}

// NewPrivet constructs a new Privet object.
//
// getProximityToken should be GoogleCloudPrint.ProximityToken()
// createTempFile should be cups.CreateTempFile()
func NewPrivet(gcpBaseURL string, getProximityToken func(string, string) ([]byte, int, error), createTempFile func() (*os.File, error)) (*Privet, error) {
	zc, err := newZeroconf()
	if err != nil {
		return nil, err
	}

	p := Privet{
		xsrf: newXSRFSecret(),
		apis: make(map[string]*privetAPI),
		zc:   zc,

		jobs: make(chan *lib.Job, 10),
		jc:   *newJobCache(),

		gcpBaseURL:        gcpBaseURL,
		getProximityToken: getProximityToken,
		createTempFile:    createTempFile,
	}

	return &p, nil
}

// AddPrinter makes a printer available locally.
func (p *Privet) AddPrinter(printer lib.Printer, getPrinter func() (lib.Printer, bool)) error {
	getProximityToken := func(user string) ([]byte, int, error) { return p.getProximityToken(printer.GCPID, user) }
	api, err := newPrivetAPI(printer.GCPID, p.gcpBaseURL, p.xsrf, &p.jc, p.jobs, getPrinter, getProximityToken, p.createTempFile)
	if err != nil {
		return err
	}

	online := false
	if printer.GCPID != "" {
		online = true
	}

	var localDefaultDisplayName = printer.DefaultDisplayName
	# TODO once we add local-only support we should hide the append behind an if ! local-only
	localDefaultDisplayName = fmt.Sprintf("%s (local)", printer.DefaultDisplayName)
	err = p.zc.addPrinter(printer.GCPID, printer.Name, api.port(), localDefaultDisplayName, p.gcpBaseURL, printer.GCPID, online)
	if err != nil {
		api.quit()
		return err
	}

	p.apisMutex.Lock()
	defer p.apisMutex.Unlock()

	p.apis[printer.GCPID] = api

	return nil
}

// UpdatePrinter updates a printer's TXT mDNS record.
func (p *Privet) UpdatePrinter(diff *lib.PrinterDiff) error {
	// API never needs to be updated

	online := false
	if diff.Printer.GCPID != "" {
		online = true
	}
	return p.zc.updatePrinterTXT(diff.Printer.GCPID, diff.Printer.DefaultDisplayName, p.gcpBaseURL, diff.Printer.GCPID, online)
}

// DeletePrinter removes a printer from Privet.
func (p *Privet) DeletePrinter(gcpID string) error {
	p.apisMutex.Lock()
	defer p.apisMutex.Unlock()

	err := p.zc.removePrinter(gcpID)
	if api, ok := p.apis[gcpID]; ok {
		api.quit()
		delete(p.apis, gcpID)
	}
	return err
}

// Jobs returns a channel that emits new print jobs.
func (p *Privet) Jobs() <-chan *lib.Job {
	return p.jobs
}

func (p *Privet) Quit() {
	p.apisMutex.Lock()
	defer p.apisMutex.Unlock()

	p.zc.quit()
	for gcpID, api := range p.apis {
		api.quit()
		delete(p.apis, gcpID)
	}
}
<commit_msg>remove unnecessary declare<commit_after>/*
Copyright 2015 Google Inc. All rights reserved.

Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file or at
https://developers.google.com/open-source/licenses/bsd
*/

package privet

import (
	"os"
	"sync"

	"github.com/google/cups-connector/lib"
)

// Privet managers local discovery and printing.
type Privet struct {
	xsrf      xsrfSecret
	apis      map[string]*privetAPI
	apisMutex sync.RWMutex // Protects apis
	zc        *zeroconf

	jobs chan *lib.Job
	jc   jobCache

	gcpBaseURL        string
	getProximityToken func(string, string) ([]byte, int, error)
	createTempFile    func() (*os.File, error)
}

// NewPrivet constructs a new Privet object.
//
// getProximityToken should be GoogleCloudPrint.ProximityToken()
// createTempFile should be cups.CreateTempFile()
func NewPrivet(gcpBaseURL string, getProximityToken func(string, string) ([]byte, int, error), createTempFile func() (*os.File, error)) (*Privet, error) {
	zc, err := newZeroconf()
	if err != nil {
		return nil, err
	}

	p := Privet{
		xsrf: newXSRFSecret(),
		apis: make(map[string]*privetAPI),
		zc:   zc,

		jobs: make(chan *lib.Job, 10),
		jc:   *newJobCache(),

		gcpBaseURL:        gcpBaseURL,
		getProximityToken: getProximityToken,
		createTempFile:    createTempFile,
	}

	return &p, nil
}

// AddPrinter makes a printer available locally.
func (p *Privet) AddPrinter(printer lib.Printer, getPrinter func() (lib.Printer, bool)) error {
	getProximityToken := func(user string) ([]byte, int, error) { return p.getProximityToken(printer.GCPID, user) }
	api, err := newPrivetAPI(printer.GCPID, p.gcpBaseURL, p.xsrf, &p.jc, p.jobs, getPrinter, getProximityToken, p.createTempFile)
	if err != nil {
		return err
	}

	online := false
	if printer.GCPID != "" {
		online = true
	}

	# TODO once we add local-only support we should hide the append behind an if ! local-only
	localDefaultDisplayName = fmt.Sprintf("%s (local)", printer.DefaultDisplayName)
	err = p.zc.addPrinter(printer.GCPID, printer.Name, api.port(), localDefaultDisplayName, p.gcpBaseURL, printer.GCPID, online)
	if err != nil {
		api.quit()
		return err
	}

	p.apisMutex.Lock()
	defer p.apisMutex.Unlock()

	p.apis[printer.GCPID] = api

	return nil
}

// UpdatePrinter updates a printer's TXT mDNS record.
func (p *Privet) UpdatePrinter(diff *lib.PrinterDiff) error {
	// API never needs to be updated

	online := false
	if diff.Printer.GCPID != "" {
		online = true
	}
	return p.zc.updatePrinterTXT(diff.Printer.GCPID, diff.Printer.DefaultDisplayName, p.gcpBaseURL, diff.Printer.GCPID, online)
}

// DeletePrinter removes a printer from Privet.
func (p *Privet) DeletePrinter(gcpID string) error {
	p.apisMutex.Lock()
	defer p.apisMutex.Unlock()

	err := p.zc.removePrinter(gcpID)
	if api, ok := p.apis[gcpID]; ok {
		api.quit()
		delete(p.apis, gcpID)
	}
	return err
}

// Jobs returns a channel that emits new print jobs.
func (p *Privet) Jobs() <-chan *lib.Job {
	return p.jobs
}

func (p *Privet) Quit() {
	p.apisMutex.Lock()
	defer p.apisMutex.Unlock()

	p.zc.quit()
	for gcpID, api := range p.apis {
		api.quit()
		delete(p.apis, gcpID)
	}
}
<|endoftext|>


<commit_before>package sentry

import (
	"fmt"
	"errors"
	"runtime"
	"os"

	"github.com/Sirupsen/logrus"
	"github.com/getsentry/raven-go"

	"gitlab.com/gitlab-org/gitlab-ci-multi-runner/common"
)

type LogHook struct {
	client *raven.Client
}

func (s *LogHook) Levels() []logrus.Level {
	return []logrus.Level{
		logrus.PanicLevel,
		logrus.FatalLevel,
		logrus.ErrorLevel,
	}
}

func (s *LogHook) Fire(entry *logrus.Entry) error {
	if s.client == nil {
		return nil
	}

	tags := make(map[string]string)
	for key, value := range entry.Data {
		tags[key] = fmt.Sprint(value)
	}

	switch entry.Level {
	case logrus.PanicLevel:
		s.client.CaptureErrorAndWait(errors.New(entry.Message), tags)

	case logrus.FatalLevel:
		s.client.CaptureErrorAndWait(errors.New(entry.Message), tags)

	case logrus.ErrorLevel:
		s.client.CaptureError(errors.New(entry.Message), tags)
	}
	return nil
}

func NewLogHook(dsn string) (lh LogHook, err error) {
	tags := make(map[string]string)
	tags["built"] = common.BUILT
	tags["version"] = common.VERSION
	tags["revision"] = common.REVISION
	tags["branch"] = common.BRANCH
	tags["go-version"] = runtime.Version()
	tags["go-os"] = runtime.GOOS
	tags["go-arch"] = runtime.GOARCH
	tags["hostname"], _ = os.Hostname()
	client, err := raven.NewWithTags(dsn, tags)
	if err != nil {
		return
	}
	lh.client = client
	return
}
<commit_msg>Make fmt happy<commit_after>package sentry

import (
	"errors"
	"fmt"
	"os"
	"runtime"

	"github.com/Sirupsen/logrus"
	"github.com/getsentry/raven-go"

	"gitlab.com/gitlab-org/gitlab-ci-multi-runner/common"
)

type LogHook struct {
	client *raven.Client
}

func (s *LogHook) Levels() []logrus.Level {
	return []logrus.Level{
		logrus.PanicLevel,
		logrus.FatalLevel,
		logrus.ErrorLevel,
	}
}

func (s *LogHook) Fire(entry *logrus.Entry) error {
	if s.client == nil {
		return nil
	}

	tags := make(map[string]string)
	for key, value := range entry.Data {
		tags[key] = fmt.Sprint(value)
	}

	switch entry.Level {
	case logrus.PanicLevel:
		s.client.CaptureErrorAndWait(errors.New(entry.Message), tags)

	case logrus.FatalLevel:
		s.client.CaptureErrorAndWait(errors.New(entry.Message), tags)

	case logrus.ErrorLevel:
		s.client.CaptureError(errors.New(entry.Message), tags)
	}
	return nil
}

func NewLogHook(dsn string) (lh LogHook, err error) {
	tags := make(map[string]string)
	tags["built"] = common.BUILT
	tags["version"] = common.VERSION
	tags["revision"] = common.REVISION
	tags["branch"] = common.BRANCH
	tags["go-version"] = runtime.Version()
	tags["go-os"] = runtime.GOOS
	tags["go-arch"] = runtime.GOARCH
	tags["hostname"], _ = os.Hostname()
	client, err := raven.NewWithTags(dsn, tags)
	if err != nil {
		return
	}
	lh.client = client
	return
}
<|endoftext|>


<commit_before>package dokugen

type RankedObject interface {
	Rank() int
}

type FiniteQueue struct {
	min     int
	max     int
	objects [][]RankedObject
}

//Returns a new queue that will work for items with a rank as low as min or as high as max (inclusive)
func NewFiniteQueue(min int, max int) *FiniteQueue {
	return &FiniteQueue{min, max, make([][]RankedObject, max-min+1)}
}

func (self *FiniteQueue) Min() int {
	return self.min
}

func (self *FiniteQueue) Max() int {
	return self.max
}

func (self *FiniteQueue) Insert(obj RankedObject) {
	rank := obj.Rank()
	list, ok := self.getList(rank)
	if !ok {
		//Apparently rank wasn't legal.
		return
	}
	//Scrub the list for this item.
	for _, item := range list {
		//Structs will compare equal if all of their fields are the same.
		if item == obj {
			//It's already there, just return.
			return
		}
	}
	//If we get to here we need to add the item.
	self.setList(rank, append(list, obj))
}

func (self *FiniteQueue) Get() RankedObject {
	return self.GetSmallerThan(self.max + 1)
}

func (self *FiniteQueue) GetSmallerThan(max int) RankedObject {
	for i, list := range self.objects {
		if i+self.min >= max {
			return nil
		}
		for len(list) > 0 {
			obj := list[0]
			if len(list) == 1 {
				self.objects[i] = nil
				list = nil
			} else {
				self.objects[i] = list[1:]
				list = list[1:]
			}
			//Does this object still have the rank it did when it was inserted?
			if obj.Rank() == i+self.min {
				return obj
			}
			//otherwise, keep looking.
		}
	}
	return nil
}

func (self *FiniteQueue) legalRank(rank int) bool {
	return rank >= self.min && rank <= self.max
}

func (self *FiniteQueue) getList(rank int) ([]RankedObject, bool) {
	if !self.legalRank(rank) {
		return nil, false
	}
	return self.objects[rank-self.min], true
}

func (self *FiniteQueue) setList(rank int, list []RankedObject) {
	if !self.legalRank(rank) {
		return
	}
	self.objects[rank-self.min] = list
}
<commit_msg>Make GetSmallerThan() go in a random order (I think).<commit_after>package dokugen

import (
	"math/rand"
)

type RankedObject interface {
	Rank() int
}

type FiniteQueue struct {
	min     int
	max     int
	objects [][]RankedObject
}

//Returns a new queue that will work for items with a rank as low as min or as high as max (inclusive)
func NewFiniteQueue(min int, max int) *FiniteQueue {
	return &FiniteQueue{min, max, make([][]RankedObject, max-min+1)}
}

func (self *FiniteQueue) Min() int {
	return self.min
}

func (self *FiniteQueue) Max() int {
	return self.max
}

func (self *FiniteQueue) Insert(obj RankedObject) {
	rank := obj.Rank()
	list, ok := self.getList(rank)
	if !ok {
		//Apparently rank wasn't legal.
		return
	}
	//Scrub the list for this item.
	for _, item := range list {
		//Structs will compare equal if all of their fields are the same.
		if item == obj {
			//It's already there, just return.
			return
		}
	}
	//If we get to here we need to add the item.
	self.setList(rank, append(list, obj))
}

func (self *FiniteQueue) Get() RankedObject {
	return self.GetSmallerThan(self.max + 1)
}

func (self *FiniteQueue) GetSmallerThan(max int) RankedObject {

	for i, list := range self.objects {
		if i+self.min >= max {
			return nil
		}
	ListLoop:
		for len(list) > 0 {

			//Clear off nils from the front.
			for list[0] == nil {
				if len(list) == 1 {
					self.objects[i] = nil
					list = nil
					//There are no more in this list; move to the next.
					continue ListLoop
				} else {
					self.objects[i] = list[1:]
					list = list[1:]
				}
			}
			//Pick one at random
			index := rand.Intn(len(list))
			obj := list[index]
			//Mark its old location as emptied.
			list[index] = nil

			//Meh, try again from this list.
			if obj == nil {
				continue
			}

			//Does this object still have the rank it did when it was inserted?
			if obj.Rank() == i+self.min {
				return obj
			}
			//otherwise, keep looking.
		}
	}
	return nil
}

func (self *FiniteQueue) legalRank(rank int) bool {
	return rank >= self.min && rank <= self.max
}

func (self *FiniteQueue) getList(rank int) ([]RankedObject, bool) {
	if !self.legalRank(rank) {
		return nil, false
	}
	return self.objects[rank-self.min], true
}

func (self *FiniteQueue) setList(rank int, list []RankedObject) {
	if !self.legalRank(rank) {
		return
	}
	self.objects[rank-self.min] = list
}
<|endoftext|>


<commit_before>package main_test

import (
	"encoding/json"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"strings"
	"time"

	"github.com/concourse/time-resource/models"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gexec"
)

var _ = Describe("Out", func() {
	var tmpdir string
	var source string

	var outCmd *exec.Cmd
	var now time.Time

	BeforeEach(func() {
		var err error

		tmpdir, err = ioutil.TempDir("", "out-source")
		Expect(err).NotTo(HaveOccurred())

		source = path.Join(tmpdir, "out-dir")

		outCmd = exec.Command(outPath, source)
		now = time.Now().UTC()
	})

	AfterEach(func() {
		os.RemoveAll(tmpdir)
	})

	Context("when executed", func() {
		var source map[string]interface{}
		var response models.OutResponse

		BeforeEach(func() {
			source = map[string]interface{}{}
			response = models.OutResponse{}
		})

		JustBeforeEach(func() {
			stdin, err := outCmd.StdinPipe()
			Expect(err).NotTo(HaveOccurred())

			session, err := gexec.Start(outCmd, GinkgoWriter, GinkgoWriter)
			Expect(err).NotTo(HaveOccurred())

			err = json.NewEncoder(stdin).Encode(map[string]interface{}{
				"source": source,
			})
			Expect(err).NotTo(HaveOccurred())

			<-session.Exited
			Expect(session.ExitCode()).To(Equal(0))

			err = json.Unmarshal(session.Out.Contents(), &response)
			Expect(err).NotTo(HaveOccurred())
		})

		Context("when a location is specified", func() {
			var loc *time.Location

			BeforeEach(func() {
				var err error
				loc, err = time.LoadLocation("America/Indiana/Indianapolis")
				Expect(err).ToNot(HaveOccurred())

				source["location"] = loc.String()

				now = now.In(loc)
			})

			It("reports specified location's current time(offset: -0500) as the version", func() {
				contained := strings.Contains(response.Version.Time.String(), "-0500")
				Expect(contained).To(BeTrue())
			})
		})
		Context("when a location is not specified", func() {
			It("reports the current time(offset: 0000) as the version", func() {
				contained := strings.Contains(response.Version.Time.String(), "0000")
				Expect(contained).To(BeTrue())
			})
		})
	})
})
<commit_msg>fix mistake<commit_after>package main_test

import (
	"encoding/json"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"strings"
	"time"

	"github.com/concourse/time-resource/models"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gexec"
)

var _ = Describe("Out", func() {
	var tmpdir string
	var source string

	var outCmd *exec.Cmd
	var now time.Time

	BeforeEach(func() {
		var err error

		tmpdir, err = ioutil.TempDir("", "out-source")
		Expect(err).NotTo(HaveOccurred())

		source = path.Join(tmpdir, "out-dir")

		outCmd = exec.Command(outPath, source)
		now = time.Now().UTC()
	})

	AfterEach(func() {
		os.RemoveAll(tmpdir)
	})

	Context("when executed", func() {
		var source map[string]interface{}
		var response models.OutResponse

		BeforeEach(func() {
			source = map[string]interface{}{}
			response = models.OutResponse{}
		})

		JustBeforeEach(func() {
			stdin, err := outCmd.StdinPipe()
			Expect(err).NotTo(HaveOccurred())

			session, err := gexec.Start(outCmd, GinkgoWriter, GinkgoWriter)
			Expect(err).NotTo(HaveOccurred())

			err = json.NewEncoder(stdin).Encode(map[string]interface{}{
				"source": source,
			})
			Expect(err).NotTo(HaveOccurred())

			<-session.Exited
			Expect(session.ExitCode()).To(Equal(0))

			err = json.Unmarshal(session.Out.Contents(), &response)
			Expect(err).NotTo(HaveOccurred())
		})

		Context("when a location is specified", func() {
			var loc *time.Location

			BeforeEach(func() {
				var err error
				loc, err = time.LoadLocation("America/Indiana/Indianapolis")
				Expect(err).ToNot(HaveOccurred())

				source["location"] = loc.String()

				now = now.In(loc)
			})

			It("reports specified location's current time(offset: -0400) as the version", func() {
				contained := strings.Contains(response.Version.Time.String(), "-0400")
				Expect(contained).To(BeTrue())
			})
		})
		Context("when a location is not specified", func() {
			It("reports the current time(offset: 0000) as the version", func() {
				contained := strings.Contains(response.Version.Time.String(), "0000")
				Expect(contained).To(BeTrue())
			})
		})
	})
})
<|endoftext|>


<commit_before>package server

import (
	"fmt"
	"net"
	"net/http"
	"net/url"
	"os"
	"reflect"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/getlantern/fronted"
	"github.com/getlantern/go-igdman/igdman"
	"github.com/getlantern/golog"
	"github.com/getlantern/nattywad"
	"github.com/getlantern/waddell"

	"github.com/getlantern/flashlight/globals"
	"github.com/getlantern/flashlight/nattest"
	"github.com/getlantern/flashlight/statreporter"
	"github.com/getlantern/flashlight/statserver"
)

const (
	PortmapFailure = 50
)

var (
	log = golog.LoggerFor("flashlight.server")

	registerPeriod = 5 * time.Minute
)

type Server struct {
	// Addr: listen address in form of host:port
	Addr string

	// Host: FQDN that is guaranteed to hit this server
	Host string

	// ReadTimeout: (optional) timeout for read ops
	ReadTimeout time.Duration

	// WriteTimeout: (optional) timeout for write ops
	WriteTimeout time.Duration

	CertContext                *fronted.CertContext // context for certificate management
	AllowNonGlobalDestinations bool                 // if true, requests to LAN, Loopback, etc. will be allowed
	AllowedPorts               []int                // if specified, only connections to these ports will be allowed

	waddellClient  *waddell.Client
	nattywadServer *nattywad.Server
	cfg            *ServerConfig
	cfgMutex       sync.RWMutex
}

func (server *Server) Configure(newCfg *ServerConfig) {
	server.cfgMutex.Lock()
	defer server.cfgMutex.Unlock()

	oldCfg := server.cfg

	log.Debug("Server.Configure() called")
	if oldCfg != nil && reflect.DeepEqual(oldCfg, newCfg) {
		log.Debugf("Server configuration unchanged")
		return
	}

	if oldCfg == nil || newCfg.Portmap != oldCfg.Portmap {
		// Portmap changed
		if oldCfg != nil && oldCfg.Portmap > 0 {
			log.Debugf("Attempting to unmap old external port %d", oldCfg.Portmap)
			err := unmapPort(oldCfg.Portmap)
			if err != nil {
				log.Errorf("Unable to unmap old external port: %s", err)
			}
			log.Debugf("Unmapped old external port %d", oldCfg.Portmap)
		}

		if newCfg.Portmap > 0 {
			log.Debugf("Attempting to map new external port %d", newCfg.Portmap)
			err := mapPort(server.Addr, newCfg.Portmap)
			if err != nil {
				log.Errorf("Unable to map new external port: %s", err)
				os.Exit(PortmapFailure)
			}
			log.Debugf("Mapped new external port %d", newCfg.Portmap)
		}
	}

	nattywadIsEnabled := newCfg.WaddellAddr != ""
	nattywadWasEnabled := server.nattywadServer != nil
	waddellAddrChanged := oldCfg == nil && newCfg.WaddellAddr != "" || oldCfg != nil && oldCfg.WaddellAddr != newCfg.WaddellAddr

	if waddellAddrChanged {
		if nattywadWasEnabled {
			server.stopNattywad()
		}
		if nattywadIsEnabled {
			server.startNattywad(newCfg.WaddellAddr)
		}
	}

	server.cfg = newCfg
}

func (server *Server) ListenAndServe() error {
	if server.Host != "" {
		log.Debugf("Running as host %s", server.Host)
	}

	fs := &fronted.Server{
		Addr:                       server.Addr,
		Host:                       server.Host,
		ReadTimeout:                server.ReadTimeout,
		WriteTimeout:               server.WriteTimeout,
		CertContext:                server.CertContext,
		AllowNonGlobalDestinations: server.AllowNonGlobalDestinations,
		AllowedPorts:               server.AllowedPorts,
	}

	if server.cfg.Unencrypted {
		log.Debug("Running in unencrypted mode")
		fs.CertContext = nil
	}

	// Add callbacks to track bytes given
	fs.OnBytesReceived = func(ip string, destAddr string, req *http.Request, bytes int64) {
		onBytesGiven(destAddr, req, bytes)
		statserver.OnBytesReceived(ip, bytes)
	}
	fs.OnBytesSent = func(ip string, destAddr string, req *http.Request, bytes int64) {
		onBytesGiven(destAddr, req, bytes)
		statserver.OnBytesSent(ip, bytes)
	}

	l, err := fs.Listen()
	if err != nil {
		return fmt.Errorf("Unable to listen at %s: %s", server.Addr, err)
	}

	go server.register()

	return fs.Serve(l)
}

func (server *Server) register() {
	for {
		server.cfgMutex.RLock()
		baseUrl := server.cfg.RegisterAt
		server.cfgMutex.RUnlock()
		if baseUrl != "" {
			if globals.InstanceId == "" {
				log.Error("Unable to register server because no InstanceId is configured")
			} else {
				log.Debugf("Registering server at %v", baseUrl)
				registerUrl := baseUrl + "/register"
				vals := url.Values{
					"name": []string{globals.InstanceId},
					"port": []string{"443"},
				}
				resp, err := http.PostForm(registerUrl, vals)
				if err != nil {
					log.Errorf("Unable to register at %v: %v", registerUrl, err)
				} else if resp.StatusCode != 200 {
					log.Errorf("Unexpected response status registering at %v: %d", registerUrl, resp.StatusCode)
				} else {
					log.Debugf("Successfully registered server at %v", registerUrl)
				}
				resp.Body.Close()
				time.Sleep(registerPeriod)
			}
		}
	}
}

func (server *Server) startNattywad(waddellAddr string) {
	log.Debugf("Connecting to waddell at: %s", waddellAddr)
	var err error
	server.waddellClient, err = waddell.NewClient(&waddell.ClientConfig{
		Dial: func() (net.Conn, error) {
			return net.Dial("tcp", waddellAddr)
		},
		ServerCert:        globals.WaddellCert,
		ReconnectAttempts: 10,
		OnId: func(id waddell.PeerId) {
			log.Debugf("Connected to Waddell!! Id is: %s", id)
		},
	})
	if err != nil {
		log.Errorf("Unable to connect to waddell: %s", err)
		server.waddellClient = nil
		return
	}
	server.nattywadServer = &nattywad.Server{
		Client: server.waddellClient,
		OnSuccess: func(local *net.UDPAddr, remote *net.UDPAddr) bool {
			err := nattest.Serve(local)
			if err != nil {
				log.Error(err.Error())
				return false
			}
			return true
		},
	}
	server.nattywadServer.Start()
}

func (server *Server) stopNattywad() {
	log.Debug("Stopping nattywad server")
	server.nattywadServer.Stop()
	server.nattywadServer = nil
	log.Debug("Stopping waddell client")
	server.waddellClient.Close()
	server.waddellClient = nil
}

func mapPort(addr string, port int) error {
	internalIP, internalPortString, err := net.SplitHostPort(addr)
	if err != nil {
		return fmt.Errorf("Unable to split host and port for %v: %v", addr, err)
	}

	internalPort, err := strconv.Atoi(internalPortString)
	if err != nil {
		return fmt.Errorf("Unable to parse local port: ")
	}

	if internalIP == "" {
		internalIP, err = determineInternalIP()
		if err != nil {
			return fmt.Errorf("Unable to determine internal IP: %s", err)
		}
	}

	igd, err := igdman.NewIGD()
	if err != nil {
		return fmt.Errorf("Unable to get IGD: %s", err)
	}

	igd.RemovePortMapping(igdman.TCP, port)
	err = igd.AddPortMapping(igdman.TCP, internalIP, internalPort, port, 0)
	if err != nil {
		return fmt.Errorf("Unable to map port with igdman %d: %s", port, err)
	}

	return nil
}

func unmapPort(port int) error {
	igd, err := igdman.NewIGD()
	if err != nil {
		return fmt.Errorf("Unable to get IGD: %s", err)
	}

	igd.RemovePortMapping(igdman.TCP, port)
	if err != nil {
		return fmt.Errorf("Unable to unmap port with igdman %d: %s", port, err)
	}

	return nil
}

// determineInternalIP determines the internal IP to use for mapping ports. It
// does this by dialing a website on the public Internet and then finding out
// the LocalAddr for the corresponding connection. This gives us an interface
// that we know has Internet access, which makes it suitable for port mapping.
func determineInternalIP() (string, error) {
	conn, err := net.DialTimeout("tcp", "s3.amazonaws.com:443", 20*time.Second)
	if err != nil {
		return "", fmt.Errorf("Unable to determine local IP: %s", err)
	}
	defer conn.Close()
	host, _, err := net.SplitHostPort(conn.LocalAddr().String())
	return host, err
}

func onBytesGiven(destAddr string, req *http.Request, bytes int64) {
	host, port, _ := net.SplitHostPort(destAddr)
	if port == "" {
		port = "0"
	}

	given := statreporter.CountryDim().
		And("flserver", globals.InstanceId).
		And("destport", port)
	given.Increment("bytesGiven").Add(bytes)
	given.Increment("bytesGivenByFlashlight").Add(bytes)

	clientCountry := req.Header.Get("Cf-Ipcountry")
	if clientCountry != "" {
		givenTo := statreporter.Country(clientCountry)
		givenTo.Increment("bytesGivenTo").Add(bytes)
		givenTo.Increment("bytesGivenToByFlashlight").Add(bytes)
		givenTo.Member("distinctDestHosts", host)

		clientIp := req.Header.Get("X-Forwarded-For")
		if clientIp != "" {
			// clientIp may contain multiple ips, use the first
			ips := strings.Split(clientIp, ",")
			clientIp := strings.TrimSpace(ips[0])
			givenTo.Member("distinctClients", clientIp)
		}

	}
}
<commit_msg>Better error reporting for peer registration<commit_after>package server

import (
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"os"
	"reflect"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/getlantern/fronted"
	"github.com/getlantern/go-igdman/igdman"
	"github.com/getlantern/golog"
	"github.com/getlantern/nattywad"
	"github.com/getlantern/waddell"

	"github.com/getlantern/flashlight/globals"
	"github.com/getlantern/flashlight/nattest"
	"github.com/getlantern/flashlight/statreporter"
	"github.com/getlantern/flashlight/statserver"
)

const (
	PortmapFailure = 50
)

var (
	log = golog.LoggerFor("flashlight.server")

	registerPeriod = 5 * time.Minute
)

type Server struct {
	// Addr: listen address in form of host:port
	Addr string

	// Host: FQDN that is guaranteed to hit this server
	Host string

	// ReadTimeout: (optional) timeout for read ops
	ReadTimeout time.Duration

	// WriteTimeout: (optional) timeout for write ops
	WriteTimeout time.Duration

	CertContext                *fronted.CertContext // context for certificate management
	AllowNonGlobalDestinations bool                 // if true, requests to LAN, Loopback, etc. will be allowed
	AllowedPorts               []int                // if specified, only connections to these ports will be allowed

	waddellClient  *waddell.Client
	nattywadServer *nattywad.Server
	cfg            *ServerConfig
	cfgMutex       sync.RWMutex
}

func (server *Server) Configure(newCfg *ServerConfig) {
	server.cfgMutex.Lock()
	defer server.cfgMutex.Unlock()

	oldCfg := server.cfg

	log.Debug("Server.Configure() called")
	if oldCfg != nil && reflect.DeepEqual(oldCfg, newCfg) {
		log.Debugf("Server configuration unchanged")
		return
	}

	if oldCfg == nil || newCfg.Portmap != oldCfg.Portmap {
		// Portmap changed
		if oldCfg != nil && oldCfg.Portmap > 0 {
			log.Debugf("Attempting to unmap old external port %d", oldCfg.Portmap)
			err := unmapPort(oldCfg.Portmap)
			if err != nil {
				log.Errorf("Unable to unmap old external port: %s", err)
			}
			log.Debugf("Unmapped old external port %d", oldCfg.Portmap)
		}

		if newCfg.Portmap > 0 {
			log.Debugf("Attempting to map new external port %d", newCfg.Portmap)
			err := mapPort(server.Addr, newCfg.Portmap)
			if err != nil {
				log.Errorf("Unable to map new external port: %s", err)
				os.Exit(PortmapFailure)
			}
			log.Debugf("Mapped new external port %d", newCfg.Portmap)
		}
	}

	nattywadIsEnabled := newCfg.WaddellAddr != ""
	nattywadWasEnabled := server.nattywadServer != nil
	waddellAddrChanged := oldCfg == nil && newCfg.WaddellAddr != "" || oldCfg != nil && oldCfg.WaddellAddr != newCfg.WaddellAddr

	if waddellAddrChanged {
		if nattywadWasEnabled {
			server.stopNattywad()
		}
		if nattywadIsEnabled {
			server.startNattywad(newCfg.WaddellAddr)
		}
	}

	server.cfg = newCfg
}

func (server *Server) ListenAndServe() error {
	if server.Host != "" {
		log.Debugf("Running as host %s", server.Host)
	}

	fs := &fronted.Server{
		Addr:                       server.Addr,
		Host:                       server.Host,
		ReadTimeout:                server.ReadTimeout,
		WriteTimeout:               server.WriteTimeout,
		CertContext:                server.CertContext,
		AllowNonGlobalDestinations: server.AllowNonGlobalDestinations,
		AllowedPorts:               server.AllowedPorts,
	}

	if server.cfg.Unencrypted {
		log.Debug("Running in unencrypted mode")
		fs.CertContext = nil
	}

	// Add callbacks to track bytes given
	fs.OnBytesReceived = func(ip string, destAddr string, req *http.Request, bytes int64) {
		onBytesGiven(destAddr, req, bytes)
		statserver.OnBytesReceived(ip, bytes)
	}
	fs.OnBytesSent = func(ip string, destAddr string, req *http.Request, bytes int64) {
		onBytesGiven(destAddr, req, bytes)
		statserver.OnBytesSent(ip, bytes)
	}

	l, err := fs.Listen()
	if err != nil {
		return fmt.Errorf("Unable to listen at %s: %s", server.Addr, err)
	}

	go server.register()

	return fs.Serve(l)
}

func (server *Server) register() {
	for {
		server.cfgMutex.RLock()
		baseUrl := server.cfg.RegisterAt
		server.cfgMutex.RUnlock()
		if baseUrl != "" {
			if globals.InstanceId == "" {
				log.Error("Unable to register server because no InstanceId is configured")
			} else {
				log.Debugf("Registering server at %v", baseUrl)
				registerUrl := baseUrl + "/register"
				vals := url.Values{
					"name": []string{globals.InstanceId},
					"port": []string{"443"},
				}
				resp, err := http.PostForm(registerUrl, vals)
				if err != nil {
					log.Errorf("Unable to register at %v: %v", registerUrl, err)
				} else if resp.StatusCode != 200 {
					bodyString, _ := ioutil.ReadAll(resp.Body)
					log.Errorf("Unexpected response status registering at %v: %d    %v", registerUrl, resp.StatusCode, string(bodyString))
				} else {
					log.Debugf("Successfully registered server at %v", registerUrl)
				}
				resp.Body.Close()
				time.Sleep(registerPeriod)
			}
		}
	}
}

func (server *Server) startNattywad(waddellAddr string) {
	log.Debugf("Connecting to waddell at: %s", waddellAddr)
	var err error
	server.waddellClient, err = waddell.NewClient(&waddell.ClientConfig{
		Dial: func() (net.Conn, error) {
			return net.Dial("tcp", waddellAddr)
		},
		ServerCert:        globals.WaddellCert,
		ReconnectAttempts: 10,
		OnId: func(id waddell.PeerId) {
			log.Debugf("Connected to Waddell!! Id is: %s", id)
		},
	})
	if err != nil {
		log.Errorf("Unable to connect to waddell: %s", err)
		server.waddellClient = nil
		return
	}
	server.nattywadServer = &nattywad.Server{
		Client: server.waddellClient,
		OnSuccess: func(local *net.UDPAddr, remote *net.UDPAddr) bool {
			err := nattest.Serve(local)
			if err != nil {
				log.Error(err.Error())
				return false
			}
			return true
		},
	}
	server.nattywadServer.Start()
}

func (server *Server) stopNattywad() {
	log.Debug("Stopping nattywad server")
	server.nattywadServer.Stop()
	server.nattywadServer = nil
	log.Debug("Stopping waddell client")
	server.waddellClient.Close()
	server.waddellClient = nil
}

func mapPort(addr string, port int) error {
	internalIP, internalPortString, err := net.SplitHostPort(addr)
	if err != nil {
		return fmt.Errorf("Unable to split host and port for %v: %v", addr, err)
	}

	internalPort, err := strconv.Atoi(internalPortString)
	if err != nil {
		return fmt.Errorf("Unable to parse local port: ")
	}

	if internalIP == "" {
		internalIP, err = determineInternalIP()
		if err != nil {
			return fmt.Errorf("Unable to determine internal IP: %s", err)
		}
	}

	igd, err := igdman.NewIGD()
	if err != nil {
		return fmt.Errorf("Unable to get IGD: %s", err)
	}

	igd.RemovePortMapping(igdman.TCP, port)
	err = igd.AddPortMapping(igdman.TCP, internalIP, internalPort, port, 0)
	if err != nil {
		return fmt.Errorf("Unable to map port with igdman %d: %s", port, err)
	}

	return nil
}

func unmapPort(port int) error {
	igd, err := igdman.NewIGD()
	if err != nil {
		return fmt.Errorf("Unable to get IGD: %s", err)
	}

	igd.RemovePortMapping(igdman.TCP, port)
	if err != nil {
		return fmt.Errorf("Unable to unmap port with igdman %d: %s", port, err)
	}

	return nil
}

// determineInternalIP determines the internal IP to use for mapping ports. It
// does this by dialing a website on the public Internet and then finding out
// the LocalAddr for the corresponding connection. This gives us an interface
// that we know has Internet access, which makes it suitable for port mapping.
func determineInternalIP() (string, error) {
	conn, err := net.DialTimeout("tcp", "s3.amazonaws.com:443", 20*time.Second)
	if err != nil {
		return "", fmt.Errorf("Unable to determine local IP: %s", err)
	}
	defer conn.Close()
	host, _, err := net.SplitHostPort(conn.LocalAddr().String())
	return host, err
}

func onBytesGiven(destAddr string, req *http.Request, bytes int64) {
	host, port, _ := net.SplitHostPort(destAddr)
	if port == "" {
		port = "0"
	}

	given := statreporter.CountryDim().
		And("flserver", globals.InstanceId).
		And("destport", port)
	given.Increment("bytesGiven").Add(bytes)
	given.Increment("bytesGivenByFlashlight").Add(bytes)

	clientCountry := req.Header.Get("Cf-Ipcountry")
	if clientCountry != "" {
		givenTo := statreporter.Country(clientCountry)
		givenTo.Increment("bytesGivenTo").Add(bytes)
		givenTo.Increment("bytesGivenToByFlashlight").Add(bytes)
		givenTo.Member("distinctDestHosts", host)

		clientIp := req.Header.Get("X-Forwarded-For")
		if clientIp != "" {
			// clientIp may contain multiple ips, use the first
			ips := strings.Split(clientIp, ",")
			clientIp := strings.TrimSpace(ips[0])
			givenTo.Member("distinctClients", clientIp)
		}

	}
}
<|endoftext|>


<commit_before>// Copyright The OpenTelemetry 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 basic // import "go.opentelemetry.io/otel/sdk/metric/controller/basic"

import (
	"context"
	"fmt"
	"sync"
	"time"

	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/internal/metric/registry"
	"go.opentelemetry.io/otel/metric"
	export "go.opentelemetry.io/otel/sdk/export/metric"
	"go.opentelemetry.io/otel/sdk/instrumentation"
	sdk "go.opentelemetry.io/otel/sdk/metric"
	controllerTime "go.opentelemetry.io/otel/sdk/metric/controller/time"
	"go.opentelemetry.io/otel/sdk/resource"
)

// DefaultPeriod is used for:
//
// - the minimum time between calls to Collect()
// - the timeout for Export()
// - the timeout for Collect().
const DefaultPeriod = 10 * time.Second

// ErrControllerStarted indicates that a controller was started more
// than once.
var ErrControllerStarted = fmt.Errorf("controller already started")

// Controller organizes and synchronizes collection of metric data in
// both "pull" and "push" configurations.  This supports two distinct
// modes:
//
// - Push and Pull: Start() must be called to begin calling the exporter;
//   Collect() is called periodically by a background thread after starting
//   the controller.
// - Pull-Only: Start() is optional in this case, to call Collect periodically.
//   If Start() is not called, Collect() can be called manually to initiate
//   collection
//
// The controller supports mixing push and pull access to metric data
// using the export.Reader RWLock interface.  Collection will
// be blocked by a pull request in the basic controller.
type Controller struct {
	// lock protects libraries and synchronizes Start() and Stop().
	lock sync.Mutex
	// TODO: libraries is synchronized by lock, but could be
	// accomplished using a sync.Map.  The SDK specification will
	// probably require this, as the draft already states that
	// Stop() and MeterProvider.Meter() should not block each
	// other.
	libraries           map[instrumentation.Library]*registry.UniqueInstrumentMeterImpl
	checkpointerFactory export.CheckpointerFactory

	resource *resource.Resource
	exporter export.Exporter
	wg       sync.WaitGroup
	stopCh   chan struct{}
	clock    controllerTime.Clock
	ticker   controllerTime.Ticker

	collectPeriod  time.Duration
	collectTimeout time.Duration
	pushTimeout    time.Duration

	// collectedTime is used only in configurations with no
	// exporter, when ticker != nil.
	collectedTime time.Time
}

var _ export.InstrumentationLibraryReader = &Controller{}
var _ metric.MeterProvider = &Controller{}

func (c *Controller) Meter(instrumentationName string, opts ...metric.MeterOption) metric.Meter {
	cfg := metric.NewMeterConfig(opts...)
	library := instrumentation.Library{
		Name:      instrumentationName,
		Version:   cfg.InstrumentationVersion(),
		SchemaURL: cfg.SchemaURL(),
	}

	c.lock.Lock()
	defer c.lock.Unlock()
	m, ok := c.libraries[library]
	if !ok {
		checkpointer := c.checkpointerFactory.NewCheckpointer()
		accumulator := sdk.NewAccumulator(checkpointer)
		m = registry.NewUniqueInstrumentMeterImpl(&accumulatorCheckpointer{
			Accumulator:  accumulator,
			checkpointer: checkpointer,
			library:      library,
		})

		c.libraries[library] = m
	}
	return metric.WrapMeterImpl(m)
}

type accumulatorCheckpointer struct {
	*sdk.Accumulator
	checkpointer export.Checkpointer
	library      instrumentation.Library
}

// New constructs a Controller using the provided checkpointer factory
// and options (including optional exporter) to configure a metric
// export pipeline.
func New(checkpointerFactory export.CheckpointerFactory, opts ...Option) *Controller {
	c := &config{
		CollectPeriod:  DefaultPeriod,
		CollectTimeout: DefaultPeriod,
		PushTimeout:    DefaultPeriod,
	}
	for _, opt := range opts {
		opt.apply(c)
	}
	if c.Resource == nil {
		c.Resource = resource.Default()
	} else {
		var err error
		c.Resource, err = resource.Merge(resource.Environment(), c.Resource)
		if err != nil {
			otel.Handle(err)
		}
	}
	return &Controller{
		libraries:           map[instrumentation.Library]*registry.UniqueInstrumentMeterImpl{},
		checkpointerFactory: checkpointerFactory,
		exporter:            c.Exporter,
		resource:            c.Resource,
		stopCh:              nil,
		clock:               controllerTime.RealClock{},

		collectPeriod:  c.CollectPeriod,
		collectTimeout: c.CollectTimeout,
		pushTimeout:    c.PushTimeout,
	}
}

// SetClock supports setting a mock clock for testing.  This must be
// called before Start().
func (c *Controller) SetClock(clock controllerTime.Clock) {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.clock = clock
}

// Resource returns the *resource.Resource associated with this
// controller.
func (c *Controller) Resource() *resource.Resource {
	return c.resource
}

// Start begins a ticker that periodically collects and exports
// metrics with the configured interval.  This is required for calling
// a configured Exporter (see WithExporter) and is otherwise optional
// when only pulling metric data.
//
// The passed context is passed to Collect() and subsequently to
// asynchronous instrument callbacks.  Returns an error when the
// controller was already started.
//
// Note that it is not necessary to Start a controller when only
// pulling data; use the Collect() and ForEach() methods directly in
// this case.
func (c *Controller) Start(ctx context.Context) error {
	c.lock.Lock()
	defer c.lock.Unlock()

	if c.stopCh != nil {
		return ErrControllerStarted
	}

	c.wg.Add(1)
	c.stopCh = make(chan struct{})
	c.ticker = c.clock.Ticker(c.collectPeriod)
	go c.runTicker(ctx, c.stopCh)
	return nil
}

// Stop waits for the background goroutine to return and then collects
// and exports metrics one last time before returning.  The passed
// context is passed to the final Collect() and subsequently to the
// final asynchronous instruments.
//
// Note that Stop() will not cancel an ongoing collection or export.
func (c *Controller) Stop(ctx context.Context) error {
	if lastCollection := func() bool {
		c.lock.Lock()
		defer c.lock.Unlock()

		if c.stopCh == nil {
			return false
		}

		close(c.stopCh)
		c.stopCh = nil
		c.wg.Wait()
		c.ticker.Stop()
		c.ticker = nil
		return true
	}(); !lastCollection {
		return nil
	}
	return c.collect(ctx)
}

// runTicker collection on ticker events until the stop channel is closed.
func (c *Controller) runTicker(ctx context.Context, stopCh chan struct{}) {
	defer c.wg.Done()
	for {
		select {
		case <-stopCh:
			return
		case <-c.ticker.C():
			if err := c.collect(ctx); err != nil {
				otel.Handle(err)
			}
		}
	}
}

// collect computes a checkpoint and optionally exports it.
func (c *Controller) collect(ctx context.Context) error {
	if err := c.checkpoint(ctx); err != nil {
		return err
	}
	if c.exporter == nil {
		return nil
	}

	// Note: this is not subject to collectTimeout.  This blocks the next
	// collection despite collectTimeout because it holds a lock.
	return c.export(ctx)
}

// accumulatorList returns a snapshot of current accumulators
// registered to this controller.  This briefly locks the controller.
func (c *Controller) accumulatorList() []*accumulatorCheckpointer {
	c.lock.Lock()
	defer c.lock.Unlock()

	var r []*accumulatorCheckpointer
	for _, entry := range c.libraries {
		acc, ok := entry.MeterImpl().(*accumulatorCheckpointer)
		if ok {
			r = append(r, acc)
		}
	}
	return r
}

// checkpoint calls the Accumulator and Checkpointer interfaces to
// compute the Reader.  This applies the configured collection
// timeout.  Note that this does not try to cancel a Collect or Export
// when Stop() is called.
func (c *Controller) checkpoint(ctx context.Context) error {
	for _, impl := range c.accumulatorList() {
		if err := c.checkpointSingleAccumulator(ctx, impl); err != nil {
			return err
		}
	}
	return nil
}

// checkpointSingleAccumulator checkpoints a single instrumentation
// library's accumulator, which involves calling
// checkpointer.StartCollection, accumulator.Collect, and
// checkpointer.FinishCollection in sequence.
func (c *Controller) checkpointSingleAccumulator(ctx context.Context, ac *accumulatorCheckpointer) error {
	ckpt := ac.checkpointer.Reader()
	ckpt.Lock()
	defer ckpt.Unlock()

	ac.checkpointer.StartCollection()

	if c.collectTimeout > 0 {
		var cancel context.CancelFunc
		ctx, cancel = context.WithTimeout(ctx, c.collectTimeout)
		defer cancel()
	}

	_ = ac.Accumulator.Collect(ctx)

	var err error
	select {
	case <-ctx.Done():
		err = ctx.Err()
	default:
		// The context wasn't done, ok.
	}

	// Finish the checkpoint whether the accumulator timed out or not.
	if cerr := ac.checkpointer.FinishCollection(); cerr != nil {
		if err == nil {
			err = cerr
		} else {
			err = fmt.Errorf("%s: %w", cerr.Error(), err)
		}
	}

	return err
}

// export calls the exporter with a read lock on the Reader,
// applying the configured export timeout.
func (c *Controller) export(ctx context.Context) error {
	if c.pushTimeout > 0 {
		var cancel context.CancelFunc
		ctx, cancel = context.WithTimeout(ctx, c.pushTimeout)
		defer cancel()
	}

	return c.exporter.Export(ctx, c.resource, c)
}

// ForEach implements export.InstrumentationLibraryReader.
func (c *Controller) ForEach(readerFunc func(l instrumentation.Library, r export.Reader) error) error {
	for _, acPair := range c.accumulatorList() {
		reader := acPair.checkpointer.Reader()
		// TODO: We should not fail fast; instead accumulate errors.
		if err := func() error {
			reader.RLock()
			defer reader.RUnlock()
			return readerFunc(acPair.library, reader)
		}(); err != nil {
			return err
		}
	}
	return nil
}

// IsRunning returns true if the controller was started via Start(),
// indicating that the current export.Reader is being kept
// up-to-date.
func (c *Controller) IsRunning() bool {
	c.lock.Lock()
	defer c.lock.Unlock()
	return c.ticker != nil
}

// Collect requests a collection.  The collection will be skipped if
// the last collection is aged less than the configured collection
// period.
func (c *Controller) Collect(ctx context.Context) error {
	if c.IsRunning() {
		// When there's a non-nil ticker, there's a goroutine
		// computing checkpoints with the collection period.
		return ErrControllerStarted
	}
	if !c.shouldCollect() {
		return nil
	}

	return c.checkpoint(ctx)
}

// shouldCollect returns true if the collector should collect now,
// based on the timestamp, the last collection time, and the
// configured period.
func (c *Controller) shouldCollect() bool {
	c.lock.Lock()
	defer c.lock.Unlock()

	if c.collectPeriod == 0 {
		return true
	}
	now := c.clock.Now()
	if now.Sub(c.collectedTime) < c.collectPeriod {
		return false
	}
	c.collectedTime = now
	return true
}
<commit_msg>Fix TODO, use sync.Map to avoid blocking calls (#2381)<commit_after>// Copyright The OpenTelemetry 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 basic // import "go.opentelemetry.io/otel/sdk/metric/controller/basic"

import (
	"context"
	"fmt"
	"sync"
	"time"

	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/internal/metric/registry"
	"go.opentelemetry.io/otel/metric"
	export "go.opentelemetry.io/otel/sdk/export/metric"
	"go.opentelemetry.io/otel/sdk/instrumentation"
	sdk "go.opentelemetry.io/otel/sdk/metric"
	controllerTime "go.opentelemetry.io/otel/sdk/metric/controller/time"
	"go.opentelemetry.io/otel/sdk/resource"
)

// DefaultPeriod is used for:
//
// - the minimum time between calls to Collect()
// - the timeout for Export()
// - the timeout for Collect().
const DefaultPeriod = 10 * time.Second

// ErrControllerStarted indicates that a controller was started more
// than once.
var ErrControllerStarted = fmt.Errorf("controller already started")

// Controller organizes and synchronizes collection of metric data in
// both "pull" and "push" configurations.  This supports two distinct
// modes:
//
// - Push and Pull: Start() must be called to begin calling the exporter;
//   Collect() is called periodically by a background thread after starting
//   the controller.
// - Pull-Only: Start() is optional in this case, to call Collect periodically.
//   If Start() is not called, Collect() can be called manually to initiate
//   collection
//
// The controller supports mixing push and pull access to metric data
// using the export.Reader RWLock interface.  Collection will
// be blocked by a pull request in the basic controller.
type Controller struct {
	// lock synchronizes Start() and Stop().
	lock                sync.Mutex
	libraries           sync.Map
	checkpointerFactory export.CheckpointerFactory

	resource *resource.Resource
	exporter export.Exporter
	wg       sync.WaitGroup
	stopCh   chan struct{}
	clock    controllerTime.Clock
	ticker   controllerTime.Ticker

	collectPeriod  time.Duration
	collectTimeout time.Duration
	pushTimeout    time.Duration

	// collectedTime is used only in configurations with no
	// exporter, when ticker != nil.
	collectedTime time.Time
}

var _ export.InstrumentationLibraryReader = &Controller{}
var _ metric.MeterProvider = &Controller{}

func (c *Controller) Meter(instrumentationName string, opts ...metric.MeterOption) metric.Meter {
	cfg := metric.NewMeterConfig(opts...)
	library := instrumentation.Library{
		Name:      instrumentationName,
		Version:   cfg.InstrumentationVersion(),
		SchemaURL: cfg.SchemaURL(),
	}

	m, ok := c.libraries.Load(library)
	if !ok {
		checkpointer := c.checkpointerFactory.NewCheckpointer()
		m, _ = c.libraries.LoadOrStore(
			library,
			registry.NewUniqueInstrumentMeterImpl(&accumulatorCheckpointer{
				Accumulator:  sdk.NewAccumulator(checkpointer),
				checkpointer: checkpointer,
				library:      library,
			}))
	}
	return metric.WrapMeterImpl(m.(*registry.UniqueInstrumentMeterImpl))
}

type accumulatorCheckpointer struct {
	*sdk.Accumulator
	checkpointer export.Checkpointer
	library      instrumentation.Library
}

// New constructs a Controller using the provided checkpointer factory
// and options (including optional exporter) to configure a metric
// export pipeline.
func New(checkpointerFactory export.CheckpointerFactory, opts ...Option) *Controller {
	c := &config{
		CollectPeriod:  DefaultPeriod,
		CollectTimeout: DefaultPeriod,
		PushTimeout:    DefaultPeriod,
	}
	for _, opt := range opts {
		opt.apply(c)
	}
	if c.Resource == nil {
		c.Resource = resource.Default()
	} else {
		var err error
		c.Resource, err = resource.Merge(resource.Environment(), c.Resource)
		if err != nil {
			otel.Handle(err)
		}
	}
	return &Controller{
		checkpointerFactory: checkpointerFactory,
		exporter:            c.Exporter,
		resource:            c.Resource,
		stopCh:              nil,
		clock:               controllerTime.RealClock{},

		collectPeriod:  c.CollectPeriod,
		collectTimeout: c.CollectTimeout,
		pushTimeout:    c.PushTimeout,
	}
}

// SetClock supports setting a mock clock for testing.  This must be
// called before Start().
func (c *Controller) SetClock(clock controllerTime.Clock) {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.clock = clock
}

// Resource returns the *resource.Resource associated with this
// controller.
func (c *Controller) Resource() *resource.Resource {
	return c.resource
}

// Start begins a ticker that periodically collects and exports
// metrics with the configured interval.  This is required for calling
// a configured Exporter (see WithExporter) and is otherwise optional
// when only pulling metric data.
//
// The passed context is passed to Collect() and subsequently to
// asynchronous instrument callbacks.  Returns an error when the
// controller was already started.
//
// Note that it is not necessary to Start a controller when only
// pulling data; use the Collect() and ForEach() methods directly in
// this case.
func (c *Controller) Start(ctx context.Context) error {
	c.lock.Lock()
	defer c.lock.Unlock()

	if c.stopCh != nil {
		return ErrControllerStarted
	}

	c.wg.Add(1)
	c.stopCh = make(chan struct{})
	c.ticker = c.clock.Ticker(c.collectPeriod)
	go c.runTicker(ctx, c.stopCh)
	return nil
}

// Stop waits for the background goroutine to return and then collects
// and exports metrics one last time before returning.  The passed
// context is passed to the final Collect() and subsequently to the
// final asynchronous instruments.
//
// Note that Stop() will not cancel an ongoing collection or export.
func (c *Controller) Stop(ctx context.Context) error {
	if lastCollection := func() bool {
		c.lock.Lock()
		defer c.lock.Unlock()

		if c.stopCh == nil {
			return false
		}

		close(c.stopCh)
		c.stopCh = nil
		c.wg.Wait()
		c.ticker.Stop()
		c.ticker = nil
		return true
	}(); !lastCollection {
		return nil
	}
	return c.collect(ctx)
}

// runTicker collection on ticker events until the stop channel is closed.
func (c *Controller) runTicker(ctx context.Context, stopCh chan struct{}) {
	defer c.wg.Done()
	for {
		select {
		case <-stopCh:
			return
		case <-c.ticker.C():
			if err := c.collect(ctx); err != nil {
				otel.Handle(err)
			}
		}
	}
}

// collect computes a checkpoint and optionally exports it.
func (c *Controller) collect(ctx context.Context) error {
	if err := c.checkpoint(ctx); err != nil {
		return err
	}
	if c.exporter == nil {
		return nil
	}

	// Note: this is not subject to collectTimeout.  This blocks the next
	// collection despite collectTimeout because it holds a lock.
	return c.export(ctx)
}

// accumulatorList returns a snapshot of current accumulators
// registered to this controller.  This briefly locks the controller.
func (c *Controller) accumulatorList() []*accumulatorCheckpointer {
	var r []*accumulatorCheckpointer
	c.libraries.Range(func(key, value interface{}) bool {
		acc, ok := value.(*registry.UniqueInstrumentMeterImpl).MeterImpl().(*accumulatorCheckpointer)
		if ok {
			r = append(r, acc)
		}
		return true
	})
	return r
}

// checkpoint calls the Accumulator and Checkpointer interfaces to
// compute the Reader.  This applies the configured collection
// timeout.  Note that this does not try to cancel a Collect or Export
// when Stop() is called.
func (c *Controller) checkpoint(ctx context.Context) error {
	for _, impl := range c.accumulatorList() {
		if err := c.checkpointSingleAccumulator(ctx, impl); err != nil {
			return err
		}
	}
	return nil
}

// checkpointSingleAccumulator checkpoints a single instrumentation
// library's accumulator, which involves calling
// checkpointer.StartCollection, accumulator.Collect, and
// checkpointer.FinishCollection in sequence.
func (c *Controller) checkpointSingleAccumulator(ctx context.Context, ac *accumulatorCheckpointer) error {
	ckpt := ac.checkpointer.Reader()
	ckpt.Lock()
	defer ckpt.Unlock()

	ac.checkpointer.StartCollection()

	if c.collectTimeout > 0 {
		var cancel context.CancelFunc
		ctx, cancel = context.WithTimeout(ctx, c.collectTimeout)
		defer cancel()
	}

	_ = ac.Accumulator.Collect(ctx)

	var err error
	select {
	case <-ctx.Done():
		err = ctx.Err()
	default:
		// The context wasn't done, ok.
	}

	// Finish the checkpoint whether the accumulator timed out or not.
	if cerr := ac.checkpointer.FinishCollection(); cerr != nil {
		if err == nil {
			err = cerr
		} else {
			err = fmt.Errorf("%s: %w", cerr.Error(), err)
		}
	}

	return err
}

// export calls the exporter with a read lock on the Reader,
// applying the configured export timeout.
func (c *Controller) export(ctx context.Context) error {
	if c.pushTimeout > 0 {
		var cancel context.CancelFunc
		ctx, cancel = context.WithTimeout(ctx, c.pushTimeout)
		defer cancel()
	}

	return c.exporter.Export(ctx, c.resource, c)
}

// ForEach implements export.InstrumentationLibraryReader.
func (c *Controller) ForEach(readerFunc func(l instrumentation.Library, r export.Reader) error) error {
	for _, acPair := range c.accumulatorList() {
		reader := acPair.checkpointer.Reader()
		// TODO: We should not fail fast; instead accumulate errors.
		if err := func() error {
			reader.RLock()
			defer reader.RUnlock()
			return readerFunc(acPair.library, reader)
		}(); err != nil {
			return err
		}
	}
	return nil
}

// IsRunning returns true if the controller was started via Start(),
// indicating that the current export.Reader is being kept
// up-to-date.
func (c *Controller) IsRunning() bool {
	c.lock.Lock()
	defer c.lock.Unlock()
	return c.ticker != nil
}

// Collect requests a collection.  The collection will be skipped if
// the last collection is aged less than the configured collection
// period.
func (c *Controller) Collect(ctx context.Context) error {
	if c.IsRunning() {
		// When there's a non-nil ticker, there's a goroutine
		// computing checkpoints with the collection period.
		return ErrControllerStarted
	}
	if !c.shouldCollect() {
		return nil
	}

	return c.checkpoint(ctx)
}

// shouldCollect returns true if the collector should collect now,
// based on the timestamp, the last collection time, and the
// configured period.
func (c *Controller) shouldCollect() bool {
	c.lock.Lock()
	defer c.lock.Unlock()

	if c.collectPeriod == 0 {
		return true
	}
	now := c.clock.Now()
	if now.Sub(c.collectedTime) < c.collectPeriod {
		return false
	}
	c.collectedTime = now
	return true
}
<|endoftext|>


<commit_before>// Copyright ©2014 The gonum 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 goblas

import (
	"fmt"
	"runtime"
	"sync"

	"github.com/gonum/blas"
)

const (
	blockSize   = 64 // b x b matrix
	minParBlock = 4  // minimum number of blocks needed to go parallel
	buffMul     = 4  // how big is the buffer relative to the number of workers
)

// Dgemm computes c := beta * C + alpha * A * B. If tA or tB is blas.Trans,
// A or B is transposed.
// m is the number of rows in A or A transpose
// n is the number of columns in B or B transpose
// k is the columns of A and rows of B
func (Blas) Dgemm(tA, tB blas.Transpose, m, n, k int, alpha float64, a []float64, lda int, b []float64, ldb int, beta float64, c []float64, ldc int) {
	var amat, bmat, cmat general
	if tA == blas.Trans {
		amat = general{
			data:   a,
			rows:   k,
			cols:   m,
			stride: lda,
		}
	} else {
		amat = general{
			data:   a,
			rows:   m,
			cols:   k,
			stride: lda,
		}
	}
	err := amat.check()
	if err != nil {
		panic(err)
	}
	if tB == blas.Trans {
		bmat = general{
			data:   b,
			rows:   n,
			cols:   k,
			stride: ldb,
		}
	} else {
		bmat = general{
			data:   b,
			rows:   k,
			cols:   n,
			stride: ldb,
		}
	}

	err = bmat.check()
	if err != nil {
		panic(err)
	}
	cmat = general{
		data:   c,
		rows:   m,
		cols:   n,
		stride: ldc,
	}
	err = cmat.check()
	if err != nil {
		panic(err)
	}
	if tA != blas.Trans && tA != blas.NoTrans {
		panic(badTranspose)
	}
	if tB != blas.Trans && tB != blas.NoTrans {
		panic(badTranspose)
	}

	// scale c
	if beta != 1 {
		for i := 0; i < m; i++ {
			ctmp := cmat.data[i*cmat.stride : i*cmat.stride+cmat.cols]
			for j := range ctmp {
				ctmp[j] *= beta
			}
		}
	}

	dgemmParallel(tA, tB, amat, bmat, cmat, alpha)
}

func dgemmParallel(tA, tB blas.Transpose, a, b, c general, alpha float64) {
	// dgemmParallel computes a parallel matrix multiplication by partitioning
	// a and b into sub-blocks, and updating c with the multiplication of the sub-block
	// In all cases,
	// A = [ 	A_11	A_12 ... 	A_1j
	//			A_21	A_22 ...	A_2j
	//				...
	//			A_i1	A_i2 ...	A_ij]
	//
	// and same for B. All of the submatrix sizes are blockSize*blockSize except
	// at the edges.
	// In all cases, there is one dimension for each matrix along which
	// C must be updated sequentially.
	// Cij = \sum_k Aik Bki,	(A * B)
	// Cij = \sum_k Aki Bkj,	(A^T * B)
	// Cij = \sum_k Aik Bjk,	(A * B^T)
	// Cij = \sum_k Aki Bjk,	(A^T * B^T)
	//
	// This code computes one {i, j} block sequentially along the k dimension,
	// and computes all of the {i, j} blocks concurrently. This
	// partitioning allows Cij to be updated in-place without race-conditions.
	// Instead of launching a goroutine for each possible concurrent computation,
	// a number of worker goroutines are created and channels are used to pass
	// available and completed cases.
	//
	// http://alexkr.com/docs/matrixmult.pdf is a good reference on matrix-matrix
	// multiplies, though this code does not copy matrices to attempt to eliminate
	// cache misses.

	aTrans := tA == blas.Trans
	bTrans := tB == blas.Trans

	maxKLen, parBlocks := computeNumBlocks(a, b, aTrans, bTrans)
	if parBlocks < minParBlock {
		// The matrix multiplication is small in the dimensions where it can be
		// computed concurrently. Just do it in serial.
		dgemmSerial(tA, tB, a, b, c, alpha)
		return
	}

	nWorkers := runtime.GOMAXPROCS(0)
	if parBlocks < nWorkers {
		nWorkers = parBlocks
	}
	// There is a tradeoff between the workers having to wait for work
	// and a large buffer making operations slow.
	buf := buffMul * nWorkers
	if buf > parBlocks {
		buf = parBlocks
	}

	sendChan := make(chan subMul, buf)

	// Launch workers. A worker receives an {i, j} submatrix of c, and computes
	// A_ik B_ki (or the transposed version) storing the result in c_ij. When the
	// channel is finally closed, it signals to the waitgroup that it has finished
	// computing.
	var wg sync.WaitGroup
	for i := 0; i < nWorkers; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			// Make local copies of otherwise global variables to reduce shared memory.
			// This has a noticable effect on benchmarks in some cases.
			alpha := alpha
			aTrans := aTrans
			bTrans := bTrans
			crows := c.rows
			ccols := c.cols
			for sub := range sendChan {
				i := sub.i
				j := sub.j
				leni := blockSize
				if i+leni > crows {
					leni = crows - i
				}
				lenj := blockSize
				if j+lenj > ccols {
					lenj = ccols - j
				}
				cSub := c.view(i, j, leni, lenj)

				// Compute A_ik B_kj for all k
				for k := 0; k < maxKLen; k += blockSize {
					lenk := blockSize
					if k+lenk > maxKLen {
						lenk = maxKLen - k
					}
					var aSub, bSub general
					if aTrans {
						aSub = a.view(k, i, lenk, leni)
					} else {
						aSub = a.view(i, k, leni, lenk)
					}
					if bTrans {
						bSub = b.view(j, k, lenj, lenk)
					} else {
						bSub = b.view(k, j, lenk, lenj)
					}

					dgemmSerial(tA, tB, aSub, bSub, cSub, alpha)
				}
			}
		}()
	}

	// Send out all of the {i, j} subblocks for computation.
	for i := 0; i < c.rows; i += blockSize {
		for j := 0; j < c.cols; j += blockSize {
			sendChan <- subMul{
				i: i,
				j: j,
			}
		}
	}
	close(sendChan)
	wg.Wait()
	return
}

type subMul struct {
	i, j int // index of block
}

// computeNumBlocks says how many blocks there are to compute. maxKLen says the length of the
// k dimension, parBlocks is the number of blocks that could be computed in parallel
// (the submatrices in i and j). expect is the full number of blocks that will be computed.
func computeNumBlocks(a, b general, aTrans, bTrans bool) (maxKLen, parBlocks int) {
	aRowBlocks := a.rows / blockSize
	if a.rows%blockSize != 0 {
		aRowBlocks++
	}
	aColBlocks := a.cols / blockSize
	if a.cols%blockSize != 0 {
		aColBlocks++
	}
	bRowBlocks := b.rows / blockSize
	if b.rows%blockSize != 0 {
		bRowBlocks++
	}
	bColBlocks := b.cols / blockSize
	if b.cols%blockSize != 0 {
		bColBlocks++
	}

	switch {
	case !aTrans && !bTrans:
		// Cij = \sum_k Aik Bki
		maxKLen = a.cols
		parBlocks = aRowBlocks * bColBlocks
	case aTrans && !bTrans:
		// Cij = \sum_k Aki Bkj
		maxKLen = a.rows
		parBlocks = aColBlocks * bColBlocks
	case !aTrans && bTrans:
		// Cij = \sum_k Aik Bjk
		maxKLen = a.cols
		parBlocks = aRowBlocks * bRowBlocks
	case aTrans && bTrans:
		// Cij = \sum_k Aki Bjk
		maxKLen = a.rows
		parBlocks = aColBlocks * bRowBlocks
	}
	return
}

// dgemmSerial is serial matrix multiply
func dgemmSerial(tA, tB blas.Transpose, a, b, c general, alpha float64) {
	switch {
	case tA == blas.NoTrans && tB == blas.NoTrans:
		dgemmSerialNotNot(a, b, c, alpha)
		return
	case tA == blas.Trans && tB == blas.NoTrans:
		dgemmSerialTransNot(a, b, c, alpha)
		return
	case tA == blas.NoTrans && tB == blas.Trans:
		dgemmSerialNotTrans(a, b, c, alpha)
		return
	case tA == blas.Trans && tB == blas.Trans:
		dgemmSerialTransTrans(a, b, c, alpha)
		return
	default:
		panic("unreachable")
	}
}

// dgemmSerial where neither a nor b are transposed
func dgemmSerialNotNot(a, b, c general, alpha float64) {
	if debug {
		if a.cols != b.rows {
			panic("inner dimension mismatch")
		}
		if a.rows != c.rows {
			panic("outer dimension mismatch")
		}
		if b.cols != c.cols {
			panic("outer dimension mismatch")
		}
	}

	// This style is used instead of the literal [i*stride +j]) is used because
	// approximately 5 times faster as of go 1.3.
	for i := 0; i < a.rows; i++ {
		ctmp := c.data[i*c.stride : i*c.stride+c.cols]
		for l, v := range a.data[i*a.stride : i*a.stride+a.cols] {
			tmp := alpha * v
			if tmp != 0 {
				for j, w := range b.data[l*b.stride : l*b.stride+b.cols] {
					ctmp[j] += tmp * w
				}
			}
		}
	}
}

// dgemmSerial where neither a is transposed and b is not
func dgemmSerialTransNot(a, b, c general, alpha float64) {
	if debug {
		if a.rows != b.rows {
			fmt.Println(a.rows, b.rows)
			panic("inner dimension mismatch")
		}
		if a.cols != c.rows {
			panic("outer dimension mismatch")
		}
		if b.cols != c.cols {
			panic("outer dimension mismatch")
		}
	}

	// This style is used instead of the literal [i*stride +j]) is used because
	// approximately 5 times faster as of go 1.3.
	for l := 0; l < a.rows; l++ {
		btmp := b.data[l*b.stride : l*b.stride+b.cols]
		for i, v := range a.data[l*a.stride : l*a.stride+a.cols] {
			tmp := alpha * v
			ctmp := c.data[i*c.stride : i*c.stride+c.cols]
			if tmp != 0 {
				for j, w := range btmp {
					ctmp[j] += tmp * w
				}
			}
		}
	}
}

// dgemmSerial where neither a is not transposed and b is
func dgemmSerialNotTrans(a, b, c general, alpha float64) {
	if debug {
		if a.cols != b.cols {
			panic("inner dimension mismatch")
		}
		if a.rows != c.rows {
			panic("outer dimension mismatch")
		}
		if b.rows != c.cols {
			panic("outer dimension mismatch")
		}
	}

	// This style is used instead of the literal [i*stride +j]) is used because
	// approximately 5 times faster as of go 1.3.
	for i := 0; i < a.rows; i++ {
		atmp := a.data[i*a.stride : i*a.stride+a.cols]
		ctmp := c.data[i*c.stride : i*c.stride+c.cols]
		for j := 0; j < b.rows; j++ {
			var tmp float64
			for l, v := range b.data[j*b.stride : j*b.stride+b.cols] {
				tmp += atmp[l] * v
			}
			ctmp[j] += alpha * tmp
		}
	}

}

// dgemmSerial where both are transposed
func dgemmSerialTransTrans(a, b, c general, alpha float64) {
	if debug {
		if a.rows != b.cols {
			panic("inner dimension mismatch")
		}
		if a.cols != c.rows {
			panic("outer dimension mismatch")
		}
		if b.rows != c.cols {
			panic("outer dimension mismatch")
		}
	}

	// This style is used instead of the literal [i*stride +j]) is used because
	// approximately 5 times faster as of go 1.3.
	for l := 0; l < a.rows; l++ {
		for i, v := range a.data[l*a.stride : l*a.stride+a.cols] {
			ctmp := c.data[i*c.stride : i*c.stride+c.cols]
			if v != 0 {
				tmp := alpha * v
				for j := 0; j < b.rows; j++ {
					ctmp[j] += tmp * b.data[j*b.stride+l]
				}
			}
		}
	}
}
<commit_msg>removed unnecessary return<commit_after>// Copyright ©2014 The gonum 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 goblas

import (
	"fmt"
	"runtime"
	"sync"

	"github.com/gonum/blas"
)

const (
	blockSize   = 64 // b x b matrix
	minParBlock = 4  // minimum number of blocks needed to go parallel
	buffMul     = 4  // how big is the buffer relative to the number of workers
)

// Dgemm computes c := beta * C + alpha * A * B. If tA or tB is blas.Trans,
// A or B is transposed.
// m is the number of rows in A or A transpose
// n is the number of columns in B or B transpose
// k is the columns of A and rows of B
func (Blas) Dgemm(tA, tB blas.Transpose, m, n, k int, alpha float64, a []float64, lda int, b []float64, ldb int, beta float64, c []float64, ldc int) {
	var amat, bmat, cmat general
	if tA == blas.Trans {
		amat = general{
			data:   a,
			rows:   k,
			cols:   m,
			stride: lda,
		}
	} else {
		amat = general{
			data:   a,
			rows:   m,
			cols:   k,
			stride: lda,
		}
	}
	err := amat.check()
	if err != nil {
		panic(err)
	}
	if tB == blas.Trans {
		bmat = general{
			data:   b,
			rows:   n,
			cols:   k,
			stride: ldb,
		}
	} else {
		bmat = general{
			data:   b,
			rows:   k,
			cols:   n,
			stride: ldb,
		}
	}

	err = bmat.check()
	if err != nil {
		panic(err)
	}
	cmat = general{
		data:   c,
		rows:   m,
		cols:   n,
		stride: ldc,
	}
	err = cmat.check()
	if err != nil {
		panic(err)
	}
	if tA != blas.Trans && tA != blas.NoTrans {
		panic(badTranspose)
	}
	if tB != blas.Trans && tB != blas.NoTrans {
		panic(badTranspose)
	}

	// scale c
	if beta != 1 {
		for i := 0; i < m; i++ {
			ctmp := cmat.data[i*cmat.stride : i*cmat.stride+cmat.cols]
			for j := range ctmp {
				ctmp[j] *= beta
			}
		}
	}

	dgemmParallel(tA, tB, amat, bmat, cmat, alpha)
}

func dgemmParallel(tA, tB blas.Transpose, a, b, c general, alpha float64) {
	// dgemmParallel computes a parallel matrix multiplication by partitioning
	// a and b into sub-blocks, and updating c with the multiplication of the sub-block
	// In all cases,
	// A = [ 	A_11	A_12 ... 	A_1j
	//			A_21	A_22 ...	A_2j
	//				...
	//			A_i1	A_i2 ...	A_ij]
	//
	// and same for B. All of the submatrix sizes are blockSize*blockSize except
	// at the edges.
	// In all cases, there is one dimension for each matrix along which
	// C must be updated sequentially.
	// Cij = \sum_k Aik Bki,	(A * B)
	// Cij = \sum_k Aki Bkj,	(A^T * B)
	// Cij = \sum_k Aik Bjk,	(A * B^T)
	// Cij = \sum_k Aki Bjk,	(A^T * B^T)
	//
	// This code computes one {i, j} block sequentially along the k dimension,
	// and computes all of the {i, j} blocks concurrently. This
	// partitioning allows Cij to be updated in-place without race-conditions.
	// Instead of launching a goroutine for each possible concurrent computation,
	// a number of worker goroutines are created and channels are used to pass
	// available and completed cases.
	//
	// http://alexkr.com/docs/matrixmult.pdf is a good reference on matrix-matrix
	// multiplies, though this code does not copy matrices to attempt to eliminate
	// cache misses.

	aTrans := tA == blas.Trans
	bTrans := tB == blas.Trans

	maxKLen, parBlocks := computeNumBlocks(a, b, aTrans, bTrans)
	if parBlocks < minParBlock {
		// The matrix multiplication is small in the dimensions where it can be
		// computed concurrently. Just do it in serial.
		dgemmSerial(tA, tB, a, b, c, alpha)
		return
	}

	nWorkers := runtime.GOMAXPROCS(0)
	if parBlocks < nWorkers {
		nWorkers = parBlocks
	}
	// There is a tradeoff between the workers having to wait for work
	// and a large buffer making operations slow.
	buf := buffMul * nWorkers
	if buf > parBlocks {
		buf = parBlocks
	}

	sendChan := make(chan subMul, buf)

	// Launch workers. A worker receives an {i, j} submatrix of c, and computes
	// A_ik B_ki (or the transposed version) storing the result in c_ij. When the
	// channel is finally closed, it signals to the waitgroup that it has finished
	// computing.
	var wg sync.WaitGroup
	for i := 0; i < nWorkers; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			// Make local copies of otherwise global variables to reduce shared memory.
			// This has a noticable effect on benchmarks in some cases.
			alpha := alpha
			aTrans := aTrans
			bTrans := bTrans
			crows := c.rows
			ccols := c.cols
			for sub := range sendChan {
				i := sub.i
				j := sub.j
				leni := blockSize
				if i+leni > crows {
					leni = crows - i
				}
				lenj := blockSize
				if j+lenj > ccols {
					lenj = ccols - j
				}
				cSub := c.view(i, j, leni, lenj)

				// Compute A_ik B_kj for all k
				for k := 0; k < maxKLen; k += blockSize {
					lenk := blockSize
					if k+lenk > maxKLen {
						lenk = maxKLen - k
					}
					var aSub, bSub general
					if aTrans {
						aSub = a.view(k, i, lenk, leni)
					} else {
						aSub = a.view(i, k, leni, lenk)
					}
					if bTrans {
						bSub = b.view(j, k, lenj, lenk)
					} else {
						bSub = b.view(k, j, lenk, lenj)
					}

					dgemmSerial(tA, tB, aSub, bSub, cSub, alpha)
				}
			}
		}()
	}

	// Send out all of the {i, j} subblocks for computation.
	for i := 0; i < c.rows; i += blockSize {
		for j := 0; j < c.cols; j += blockSize {
			sendChan <- subMul{
				i: i,
				j: j,
			}
		}
	}
	close(sendChan)
	wg.Wait()
}

type subMul struct {
	i, j int // index of block
}

// computeNumBlocks says how many blocks there are to compute. maxKLen says the length of the
// k dimension, parBlocks is the number of blocks that could be computed in parallel
// (the submatrices in i and j). expect is the full number of blocks that will be computed.
func computeNumBlocks(a, b general, aTrans, bTrans bool) (maxKLen, parBlocks int) {
	aRowBlocks := a.rows / blockSize
	if a.rows%blockSize != 0 {
		aRowBlocks++
	}
	aColBlocks := a.cols / blockSize
	if a.cols%blockSize != 0 {
		aColBlocks++
	}
	bRowBlocks := b.rows / blockSize
	if b.rows%blockSize != 0 {
		bRowBlocks++
	}
	bColBlocks := b.cols / blockSize
	if b.cols%blockSize != 0 {
		bColBlocks++
	}

	switch {
	case !aTrans && !bTrans:
		// Cij = \sum_k Aik Bki
		maxKLen = a.cols
		parBlocks = aRowBlocks * bColBlocks
	case aTrans && !bTrans:
		// Cij = \sum_k Aki Bkj
		maxKLen = a.rows
		parBlocks = aColBlocks * bColBlocks
	case !aTrans && bTrans:
		// Cij = \sum_k Aik Bjk
		maxKLen = a.cols
		parBlocks = aRowBlocks * bRowBlocks
	case aTrans && bTrans:
		// Cij = \sum_k Aki Bjk
		maxKLen = a.rows
		parBlocks = aColBlocks * bRowBlocks
	}
	return
}

// dgemmSerial is serial matrix multiply
func dgemmSerial(tA, tB blas.Transpose, a, b, c general, alpha float64) {
	switch {
	case tA == blas.NoTrans && tB == blas.NoTrans:
		dgemmSerialNotNot(a, b, c, alpha)
		return
	case tA == blas.Trans && tB == blas.NoTrans:
		dgemmSerialTransNot(a, b, c, alpha)
		return
	case tA == blas.NoTrans && tB == blas.Trans:
		dgemmSerialNotTrans(a, b, c, alpha)
		return
	case tA == blas.Trans && tB == blas.Trans:
		dgemmSerialTransTrans(a, b, c, alpha)
		return
	default:
		panic("unreachable")
	}
}

// dgemmSerial where neither a nor b are transposed
func dgemmSerialNotNot(a, b, c general, alpha float64) {
	if debug {
		if a.cols != b.rows {
			panic("inner dimension mismatch")
		}
		if a.rows != c.rows {
			panic("outer dimension mismatch")
		}
		if b.cols != c.cols {
			panic("outer dimension mismatch")
		}
	}

	// This style is used instead of the literal [i*stride +j]) is used because
	// approximately 5 times faster as of go 1.3.
	for i := 0; i < a.rows; i++ {
		ctmp := c.data[i*c.stride : i*c.stride+c.cols]
		for l, v := range a.data[i*a.stride : i*a.stride+a.cols] {
			tmp := alpha * v
			if tmp != 0 {
				for j, w := range b.data[l*b.stride : l*b.stride+b.cols] {
					ctmp[j] += tmp * w
				}
			}
		}
	}
}

// dgemmSerial where neither a is transposed and b is not
func dgemmSerialTransNot(a, b, c general, alpha float64) {
	if debug {
		if a.rows != b.rows {
			fmt.Println(a.rows, b.rows)
			panic("inner dimension mismatch")
		}
		if a.cols != c.rows {
			panic("outer dimension mismatch")
		}
		if b.cols != c.cols {
			panic("outer dimension mismatch")
		}
	}

	// This style is used instead of the literal [i*stride +j]) is used because
	// approximately 5 times faster as of go 1.3.
	for l := 0; l < a.rows; l++ {
		btmp := b.data[l*b.stride : l*b.stride+b.cols]
		for i, v := range a.data[l*a.stride : l*a.stride+a.cols] {
			tmp := alpha * v
			ctmp := c.data[i*c.stride : i*c.stride+c.cols]
			if tmp != 0 {
				for j, w := range btmp {
					ctmp[j] += tmp * w
				}
			}
		}
	}
}

// dgemmSerial where neither a is not transposed and b is
func dgemmSerialNotTrans(a, b, c general, alpha float64) {
	if debug {
		if a.cols != b.cols {
			panic("inner dimension mismatch")
		}
		if a.rows != c.rows {
			panic("outer dimension mismatch")
		}
		if b.rows != c.cols {
			panic("outer dimension mismatch")
		}
	}

	// This style is used instead of the literal [i*stride +j]) is used because
	// approximately 5 times faster as of go 1.3.
	for i := 0; i < a.rows; i++ {
		atmp := a.data[i*a.stride : i*a.stride+a.cols]
		ctmp := c.data[i*c.stride : i*c.stride+c.cols]
		for j := 0; j < b.rows; j++ {
			var tmp float64
			for l, v := range b.data[j*b.stride : j*b.stride+b.cols] {
				tmp += atmp[l] * v
			}
			ctmp[j] += alpha * tmp
		}
	}

}

// dgemmSerial where both are transposed
func dgemmSerialTransTrans(a, b, c general, alpha float64) {
	if debug {
		if a.rows != b.cols {
			panic("inner dimension mismatch")
		}
		if a.cols != c.rows {
			panic("outer dimension mismatch")
		}
		if b.rows != c.cols {
			panic("outer dimension mismatch")
		}
	}

	// This style is used instead of the literal [i*stride +j]) is used because
	// approximately 5 times faster as of go 1.3.
	for l := 0; l < a.rows; l++ {
		for i, v := range a.data[l*a.stride : l*a.stride+a.cols] {
			ctmp := c.data[i*c.stride : i*c.stride+c.cols]
			if v != 0 {
				tmp := alpha * v
				for j := 0; j < b.rows; j++ {
					ctmp[j] += tmp * b.data[j*b.stride+l]
				}
			}
		}
	}
}
<|endoftext|>