<commit_before>package controller

import (
	"fmt"
	"sync/atomic"
	"time"

	"golang.org/x/net/context"

	"github.com/giantswarm/inago/task"
)

func (c controller) executeTaskAction(f func(ctx context.Context, req Request) (*task.Task, error), ctx context.Context, req Request) error {
	taskObject, err := f(ctx, req)
	if err != nil {
		return maskAny(err)
	}
	closer := make(<-chan struct{})
	taskObject, err = c.WaitForTask(ctx, taskObject.ID, closer)
	if err != nil {
		return maskAny(err)
	}
	if task.HasFailedStatus(taskObject) {
		return maskAny(fmt.Errorf(taskObject.Error))
	}
	return nil
}

func (c controller) getNumRunningSlices(req Request) (int, error) {
	groupStatus, err := c.groupStatus(req)
	if IsUnitNotFound(err) {
		return 0, nil
	} else if err != nil {
		return 0, maskAny(err)
	}

	grouped, err := UnitStatusList(groupStatus).Group()
	if err != nil {
		return 0, maskAny(err)
	}

	var sliceIDs []string
	for _, us := range grouped {
		groupedStatuses := grouped.unitStatusesBySliceID(us.SliceID)
		if len(groupedStatuses) > 1 {
			// This group has an inconsistent state. Thus we do not consider it
			// running.
			continue
		}

		aggregator := Aggregator{
			Logger: c.Config.Logger,
		}
		ok, err := aggregator.unitHasStatus(groupedStatuses[0], StatusRunning)
		if err != nil {
			return 0, maskAny(err)
		}
		if !ok {
			continue
		}

		sliceIDs = append(sliceIDs, us.SliceID)
	}

	return len(sliceIDs), nil
}

func (c controller) isGroupRemovalAllowed(req Request, minAlive int) (bool, error) {
	numRunning, err := c.getNumRunningSlices(req)
	if err != nil {
		return false, maskAny(err)
	}

	if numRunning > minAlive {
		return true, nil
	}

	return false, nil
}

func (c controller) isGroupAdditionAllowed(req Request, maxGrowth int) (bool, error) {
	numRunning, err := c.getNumRunningSlices(req)
	if err != nil {
		return false, maskAny(err)
	}

	if numRunning < maxGrowth {
		return true, nil
	}

	return false, nil
}

func (c controller) addFirst(ctx context.Context, req Request, opts UpdateOptions) error {
	newReq, err := c.runAddWorker(ctx, req, opts)
	if err != nil {
		return maskAny(err)
	}
	n, err := c.getNumRunningSlices(newReq)
	if err != nil {
		return maskAny(err)
	}
	if n != len(newReq.SliceIDs) {
		return maskAnyf(updateFailedError, "slice not running: %v", newReq.SliceIDs)
	}
	err = c.runRemoveWorker(ctx, req)
	if err != nil {
		return maskAny(err)
	}

	return nil
}

func (c controller) runAddWorker(ctx context.Context, req Request, opts UpdateOptions) (Request, error) {
	// Create new random IDs.
	req.DesiredSlices = 1
	req.SliceIDs = nil
	newReq, err := c.ExtendWithRandomSliceIDs(req)
	if err != nil {
	       return Request{}, maskAny(err)
	}

	// Submit.
	if err := c.executeTaskAction(c.Submit, ctx, newReq); err != nil {
		return Request{}, maskAny(err)
	}

	// Start.
	if err := c.executeTaskAction(c.Start, ctx, newReq); err != nil {
		return Request{}, maskAny(err)
	}

	time.Sleep(time.Duration(opts.ReadySecs) * time.Second)

	return req, nil
}

func (c controller) removeFirst(ctx context.Context, req Request, opts UpdateOptions) error {
	err := c.runRemoveWorker(ctx, req)
	if err != nil {
		return maskAny(err)
	}
	newReq, err := c.runAddWorker(ctx, req, opts)
	if err != nil {
		return maskAny(err)
	}
	n, err := c.getNumRunningSlices(newReq)
	if err != nil {
		return maskAny(err)
	}
	if n != len(newReq.SliceIDs) {
		return maskAnyf(updateFailedError, "slice not running: %v", newReq.SliceIDs)
	}

	return nil
}

func (c controller) runRemoveWorker(ctx context.Context, req Request) error {
	// Stop.
	if err := c.executeTaskAction(c.Stop, ctx, req); err != nil {
		return maskAny(err)
	}

	// Destroy.
	if err := c.executeTaskAction(c.Destroy, ctx, req); err != nil {
		return maskAny(err)
	}
	return nil
}

// UpdateOptions represents the options defining the strategy of an update
// process. Lets have a look at how the update process of 3 group slices would
// look like using the given options.
//
//     MaxGrowth    1
//     MinAlive     2
//     ReadySecs    30
//
//     @1 (running)  ->  @1 (stopped/destroyed)
//     @2 (running)  ->  @2 (running)            ->  @2 (stopped/destroyed)
//     @3 (running)  ->  @3 (running)            ->  @3 (running)            ->  @3 (stopped/destroyed)
//                   ->  @1 (submitted/running)  ->  @1 (running)            ->  @1 (running)            ->  @1 (running)
//                                               ->  @2 (submitted/running)  ->  @2 (running)            ->  @2 (running)
//                                                                           ->  @3 (submitted/running)  ->  @3 (running)
//
type UpdateOptions struct {
	// MaxGrowth represents the number of groups allowed to be added at a given
	// time. No more than MaxGrowth groups will be added at the same time during
	// the update process.
	MaxGrowth int

	// MinAlive represents the number of groups required to stay healthy during
	// the update process. No more than MinAlive groups will be removed at the
	// same time during the update process.
	MinAlive int

	// ReadySecs represents the number of seconds required to wait before ending
	// the update process of one group and starting the update process of another
	// group. This is basically a cool down where the update process sleeps
	// before updating the next group.
	ReadySecs int
}

func (c controller) UpdateWithStrategy(ctx context.Context, req Request, opts UpdateOptions) error {
	fail := make(chan error, 1)
	numTotal := len(req.SliceIDs)
	done := make(chan struct{}, numTotal)
	var addInProgress int64
	var removeInProgress int64

	if !req.isSliceable() {
		return maskAnyf(updateNotAllowedError, "cannot update unslicable group")
	}

	for _, sliceID := range req.SliceIDs {
		if sliceID == "" {
			return maskAnyf(updateNotAllowedError, "group misses slice ID")
		}
	}

	if numTotal < opts.MinAlive {
		return maskAnyf(updateNotAllowedError, "invalid min alive option")
	}

	for _, sliceID := range req.SliceIDs {
		newReq := req
		newReq.SliceIDs = []string{sliceID}

		for {
			// add
			maxGrowth := opts.MaxGrowth + numTotal - opts.MinAlive - int(addInProgress)
			ok, err := c.isGroupAdditionAllowed(req, maxGrowth)
			if err != nil {
				return maskAny(err)
			}
			if ok {
				go func() {
					atomic.AddInt64(&addInProgress, 1)
					err := c.addFirst(ctx, newReq, opts)
					if err != nil {
						fail <- maskAny(err)
						return
					}
					atomic.AddInt64(&addInProgress, -1)
					done <- struct{}{}
				}()

				break
			}

			// remove
			minAlive := opts.MinAlive + int(removeInProgress)
			ok, err = c.isGroupRemovalAllowed(req, minAlive)
			if err != nil {
				return maskAny(err)
			}
			if ok {
				go func() {
					atomic.AddInt64(&removeInProgress, 1)
					err := c.removeFirst(ctx, newReq, opts)
					if err != nil {
						fail <- maskAny(err)
						return
					}
					atomic.AddInt64(&removeInProgress, -1)
					done <- struct{}{}
				}()

				break
			}

			time.Sleep(c.WaitSleep)
		}
	}

	tc := 0
	for {
		select {
		case err := <-fail:
			return maskAny(err)
		case <-done:
			tc++
			if tc == numTotal {
				return nil
			}
		case <-time.After(c.WaitTimeout):
			return maskAny(waitTimeoutReachedError)
		}
	}
}
<commit_msg>ENGLISH<commit_after>package controller

import (
	"fmt"
	"sync/atomic"
	"time"

	"golang.org/x/net/context"

	"github.com/giantswarm/inago/task"
)

func (c controller) executeTaskAction(f func(ctx context.Context, req Request) (*task.Task, error), ctx context.Context, req Request) error {
	taskObject, err := f(ctx, req)
	if err != nil {
		return maskAny(err)
	}
	closer := make(<-chan struct{})
	taskObject, err = c.WaitForTask(ctx, taskObject.ID, closer)
	if err != nil {
		return maskAny(err)
	}
	if task.HasFailedStatus(taskObject) {
		return maskAny(fmt.Errorf(taskObject.Error))
	}
	return nil
}

func (c controller) getNumRunningSlices(req Request) (int, error) {
	groupStatus, err := c.groupStatus(req)
	if IsUnitNotFound(err) {
		return 0, nil
	} else if err != nil {
		return 0, maskAny(err)
	}

	grouped, err := UnitStatusList(groupStatus).Group()
	if err != nil {
		return 0, maskAny(err)
	}

	var sliceIDs []string
	for _, us := range grouped {
		groupedStatuses := grouped.unitStatusesBySliceID(us.SliceID)
		if len(groupedStatuses) > 1 {
			// This group has an inconsistent state. Thus we do not consider it
			// running.
			continue
		}

		aggregator := Aggregator{
			Logger: c.Config.Logger,
		}
		ok, err := aggregator.unitHasStatus(groupedStatuses[0], StatusRunning)
		if err != nil {
			return 0, maskAny(err)
		}
		if !ok {
			continue
		}

		sliceIDs = append(sliceIDs, us.SliceID)
	}

	return len(sliceIDs), nil
}

func (c controller) isGroupRemovalAllowed(req Request, minAlive int) (bool, error) {
	numRunning, err := c.getNumRunningSlices(req)
	if err != nil {
		return false, maskAny(err)
	}

	if numRunning > minAlive {
		return true, nil
	}

	return false, nil
}

func (c controller) isGroupAdditionAllowed(req Request, maxGrowth int) (bool, error) {
	numRunning, err := c.getNumRunningSlices(req)
	if err != nil {
		return false, maskAny(err)
	}

	if numRunning < maxGrowth {
		return true, nil
	}

	return false, nil
}

func (c controller) addFirst(ctx context.Context, req Request, opts UpdateOptions) error {
	newReq, err := c.runAddWorker(ctx, req, opts)
	if err != nil {
		return maskAny(err)
	}
	n, err := c.getNumRunningSlices(newReq)
	if err != nil {
		return maskAny(err)
	}
	if n != len(newReq.SliceIDs) {
		return maskAnyf(updateFailedError, "slice not running: %v", newReq.SliceIDs)
	}
	err = c.runRemoveWorker(ctx, req)
	if err != nil {
		return maskAny(err)
	}

	return nil
}

func (c controller) runAddWorker(ctx context.Context, req Request, opts UpdateOptions) (Request, error) {
	// Create new random IDs.
	req.DesiredSlices = 1
	req.SliceIDs = nil
	newReq, err := c.ExtendWithRandomSliceIDs(req)
	if err != nil {
	       return Request{}, maskAny(err)
	}

	// Submit.
	if err := c.executeTaskAction(c.Submit, ctx, newReq); err != nil {
		return Request{}, maskAny(err)
	}

	// Start.
	if err := c.executeTaskAction(c.Start, ctx, newReq); err != nil {
		return Request{}, maskAny(err)
	}

	time.Sleep(time.Duration(opts.ReadySecs) * time.Second)

	return req, nil
}

func (c controller) removeFirst(ctx context.Context, req Request, opts UpdateOptions) error {
	err := c.runRemoveWorker(ctx, req)
	if err != nil {
		return maskAny(err)
	}
	newReq, err := c.runAddWorker(ctx, req, opts)
	if err != nil {
		return maskAny(err)
	}
	n, err := c.getNumRunningSlices(newReq)
	if err != nil {
		return maskAny(err)
	}
	if n != len(newReq.SliceIDs) {
		return maskAnyf(updateFailedError, "slice not running: %v", newReq.SliceIDs)
	}

	return nil
}

func (c controller) runRemoveWorker(ctx context.Context, req Request) error {
	// Stop.
	if err := c.executeTaskAction(c.Stop, ctx, req); err != nil {
		return maskAny(err)
	}

	// Destroy.
	if err := c.executeTaskAction(c.Destroy, ctx, req); err != nil {
		return maskAny(err)
	}
	return nil
}

// UpdateOptions represents the options defining the strategy of an update
// process. Lets have a look at how the update process of 3 group slices would
// look like using the given options.
//
//     MaxGrowth    1
//     MinAlive     2
//     ReadySecs    30
//
//     @1 (running)  ->  @1 (stopped/destroyed)
//     @2 (running)  ->  @2 (running)            ->  @2 (stopped/destroyed)
//     @3 (running)  ->  @3 (running)            ->  @3 (running)            ->  @3 (stopped/destroyed)
//                   ->  @1 (submitted/running)  ->  @1 (running)            ->  @1 (running)            ->  @1 (running)
//                                               ->  @2 (submitted/running)  ->  @2 (running)            ->  @2 (running)
//                                                                           ->  @3 (submitted/running)  ->  @3 (running)
//
type UpdateOptions struct {
	// MaxGrowth represents the number of groups allowed to be added at a given
	// time. No more than MaxGrowth groups will be added at the same time during
	// the update process.
	MaxGrowth int

	// MinAlive represents the number of groups required to stay healthy during
	// the update process. No more than MinAlive groups will be removed at the
	// same time during the update process.
	MinAlive int

	// ReadySecs represents the number of seconds required to wait before ending
	// the update process of one group and starting the update process of another
	// group. This is basically a cool down where the update process sleeps
	// before updating the next group.
	ReadySecs int
}

func (c controller) UpdateWithStrategy(ctx context.Context, req Request, opts UpdateOptions) error {
	fail := make(chan error, 1)
	numTotal := len(req.SliceIDs)
	done := make(chan struct{}, numTotal)
	var addInProgress int64
	var removeInProgress int64

	if !req.isSliceable() {
		return maskAnyf(updateNotAllowedError, "cannot update unsliceable group")
	}

	for _, sliceID := range req.SliceIDs {
		if sliceID == "" {
			return maskAnyf(updateNotAllowedError, "group misses slice ID")
		}
	}

	if numTotal < opts.MinAlive {
		return maskAnyf(updateNotAllowedError, "invalid min alive option")
	}

	for _, sliceID := range req.SliceIDs {
		newReq := req
		newReq.SliceIDs = []string{sliceID}

		for {
			// add
			maxGrowth := opts.MaxGrowth + numTotal - opts.MinAlive - int(addInProgress)
			ok, err := c.isGroupAdditionAllowed(req, maxGrowth)
			if err != nil {
				return maskAny(err)
			}
			if ok {
				go func() {
					atomic.AddInt64(&addInProgress, 1)
					err := c.addFirst(ctx, newReq, opts)
					if err != nil {
						fail <- maskAny(err)
						return
					}
					atomic.AddInt64(&addInProgress, -1)
					done <- struct{}{}
				}()

				break
			}

			// remove
			minAlive := opts.MinAlive + int(removeInProgress)
			ok, err = c.isGroupRemovalAllowed(req, minAlive)
			if err != nil {
				return maskAny(err)
			}
			if ok {
				go func() {
					atomic.AddInt64(&removeInProgress, 1)
					err := c.removeFirst(ctx, newReq, opts)
					if err != nil {
						fail <- maskAny(err)
						return
					}
					atomic.AddInt64(&removeInProgress, -1)
					done <- struct{}{}
				}()

				break
			}

			time.Sleep(c.WaitSleep)
		}
	}

	tc := 0
	for {
		select {
		case err := <-fail:
			return maskAny(err)
		case <-done:
			tc++
			if tc == numTotal {
				return nil
			}
		case <-time.After(c.WaitTimeout):
			return maskAny(waitTimeoutReachedError)
		}
	}
}
<|endoftext|>


<commit_before>package controllers

import (
	"github.com/codegangsta/martini-contrib/render"
	"github.com/go-martini/martini"
	"github.com/rolandjudd/thingstodo/models"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
	"time"
)

// Called on a GET to /events
// Returns a list of all events
func GetAllEvents(db *mgo.Database, r render.Render) {

	var events []models.Event
	err := db.C("events").Find(bson.M{}).All(&events)
	if err != nil {
		panic(err)
	}

	r.JSON(200, events)
}

// Called on a Get to /events/:id
// Returns a single event with the id :id
func GetEvent(db *mgo.Database, r render.Render, p martini.Params) {

	var id bson.ObjectId
	var event models.Event

	if bson.IsObjectIdHex(p["id"]) {
		id = bson.ObjectIdHex(p["id"])
	} else {
		r.JSON(400, "Bad Request: Invalid ObjectId")
		return
	}

	err := db.C("events").FindId(id).One(&event)

	// TODO Check for 404

	if err != nil {
		panic(err)
	}

	r.JSON(200, event)

}

// Called on a POST to /events
// Assuming valid event; adds the given event
func AddEvent(db *mgo.Database, r render.Render, event models.Event) {

	// Create a unique id
	event.Id = bson.NewObjectId()

	// TODO Should be the user Id
	event.CreatedBy = bson.NewObjectId()
	event.CreatedAt = time.Now().UTC()

	err := db.C("events").Insert(event)
	if err != nil {
		panic(err)
	}

	r.JSON(201, event)
}
<commit_msg>Filter events that are over, and sort events returned by the end_time<commit_after>package controllers

import (
	"github.com/codegangsta/martini-contrib/render"
	"github.com/go-martini/martini"
	"github.com/rolandjudd/thingstodo/models"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
	"time"
)

// Called on a GET to /events
// Returns a list of all events
func GetAllEvents(db *mgo.Database, r render.Render) {

	var events []models.Event

	// Only display events that haven't ended, and sort by end_time
	err := db.C("events").Find(bson.M{
		"end_time": bson.M{
			"$gt": time.Now().UTC(),
		},
	}).Sort("-end_time").All(&events)

	if err != nil {
		panic(err)
	}

	r.JSON(200, events)
}

// Called on a Get to /events/:id
// Returns a single event with the id :id
func GetEvent(db *mgo.Database, r render.Render, p martini.Params) {

	var id bson.ObjectId
	var event models.Event

	if bson.IsObjectIdHex(p["id"]) {
		id = bson.ObjectIdHex(p["id"])
	} else {
		r.JSON(400, "Bad Request: Invalid ObjectId")
		return
	}

	err := db.C("events").FindId(id).One(&event)

	// TODO Check for 404

	if err != nil {
		panic(err)
	}

	r.JSON(200, event)

}

// Called on a POST to /events
// Assuming valid event; adds the given event
func AddEvent(db *mgo.Database, r render.Render, event models.Event) {

	// Create a unique id
	event.Id = bson.NewObjectId()

	// TODO Should be the user Id
	event.CreatedBy = bson.NewObjectId()
	event.CreatedAt = time.Now().UTC()

	err := db.C("events").Insert(event)
	if err != nil {
		panic(err)
	}

	r.JSON(201, event)
}
<|endoftext|>


<commit_before>package openstack

import (
	"context"
	"fmt"
	"log"
	"time"

	"github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumeactions"

	"github.com/gophercloud/gophercloud"
	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
	"github.com/gophercloud/gophercloud/openstack/imageservice/v2/images"
	"github.com/hashicorp/packer/helper/multistep"
	"github.com/hashicorp/packer/packer"
)

type stepCreateImage struct {
	UseBlockStorageVolume bool
}

func (s *stepCreateImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
	config := state.Get("config").(*Config)
	server := state.Get("server").(*servers.Server)
	ui := state.Get("ui").(packer.Ui)

	// We need the v2 compute client
	computeClient, err := config.computeV2Client()
	if err != nil {
		err = fmt.Errorf("Error initializing compute client: %s", err)
		state.Put("error", err)
		return multistep.ActionHalt
	}

	// We need the v2 image client
	imageClient, err := config.imageV2Client()
	if err != nil {
		err = fmt.Errorf("Error initializing image service client: %s", err)
		state.Put("error", err)
		return multistep.ActionHalt
	}

	// Create the image.
	// Image source depends on the type of the Compute instance. It can be
	// Block Storage service volume or regular Compute service local volume.
	ui.Say(fmt.Sprintf("Creating the image: %s", config.ImageName))
	var imageId string
	var blockStorageClient *gophercloud.ServiceClient
	if s.UseBlockStorageVolume {
		// We need the v3 block storage client.
		blockStorageClient, err = config.blockStorageV3Client()
		if err != nil {
			err = fmt.Errorf("Error initializing block storage client: %s", err)
			state.Put("error", err)
			return multistep.ActionHalt
		}
		volume := state.Get("volume_id").(string)

		// set ImageMetadata before uploading to glance so the new image captured the desired values
		if len(config.ImageMetadata) > 0 {
			err = volumeactions.SetImageMetadata(blockStorageClient, volume, volumeactions.ImageMetadataOpts{
				Metadata: config.ImageMetadata,
			}).ExtractErr()
			if err != nil {
				err := fmt.Errorf("Error setting image metadata: %s", err)
				state.Put("error", err)
				ui.Error(err.Error())
			}
		}

		image, err := volumeactions.UploadImage(blockStorageClient, volume, volumeactions.UploadImageOpts{
			DiskFormat: config.ImageDiskFormat,
			ImageName:  config.ImageName,
		}).Extract()
		if err != nil {
			err := fmt.Errorf("Error creating image: %s", err)
			state.Put("error", err)
			ui.Error(err.Error())
			return multistep.ActionHalt
		}
		imageId = image.ImageID
	} else {
		imageId, err = servers.CreateImage(computeClient, server.ID, servers.CreateImageOpts{
			Name:     config.ImageName,
			Metadata: config.ImageMetadata,
		}).ExtractImageID()
		if err != nil {
			err := fmt.Errorf("Error creating image: %s", err)
			state.Put("error", err)
			ui.Error(err.Error())
			return multistep.ActionHalt
		}
	}

	// Set the Image ID in the state
	ui.Message(fmt.Sprintf("Image: %s", imageId))
	state.Put("image", imageId)

	// Wait for the image to become ready
	ui.Say(fmt.Sprintf("Waiting for image %s (image id: %s) to become ready...", config.ImageName, imageId))
	if err := WaitForImage(ctx, imageClient, imageId); err != nil {
		err := fmt.Errorf("Error waiting for image: %s", err)
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	return multistep.ActionContinue
}

func (s *stepCreateImage) Cleanup(multistep.StateBag) {
	// No cleanup...
}

// WaitForImage waits for the given Image ID to become ready.
func WaitForImage(ctx context.Context, client *gophercloud.ServiceClient, imageId string) error {
	maxNumErrors := 10
	numErrors := 0

	for {
		if err := ctx.Err(); err != nil {
			return err
		}
		image, err := images.Get(client, imageId).Extract()
		if err != nil {
			errCode, ok := err.(*gophercloud.ErrUnexpectedResponseCode)
			if ok && (errCode.Actual == 500 || errCode.Actual == 404) {
				numErrors++
				if numErrors >= maxNumErrors {
					log.Printf("[ERROR] Maximum number of errors (%d) reached; failing with: %s", numErrors, err)
					return err
				}
				log.Printf("[ERROR] %d error received, will ignore and retry: %s", errCode.Actual, err)
				time.Sleep(2 * time.Second)
				continue
			}

			return err
		}

		if image.Status == "active" {
			return nil
		}

		log.Printf("Waiting for image creation status: %s", image.Status)
		time.Sleep(2 * time.Second)
	}
}
<commit_msg>don't put error in state, or we'll fail.<commit_after>package openstack

import (
	"context"
	"fmt"
	"log"
	"time"

	"github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumeactions"

	"github.com/gophercloud/gophercloud"
	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
	"github.com/gophercloud/gophercloud/openstack/imageservice/v2/images"
	"github.com/hashicorp/packer/helper/multistep"
	"github.com/hashicorp/packer/packer"
)

type stepCreateImage struct {
	UseBlockStorageVolume bool
}

func (s *stepCreateImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
	config := state.Get("config").(*Config)
	server := state.Get("server").(*servers.Server)
	ui := state.Get("ui").(packer.Ui)

	// We need the v2 compute client
	computeClient, err := config.computeV2Client()
	if err != nil {
		err = fmt.Errorf("Error initializing compute client: %s", err)
		state.Put("error", err)
		return multistep.ActionHalt
	}

	// We need the v2 image client
	imageClient, err := config.imageV2Client()
	if err != nil {
		err = fmt.Errorf("Error initializing image service client: %s", err)
		state.Put("error", err)
		return multistep.ActionHalt
	}

	// Create the image.
	// Image source depends on the type of the Compute instance. It can be
	// Block Storage service volume or regular Compute service local volume.
	ui.Say(fmt.Sprintf("Creating the image: %s", config.ImageName))
	var imageId string
	var blockStorageClient *gophercloud.ServiceClient
	if s.UseBlockStorageVolume {
		// We need the v3 block storage client.
		blockStorageClient, err = config.blockStorageV3Client()
		if err != nil {
			err = fmt.Errorf("Error initializing block storage client: %s", err)
			state.Put("error", err)
			return multistep.ActionHalt
		}
		volume := state.Get("volume_id").(string)

		// set ImageMetadata before uploading to glance so the new image captured the desired values
		if len(config.ImageMetadata) > 0 {
			err = volumeactions.SetImageMetadata(blockStorageClient, volume, volumeactions.ImageMetadataOpts{
				Metadata: config.ImageMetadata,
			}).ExtractErr()
			if err != nil {
				err := fmt.Errorf("Error setting image metadata: %s", err)
				ui.Error(err.Error())
			}
		}

		image, err := volumeactions.UploadImage(blockStorageClient, volume, volumeactions.UploadImageOpts{
			DiskFormat: config.ImageDiskFormat,
			ImageName:  config.ImageName,
		}).Extract()
		if err != nil {
			err := fmt.Errorf("Error creating image: %s", err)
			state.Put("error", err)
			ui.Error(err.Error())
			return multistep.ActionHalt
		}
		imageId = image.ImageID
	} else {
		imageId, err = servers.CreateImage(computeClient, server.ID, servers.CreateImageOpts{
			Name:     config.ImageName,
			Metadata: config.ImageMetadata,
		}).ExtractImageID()
		if err != nil {
			err := fmt.Errorf("Error creating image: %s", err)
			state.Put("error", err)
			ui.Error(err.Error())
			return multistep.ActionHalt
		}
	}

	// Set the Image ID in the state
	ui.Message(fmt.Sprintf("Image: %s", imageId))
	state.Put("image", imageId)

	// Wait for the image to become ready
	ui.Say(fmt.Sprintf("Waiting for image %s (image id: %s) to become ready...", config.ImageName, imageId))
	if err := WaitForImage(ctx, imageClient, imageId); err != nil {
		err := fmt.Errorf("Error waiting for image: %s", err)
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	return multistep.ActionContinue
}

func (s *stepCreateImage) Cleanup(multistep.StateBag) {
	// No cleanup...
}

// WaitForImage waits for the given Image ID to become ready.
func WaitForImage(ctx context.Context, client *gophercloud.ServiceClient, imageId string) error {
	maxNumErrors := 10
	numErrors := 0

	for {
		if err := ctx.Err(); err != nil {
			return err
		}
		image, err := images.Get(client, imageId).Extract()
		if err != nil {
			errCode, ok := err.(*gophercloud.ErrUnexpectedResponseCode)
			if ok && (errCode.Actual == 500 || errCode.Actual == 404) {
				numErrors++
				if numErrors >= maxNumErrors {
					log.Printf("[ERROR] Maximum number of errors (%d) reached; failing with: %s", numErrors, err)
					return err
				}
				log.Printf("[ERROR] %d error received, will ignore and retry: %s", errCode.Actual, err)
				time.Sleep(2 * time.Second)
				continue
			}

			return err
		}

		if image.Status == "active" {
			return nil
		}

		log.Printf("Waiting for image creation status: %s", image.Status)
		time.Sleep(2 * time.Second)
	}
}
<|endoftext|>


<commit_before>package storage

import (
	zklib "github.com/samuel/go-zookeeper/zk"

	"github.com/zenoss/glog"
	"github.com/zenoss/serviced/coordinator/client"
	"github.com/zenoss/serviced/coordinator/client/zookeeper"
	"github.com/zenoss/serviced/domain/host"
	"github.com/zenoss/serviced/zzk"

	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"testing"
	"time"
)

func TestClient(t *testing.T) {
	zookeeper.EnsureZkFatjar()
	basePath := ""
	tc, err := zklib.StartTestCluster(1)
	if err != nil {
		t.Fatalf("could not start test zk cluster: %s", err)
	}
	defer os.RemoveAll(tc.Path)
	defer tc.Stop()
	time.Sleep(time.Second)

	servers := []string{fmt.Sprintf("127.0.0.1:%d", tc.Servers[0].Port)}

	dsnBytes, err := json.Marshal(zookeeper.DSN{Servers: servers, Timeout: time.Second * 15})
	if err != nil {
		t.Fatal("unexpected error creating zk DSN: %s", err)
	}
	dsn := string(dsnBytes)
	zClient, err := client.New("zookeeper", dsn, basePath, nil)

	zzk.InitializeGlobalCoordClient(zClient)

	conn, err := zzk.GetBasePathConnection("/")
	if err != nil {
		t.Fatal("unexpected error getting connection")
	}

	h := host.New()
	h.ID = "nodeID"
	h.IPAddr = "192.168.1.5"
	defer func(old func(string, os.FileMode) error) {
		mkdirAll = old
	}(mkdirAll)
	dir, err := ioutil.TempDir("", "serviced_var_")
	if err != nil {
		t.Fatalf("could not create tempdir: %s", err)
	}
	defer os.RemoveAll(dir)
	c, err := NewClient(h, dir)
	if err != nil {
		t.Fatalf("unexpected error creating client: %s", err)
	}
	defer c.Close()
	time.Sleep(time.Second * 10)

	nodePath := fmt.Sprintf("/storage/clients/%s", h.IPAddr)
	glog.Infof("about to check for %s", nodePath)
	if exists, err := conn.Exists(nodePath); err != nil {
		t.Fatalf("did not expect error checking for existence of %s: %s", nodePath, err)
	} else {
		if !exists {
			t.Fatalf("could not find %s", nodePath)
		}
	}
}
<commit_msg>added PoolID to host<commit_after>package storage

import (
	zklib "github.com/samuel/go-zookeeper/zk"

	"github.com/zenoss/glog"
	"github.com/zenoss/serviced/coordinator/client"
	"github.com/zenoss/serviced/coordinator/client/zookeeper"
	"github.com/zenoss/serviced/domain/host"
	"github.com/zenoss/serviced/zzk"

	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"testing"
	"time"
)

func TestClient(t *testing.T) {
	zookeeper.EnsureZkFatjar()
	basePath := ""
	tc, err := zklib.StartTestCluster(1)
	if err != nil {
		t.Fatalf("could not start test zk cluster: %s", err)
	}
	defer os.RemoveAll(tc.Path)
	defer tc.Stop()
	time.Sleep(time.Second)

	servers := []string{fmt.Sprintf("127.0.0.1:%d", tc.Servers[0].Port)}

	dsnBytes, err := json.Marshal(zookeeper.DSN{Servers: servers, Timeout: time.Second * 15})
	if err != nil {
		t.Fatal("unexpected error creating zk DSN: %s", err)
	}
	dsn := string(dsnBytes)
	zClient, err := client.New("zookeeper", dsn, basePath, nil)

	zzk.InitializeGlobalCoordClient(zClient)

	conn, err := zzk.GetBasePathConnection("/")
	if err != nil {
		t.Fatal("unexpected error getting connection")
	}

	h := host.New()
	h.ID = "nodeID"
	h.IPAddr = "192.168.1.5"
	h.PoolID = "default"
	defer func(old func(string, os.FileMode) error) {
		mkdirAll = old
	}(mkdirAll)
	dir, err := ioutil.TempDir("", "serviced_var_")
	if err != nil {
		t.Fatalf("could not create tempdir: %s", err)
	}
	defer os.RemoveAll(dir)
	c, err := NewClient(h, dir)
	if err != nil {
		t.Fatalf("unexpected error creating client: %s", err)
	}
	defer c.Close()
	time.Sleep(time.Second * 10)

	nodePath := fmt.Sprintf("/storage/clients/%s", h.IPAddr)
	glog.Infof("about to check for %s", nodePath)
	if exists, err := conn.Exists(nodePath); err != nil {
		t.Fatalf("did not expect error checking for existence of %s: %s", nodePath, err)
	} else {
		if !exists {
			t.Fatalf("could not find %s", nodePath)
		}
	}
}
<|endoftext|>


<commit_before>package types

import (
	"math/big"

	"github.com/ethereum/go-ethereum/crypto"
	"github.com/ethereum/go-ethereum/ethutil"
	"github.com/ethereum/go-ethereum/state"
)

func CreateBloom(receipts Receipts) []byte {
	bin := new(big.Int)
	for _, receipt := range receipts {
		bin.Or(bin, LogsBloom(receipt.logs))
	}

	return ethutil.LeftPadBytes(bin.Bytes(), 64)
}

func LogsBloom(logs state.Logs) *big.Int {
	bin := new(big.Int)
	for _, log := range logs {
		data := make([][]byte, len(log.Topics())+1)
		data[0] = log.Address()

		for i, topic := range log.Topics() {
			data[i+1] = topic
		}

		for _, b := range data {
			bin.Or(bin, ethutil.BigD(bloom9(crypto.Sha3(b)).Bytes()))
		}
	}

	return bin
}

func bloom9(b []byte) *big.Int {
	r := new(big.Int)
	for _, i := range []int{0, 2, 4} {
		t := big.NewInt(1)
		b := uint(b[i+1]) + 256*(uint(b[i])&1)
		r.Or(r, t.Lsh(t, b))
	}

	return r
}

func BloomLookup(bin, topic []byte) bool {
	bloom := ethutil.BigD(bin)
	cmp := bloom9(crypto.Sha3(topic))

	return bloom.And(bloom, cmp).Cmp(cmp) == 0
}
<commit_msg>Bloom expanded by 4<commit_after>package types

import (
	"math/big"

	"github.com/ethereum/go-ethereum/crypto"
	"github.com/ethereum/go-ethereum/ethutil"
	"github.com/ethereum/go-ethereum/state"
)

func CreateBloom(receipts Receipts) []byte {
	bin := new(big.Int)
	for _, receipt := range receipts {
		bin.Or(bin, LogsBloom(receipt.logs))
	}

	return ethutil.LeftPadBytes(bin.Bytes(), 256)
}

func LogsBloom(logs state.Logs) *big.Int {
	bin := new(big.Int)
	for _, log := range logs {
		data := make([][]byte, len(log.Topics())+1)
		data[0] = log.Address()

		for i, topic := range log.Topics() {
			data[i+1] = topic
		}

		for _, b := range data {
			bin.Or(bin, ethutil.BigD(bloom9(crypto.Sha3(b)).Bytes()))
		}
	}

	return bin
}

func bloom9(b []byte) *big.Int {
	r := new(big.Int)

	for i := 0; i < 16; i += 2 {
		t := big.NewInt(1)
		b := uint(b[i+1]) + 1024*(uint(b[i])&1)
		r.Or(r, t.Lsh(t, b))
	}

	return r
}

func BloomLookup(bin, topic []byte) bool {
	bloom := ethutil.BigD(bin)
	cmp := bloom9(crypto.Sha3(topic))

	return bloom.And(bloom, cmp).Cmp(cmp) == 0
}
<|endoftext|>


<commit_before>package routing

import (
	"math"

	"github.com/lightningnetwork/lnd/channeldb"
	"github.com/roasbeef/btcd/btcec"
	"github.com/roasbeef/btcutil"
)

const (
	// HopLimit is the maximum number hops that is permissible as a route.
	// Any potential paths found that lie above this limit will be rejected
	// with an error. This value is computed using the current fixed-size
	// packet length of the Sphinx construction.
	HopLimit = 20

	// infinity is used as a starting distance in our shortest path search.
	infinity = math.MaxFloat64
)

// Route represents a path through the channel graph which runs over one or
// more channels in succession. This struct carries all the information
// required to craft the Sphinx onion packet, and send the payment along the
// first hop in the path. A route is only selected as valid if all the channels
// have sufficient capacity to carry the initial payment amount after fees are
// accounted for.
type Route struct {
	// TotalTimeLock is the cumulative (final) time lock across the entire
	// route. This is the CLTV value that should be extended to the first
	// hop in the route. All other hops will decrement the time-lock as
	// advertised, leaving enough time for all hops to wait for or present
	// the payment preimage to complete the payment.
	TotalTimeLock uint32

	// TotalFees is the sum of the fees paid at each hop within the final
	// route. In the case of a one-hop payment, this value will be zero as
	// we don't need to pay a fee it ourself.
	TotalFees btcutil.Amount

	// TotalAmount is the total amount of funds required to complete a
	// payment over this route. This value includes the cumulative fees at
	// each hop. As a result, the HTLC extended to the first-hop in the
	// route will need to have at least this many satoshis, otherwise the
	// route will fail at an intermediate node due to an insufficient
	// amount of fees.
	TotalAmount btcutil.Amount

	// Hops contains details concerning the specific forwarding details at
	// each hop.
	Hops []*Hop
}

// Hop represents the forwarding details at a particular position within the
// final route. This struct houses the values necessary to create the HTLC
// which will travel along this hop, and also encode the per-hop payload
// included within the Sphinx packet.
type Hop struct {
	// Channels is the active payment channel that this hop will travel
	// along.
	Channel *channeldb.ChannelEdge

	// TimeLockDelta is the delta that this hop will subtract from the HTLC
	// before extending it to the next hop in the route.
	TimeLockDelta uint16

	// AmtToForward is the amount that this hop will forward to the next
	// hop. This value is less than the value that the incoming HTLC
	// carries as a fee will be subtracted by the hop.
	AmtToForward btcutil.Amount

	// Fee is the total fee that this hop will subtract from the incoming
	// payment, this difference nets the hop fees for forwarding the
	// payment.
	Fee btcutil.Amount
}

// computeFee computes the fee to forward an HTLC of `amt` satoshis over the
// passed active payment channel. This value is currently computed as specified
// in BOLT07, but will likely change in the near future.
func computeFee(amt btcutil.Amount, edge *channeldb.ChannelEdge) btcutil.Amount {
	return edge.FeeBaseMSat + (amt*edge.FeeProportionalMillionths)/1000000
}

// newRoute returns a fully valid route between the source and target that's
// capable of supporting a payment of `amtToSend` after fees are fully
// computed. IF the route is too long, or the selected path cannot support the
// fully payment including fees, then a non-nil error is returned. prevHop maps
// a vertex to the channel required to get to it.
func newRoute(amtToSend btcutil.Amount, source, target vertex,
	prevHop map[vertex]edgeWithPrev) (*Route, error) {

	// As an initial sanity check, the potential route is immediately
	// invalidate if it spans more than 20 hops. The current Sphinx (onion
	// routing) implementation can only encode up to 20 hops as the entire
	// packet is fixed size. If this route is more than 20 hops, then it's
	// invalid.
	if len(prevHop) > HopLimit {
		return nil, ErrMaxHopsExceeded
	}

	// If the potential route if below the max hop limit, then we'll use
	// the prevHop map to unravel the path. We end up with a list of edges
	// in the reverse direction which we'll use to properly calculate the
	// timelock and fee values.
	pathEdges := make([]*channeldb.ChannelEdge, 0, len(prevHop))
	prev := target
	for prev != source { // TODO(roasbeef): assumes no cycles
		// Add the current hop to the limit of path edges then walk
		// backwards from this hop via the prev pointer for this hop
		// within the prevHop map.
		pathEdges = append(pathEdges, prevHop[prev].edge)
		prev = newVertex(prevHop[prev].prevNode)
	}

	route := &Route{
		Hops: make([]*Hop, len(pathEdges)),
	}

	// The running amount is the total amount of satoshis required at this
	// point in the route. We start this value at the amount we want to
	// send to the destination. This value will then get successively
	// larger as we compute the fees going backwards.
	runningAmt := amtToSend
	pathLength := len(pathEdges)
	for i, edge := range pathEdges {
		// Now we create the hop struct for this point in the route.
		// The amount to forward is the running amount, and we compute
		// the required fee based on this amount.
		nextHop := &Hop{
			Channel:       edge,
			AmtToForward:  runningAmt,
			Fee:           computeFee(runningAmt, edge),
			TimeLockDelta: edge.Expiry,
		}
		edge.Node.PubKey.Curve = nil

		// As a sanity check, we ensure that the selected channel has
		// enough capacity to forward the required amount which
		// includes the fee dictated at each hop.
		if nextHop.AmtToForward > nextHop.Channel.Capacity {
			return nil, ErrInsufficientCapacity
		}

		// We don't pay any fees to ourselves on the first-hop channel,
		// so we don't tally up the running fee and amount.
		if i != len(pathEdges)-1 {
			// For a node to forward an HTLC, then following
			// inequality most hold true: amt_in - fee >=
			// amt_to_forward. Therefore we add the fee this node
			// consumes in order to calculate the amount that it
			// show be forwarded by the prior node which is the
			// next hop in our loop.
			runningAmt += nextHop.Fee

			// Next we tally the total fees (thus far) in the
			// route, and also accumulate the total timelock in the
			// route by adding the node's time lock delta which is
			// the amount of blocks it'll subtract from the
			// incoming time lock.
			route.TotalFees += nextHop.Fee
		} else {
			nextHop.Fee = 0
		}

		route.TotalTimeLock += uint32(nextHop.TimeLockDelta)

		// Finally, as we're currently talking the route backwards, we
		// reverse the index in order to place this hop at the proper
		// spot in the forward direction of the route.
		route.Hops[pathLength-1-i] = nextHop
	}

	// The total amount required for this route will be the value the
	// source extends to the first hop in the route.
	route.TotalAmount = runningAmt

	return route, nil
}

// vertex is a simple alias for the serialization of a compressed Bitcoin
// public key.
type vertex [33]byte

// newVertex returns a new vertex given a public key.
func newVertex(pub *btcec.PublicKey) vertex {
	var v vertex
	copy(v[:], pub.SerializeCompressed())
	return v
}

// nodeWithDist is a helper struct that couples the distance from the current
// source to a node with a pointer to the node itself.
type nodeWithDist struct {
	dist float64
	node *channeldb.LightningNode
}

// edgeWithPrev is a helper struct used in path finding that couples an
// directional edge with the node's ID in the opposite direction.
type edgeWithPrev struct {
	edge     *channeldb.ChannelEdge
	prevNode *btcec.PublicKey
}

// edgeWeight computes the weight of an edge. This value is used when searching
// for the shortest path within the channel graph between two nodes. Currently
// this is just 1 + the cltv delta value required at this hop, this value
// should be tuned with experimental and empirical data.
//
// TODO(roasbeef): compute robust weight metric
func edgeWeight(e *channeldb.ChannelEdge) float64 {
	return float64(1 + e.Expiry)
}

// findRoute attempts to find a path from the source node within the
// ChannelGraph to the target node that's capable of supporting a payment of
// `amt` value. The current approach is used a multiple pass path finding
// algorithm. First we employ a modified version of Dijkstra's algorithm to
// find a potential set of shortest paths, the distance metric is related to
// the time-lock+fee along the route. Once we have a set of candidate routes,
// we calculate the required fee and time lock values running backwards along
// the route. The route that's selected is the one with the lowest total fee.
//
// TODO(roasbeef): make member, add caching
//  * add k-path
func findRoute(graph *channeldb.ChannelGraph, target *btcec.PublicKey,
	amt btcutil.Amount) (*Route, error) {

	// First initialize empty list of all the node that we've yet to
	// visited.
	// TODO(roasbeef): make into incremental fibonacci heap rather than
	// loading all into memory.
	var unvisited []*channeldb.LightningNode

	// For each node/vertex the graph we create an entry in the distance
	// map for the node set with a distance of "infinity".  We also mark
	// add the node to our set of unvisited nodes.
	distance := make(map[vertex]nodeWithDist)
	if err := graph.ForEachNode(func(node *channeldb.LightningNode) error {
		// TODO(roasbeef): with larger graph can just use disk seeks
		// with a visited map
		distance[newVertex(node.PubKey)] = nodeWithDist{
			dist: infinity,
			node: node,
		}

		unvisited = append(unvisited, node)
		return nil
	}); err != nil {
		return nil, err
	}

	// Next we obtain the source node from the graph, and initialize it
	// with a distance of 0. This indicates our starting point in the graph
	// traversal.
	sourceNode, err := graph.SourceNode()
	if err != nil {
		return nil, err
	}
	sourceVertex := newVertex(sourceNode.PubKey)
	distance[sourceVertex] = nodeWithDist{
		dist: 0,
		node: sourceNode,
	}

	// We'll use this map as a series of "previous" hop pointers. So to get
	// to `vertex` we'll take the edge that it's mapped to within `prev`.
	prev := make(map[vertex]edgeWithPrev)

	for len(unvisited) != 0 {
		var bestNode *channeldb.LightningNode
		smallestDist := infinity

		// First we examine our list of unvisited nodes, for the most
		// optimal vertex to examine next.
		for i, node := range unvisited {
			// The "best" node to visit next is node with the
			// smallest distance from the source of all the
			// unvisited nodes.
			v := newVertex(node.PubKey)
			if nodeInfo := distance[v]; nodeInfo.dist < smallestDist {
				smallestDist = nodeInfo.dist
				bestNode = nodeInfo.node

				// Since we're going to visit this node, we can
				// remove it from the set of unvisited nodes.
				copy(unvisited[i:], unvisited[i+1:])
				unvisited[len(unvisited)-1] = nil // Avoid GC leak.
				unvisited = unvisited[:len(unvisited)-1]

				break
			}
		}

		// If we've reached our target, then we're done here and can
		// exit the graph traversal early.
		if bestNode == nil || bestNode.PubKey.IsEqual(target) {
			break
		}

		// Now that we've found the next potential step to take we'll
		// examine all the outgoing edge (channels) from this node to
		// further our graph traversal.
		pivot := newVertex(bestNode.PubKey)
		err := bestNode.ForEachChannel(nil, func(edge *channeldb.ChannelEdge) error {
			// Compute the tentative distance to this new
			// channel/edge which is the distance to our current
			// pivot node plus the weight of this edge.
			tempDist := distance[pivot].dist + edgeWeight(edge)

			// If this new tentative distance is better than the
			// current best known distance to this node, then we
			// record the new better distance, and also populate
			// our "next hop" map with this edge.
			// TODO(roasbeef): add capacity to relaxation criteria?
			//  * also add min payment?
			v := newVertex(edge.Node.PubKey)
			if tempDist < distance[v].dist {
				// TODO(roasbeef): unconditionally add for all
				// paths
				distance[v] = nodeWithDist{
					dist: tempDist,
					node: edge.Node,
				}
				prev[v] = edgeWithPrev{
					edge:     edge,
					prevNode: bestNode.PubKey,
				}
			}
			return nil
		})
		if err != nil {
			return nil, err
		}
	}

	// If the target node isn't found in the prev hop map, then a path
	// doesn't exist, so we terminate in an error.
	if _, ok := prev[newVertex(target)]; !ok {
		return nil, ErrNoPathFound
	}

	// Otherwise, we construct a new route which calculate the relevant
	// total fees and proper time lock values for each hop.
	targetVerex := newVertex(target)
	return newRoute(amt, sourceVertex, targetVerex, prev)
}
<commit_msg>routing: check route length after edges for route is collected<commit_after>package routing

import (
	"math"

	"github.com/lightningnetwork/lnd/channeldb"
	"github.com/roasbeef/btcd/btcec"
	"github.com/roasbeef/btcutil"
)

const (
	// HopLimit is the maximum number hops that is permissible as a route.
	// Any potential paths found that lie above this limit will be rejected
	// with an error. This value is computed using the current fixed-size
	// packet length of the Sphinx construction.
	HopLimit = 20

	// infinity is used as a starting distance in our shortest path search.
	infinity = math.MaxFloat64
)

// Route represents a path through the channel graph which runs over one or
// more channels in succession. This struct carries all the information
// required to craft the Sphinx onion packet, and send the payment along the
// first hop in the path. A route is only selected as valid if all the channels
// have sufficient capacity to carry the initial payment amount after fees are
// accounted for.
type Route struct {
	// TotalTimeLock is the cumulative (final) time lock across the entire
	// route. This is the CLTV value that should be extended to the first
	// hop in the route. All other hops will decrement the time-lock as
	// advertised, leaving enough time for all hops to wait for or present
	// the payment preimage to complete the payment.
	TotalTimeLock uint32

	// TotalFees is the sum of the fees paid at each hop within the final
	// route. In the case of a one-hop payment, this value will be zero as
	// we don't need to pay a fee it ourself.
	TotalFees btcutil.Amount

	// TotalAmount is the total amount of funds required to complete a
	// payment over this route. This value includes the cumulative fees at
	// each hop. As a result, the HTLC extended to the first-hop in the
	// route will need to have at least this many satoshis, otherwise the
	// route will fail at an intermediate node due to an insufficient
	// amount of fees.
	TotalAmount btcutil.Amount

	// Hops contains details concerning the specific forwarding details at
	// each hop.
	Hops []*Hop
}

// Hop represents the forwarding details at a particular position within the
// final route. This struct houses the values necessary to create the HTLC
// which will travel along this hop, and also encode the per-hop payload
// included within the Sphinx packet.
type Hop struct {
	// Channels is the active payment channel that this hop will travel
	// along.
	Channel *channeldb.ChannelEdge

	// TimeLockDelta is the delta that this hop will subtract from the HTLC
	// before extending it to the next hop in the route.
	TimeLockDelta uint16

	// AmtToForward is the amount that this hop will forward to the next
	// hop. This value is less than the value that the incoming HTLC
	// carries as a fee will be subtracted by the hop.
	AmtToForward btcutil.Amount

	// Fee is the total fee that this hop will subtract from the incoming
	// payment, this difference nets the hop fees for forwarding the
	// payment.
	Fee btcutil.Amount
}

// computeFee computes the fee to forward an HTLC of `amt` satoshis over the
// passed active payment channel. This value is currently computed as specified
// in BOLT07, but will likely change in the near future.
func computeFee(amt btcutil.Amount, edge *channeldb.ChannelEdge) btcutil.Amount {
	return edge.FeeBaseMSat + (amt*edge.FeeProportionalMillionths)/1000000
}

// newRoute returns a fully valid route between the source and target that's
// capable of supporting a payment of `amtToSend` after fees are fully
// computed. IF the route is too long, or the selected path cannot support the
// fully payment including fees, then a non-nil error is returned. prevHop maps
// a vertex to the channel required to get to it.
func newRoute(amtToSend btcutil.Amount, source, target vertex,
	prevHop map[vertex]edgeWithPrev) (*Route, error) {

	// If the potential route if below the max hop limit, then we'll use
	// the prevHop map to unravel the path. We end up with a list of edges
	// in the reverse direction which we'll use to properly calculate the
	// timelock and fee values.
	pathEdges := make([]*channeldb.ChannelEdge, 0, len(prevHop))
	prev := target
	for prev != source { // TODO(roasbeef): assumes no cycles
		// Add the current hop to the limit of path edges then walk
		// backwards from this hop via the prev pointer for this hop
		// within the prevHop map.
		pathEdges = append(pathEdges, prevHop[prev].edge)
		prev = newVertex(prevHop[prev].prevNode)
	}

	// The route is invalid if it spans more than 20 hops. The current
	// Sphinx (onion routing) implementation can only encode up to 20 hops
	// as the entire packet is fixed size. If this route is more than 20 hops,
	// then it's invalid.
	if len(pathEdges) > HopLimit {
		return nil, ErrMaxHopsExceeded
	}

	route := &Route{
		Hops: make([]*Hop, len(pathEdges)),
	}

	// The running amount is the total amount of satoshis required at this
	// point in the route. We start this value at the amount we want to
	// send to the destination. This value will then get successively
	// larger as we compute the fees going backwards.
	runningAmt := amtToSend
	pathLength := len(pathEdges)
	for i, edge := range pathEdges {
		// Now we create the hop struct for this point in the route.
		// The amount to forward is the running amount, and we compute
		// the required fee based on this amount.
		nextHop := &Hop{
			Channel:       edge,
			AmtToForward:  runningAmt,
			Fee:           computeFee(runningAmt, edge),
			TimeLockDelta: edge.Expiry,
		}
		edge.Node.PubKey.Curve = nil

		// As a sanity check, we ensure that the selected channel has
		// enough capacity to forward the required amount which
		// includes the fee dictated at each hop.
		if nextHop.AmtToForward > nextHop.Channel.Capacity {
			return nil, ErrInsufficientCapacity
		}

		// We don't pay any fees to ourselves on the first-hop channel,
		// so we don't tally up the running fee and amount.
		if i != len(pathEdges)-1 {
			// For a node to forward an HTLC, then following
			// inequality most hold true: amt_in - fee >=
			// amt_to_forward. Therefore we add the fee this node
			// consumes in order to calculate the amount that it
			// show be forwarded by the prior node which is the
			// next hop in our loop.
			runningAmt += nextHop.Fee

			// Next we tally the total fees (thus far) in the
			// route, and also accumulate the total timelock in the
			// route by adding the node's time lock delta which is
			// the amount of blocks it'll subtract from the
			// incoming time lock.
			route.TotalFees += nextHop.Fee
		} else {
			nextHop.Fee = 0
		}

		route.TotalTimeLock += uint32(nextHop.TimeLockDelta)

		// Finally, as we're currently talking the route backwards, we
		// reverse the index in order to place this hop at the proper
		// spot in the forward direction of the route.
		route.Hops[pathLength-1-i] = nextHop
	}

	// The total amount required for this route will be the value the
	// source extends to the first hop in the route.
	route.TotalAmount = runningAmt

	return route, nil
}

// vertex is a simple alias for the serialization of a compressed Bitcoin
// public key.
type vertex [33]byte

// newVertex returns a new vertex given a public key.
func newVertex(pub *btcec.PublicKey) vertex {
	var v vertex
	copy(v[:], pub.SerializeCompressed())
	return v
}

// nodeWithDist is a helper struct that couples the distance from the current
// source to a node with a pointer to the node itself.
type nodeWithDist struct {
	dist float64
	node *channeldb.LightningNode
}

// edgeWithPrev is a helper struct used in path finding that couples an
// directional edge with the node's ID in the opposite direction.
type edgeWithPrev struct {
	edge     *channeldb.ChannelEdge
	prevNode *btcec.PublicKey
}

// edgeWeight computes the weight of an edge. This value is used when searching
// for the shortest path within the channel graph between two nodes. Currently
// this is just 1 + the cltv delta value required at this hop, this value
// should be tuned with experimental and empirical data.
//
// TODO(roasbeef): compute robust weight metric
func edgeWeight(e *channeldb.ChannelEdge) float64 {
	return float64(1 + e.Expiry)
}

// findRoute attempts to find a path from the source node within the
// ChannelGraph to the target node that's capable of supporting a payment of
// `amt` value. The current approach is used a multiple pass path finding
// algorithm. First we employ a modified version of Dijkstra's algorithm to
// find a potential set of shortest paths, the distance metric is related to
// the time-lock+fee along the route. Once we have a set of candidate routes,
// we calculate the required fee and time lock values running backwards along
// the route. The route that's selected is the one with the lowest total fee.
//
// TODO(roasbeef): make member, add caching
//  * add k-path
func findRoute(graph *channeldb.ChannelGraph, target *btcec.PublicKey,
	amt btcutil.Amount) (*Route, error) {

	// First initialize empty list of all the node that we've yet to
	// visited.
	// TODO(roasbeef): make into incremental fibonacci heap rather than
	// loading all into memory.
	var unvisited []*channeldb.LightningNode

	// For each node/vertex the graph we create an entry in the distance
	// map for the node set with a distance of "infinity".  We also mark
	// add the node to our set of unvisited nodes.
	distance := make(map[vertex]nodeWithDist)
	if err := graph.ForEachNode(func(node *channeldb.LightningNode) error {
		// TODO(roasbeef): with larger graph can just use disk seeks
		// with a visited map
		distance[newVertex(node.PubKey)] = nodeWithDist{
			dist: infinity,
			node: node,
		}

		unvisited = append(unvisited, node)
		return nil
	}); err != nil {
		return nil, err
	}

	// Next we obtain the source node from the graph, and initialize it
	// with a distance of 0. This indicates our starting point in the graph
	// traversal.
	sourceNode, err := graph.SourceNode()
	if err != nil {
		return nil, err
	}
	sourceVertex := newVertex(sourceNode.PubKey)
	distance[sourceVertex] = nodeWithDist{
		dist: 0,
		node: sourceNode,
	}

	// We'll use this map as a series of "previous" hop pointers. So to get
	// to `vertex` we'll take the edge that it's mapped to within `prev`.
	prev := make(map[vertex]edgeWithPrev)

	for len(unvisited) != 0 {
		var bestNode *channeldb.LightningNode
		smallestDist := infinity

		// First we examine our list of unvisited nodes, for the most
		// optimal vertex to examine next.
		for i, node := range unvisited {
			// The "best" node to visit next is node with the
			// smallest distance from the source of all the
			// unvisited nodes.
			v := newVertex(node.PubKey)
			if nodeInfo := distance[v]; nodeInfo.dist < smallestDist {
				smallestDist = nodeInfo.dist
				bestNode = nodeInfo.node

				// Since we're going to visit this node, we can
				// remove it from the set of unvisited nodes.
				copy(unvisited[i:], unvisited[i+1:])
				unvisited[len(unvisited)-1] = nil // Avoid GC leak.
				unvisited = unvisited[:len(unvisited)-1]

				break
			}
		}

		// If we've reached our target, then we're done here and can
		// exit the graph traversal early.
		if bestNode == nil || bestNode.PubKey.IsEqual(target) {
			break
		}

		// Now that we've found the next potential step to take we'll
		// examine all the outgoing edge (channels) from this node to
		// further our graph traversal.
		pivot := newVertex(bestNode.PubKey)
		err := bestNode.ForEachChannel(nil, func(edge *channeldb.ChannelEdge) error {
			// Compute the tentative distance to this new
			// channel/edge which is the distance to our current
			// pivot node plus the weight of this edge.
			tempDist := distance[pivot].dist + edgeWeight(edge)

			// If this new tentative distance is better than the
			// current best known distance to this node, then we
			// record the new better distance, and also populate
			// our "next hop" map with this edge.
			// TODO(roasbeef): add capacity to relaxation criteria?
			//  * also add min payment?
			v := newVertex(edge.Node.PubKey)
			if tempDist < distance[v].dist {
				// TODO(roasbeef): unconditionally add for all
				// paths
				distance[v] = nodeWithDist{
					dist: tempDist,
					node: edge.Node,
				}
				prev[v] = edgeWithPrev{
					edge:     edge,
					prevNode: bestNode.PubKey,
				}
			}
			return nil
		})
		if err != nil {
			return nil, err
		}
	}

	// If the target node isn't found in the prev hop map, then a path
	// doesn't exist, so we terminate in an error.
	if _, ok := prev[newVertex(target)]; !ok {
		return nil, ErrNoPathFound
	}

	// Otherwise, we construct a new route which calculate the relevant
	// total fees and proper time lock values for each hop.
	targetVerex := newVertex(target)
	return newRoute(amt, sourceVertex, targetVerex, prev)
}
<|endoftext|>


<commit_before>// Copyright (c) 2013 Mathieu Turcotte
// Licensed under the MIT license.

// Package browserchannel provides a server-side browser channel
// implementation. See http://goo.gl/F287G for the client-side API.
package browserchannel

import (
	crand "crypto/rand"
	"encoding/json"
	"io"
	"log"
	"math/rand"
	"net/http"
	"net/url"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"time"
)

// The browser channel protocol version implemented by this library.
const SupportedProcolVersion = 8

const (
	DefaultBindPath = "bind"
	DefaultTestPath = "test"
)

type queryType int

// Possible values for the query TYPE parameter.
const (
	queryUnset = iota
	queryTerminate
	queryXmlHttp
	queryHtml
	queryTest
)

func parseQueryType(s string) (qtype queryType) {
	switch s {
	case "html":
		qtype = queryHtml
	case "xmlhttp":
		qtype = queryXmlHttp
	case "terminate":
		qtype = queryTerminate
	case "test":
		qtype = queryTest
	}
	return
}

func (qtype queryType) setContentType(rw http.ResponseWriter) {
	if qtype == queryHtml {
		rw.Header().Set("Content-Type", "text/html")
	} else {
		rw.Header().Set("Content-Type", "text/plain")
	}
}

func parseAid(s string) (aid int, err error) {
	aid = -1
	if len(s) == 0 {
		return
	}
	aid, err = strconv.Atoi(s)
	return
}

func parseProtoVersion(s string) (version int) {
	version, err := strconv.Atoi(s)
	if err != nil {
		version = -1
	}
	return
}

type bindParams struct {
	cver    string
	sid     SessionId
	qtype   queryType
	domain  string
	rid     string
	aid     int
	chunked bool
	values  url.Values
	method  string
}

func parseBindParams(req *http.Request, values url.Values) (params *bindParams, err error) {
	cver := req.Form.Get("VER")
	qtype := parseQueryType(req.Form.Get("TYPE"))
	domain := req.Form.Get("DOMAIN")
	rid := req.Form.Get("zx")
	ci := req.Form.Get("CI") == "1"
	sid, err := parseSessionId(req.Form.Get("SID"))
	if err != nil {
		return
	}
	aid, err := parseAid(req.Form.Get("AID"))
	if err != nil {
		return
	}
	params = &bindParams{cver, sid, qtype, domain, rid, aid, ci, values, req.Method}
	return
}

type testParams struct {
	ver    int
	init   bool
	qtype  queryType
	domain string
}

func parseTestParams(req *http.Request) *testParams {
	version := parseProtoVersion(req.Form.Get("VER"))
	qtype := parseQueryType(req.Form.Get("TYPE"))
	domain := req.Form.Get("DOMAIN")
	init := req.Form.Get("MODE") == "init"
	return &testParams{version, init, qtype, domain}
}

var headers = map[string]string{
	"Cache-Control":          "no-cache, no-store, max-age=0, must-revalidate",
	"Expires":                "Fri, 01 Jan 1990 00:00:00 GMT",
	"X-Content-Type-Options": "nosniff",
	"Transfer-Encoding":      "chunked",
	"Pragma":                 "no-cache",
}

type channelMap struct {
	sync.RWMutex
	m map[SessionId]*Channel
}

func (m *channelMap) get(sid SessionId) *Channel {
	m.RLock()
	defer m.RUnlock()
	return m.m[sid]
}

func (m *channelMap) set(sid SessionId, channel *Channel) {
	m.Lock()
	defer m.Unlock()
	m.m[sid] = channel
}

func (m *channelMap) del(sid SessionId) (c *Channel, deleted bool) {
	m.Lock()
	defer m.Unlock()
	c, deleted = m.m[sid]
	delete(m.m, sid)
	return
}

// Contains the browser channel cross domain info for a single domain.
type crossDomainInfo struct {
	hostMatcher *regexp.Regexp
	domain      string
	prefixes    []string
}

func getHostPrefix(info *crossDomainInfo) string {
	if info != nil {
		return info.prefixes[rand.Intn(len(info.prefixes))]
	}
	return ""
}

type ChannelHandler func(*Channel)

type Handler struct {
	corsInfo    *crossDomainInfo
	prefix      string
	channels    *channelMap
	bindPath    string
	testPath    string
	gcChan      chan SessionId
	chanHandler ChannelHandler
}

// Creates a new browser channel handler.
func NewHandler(chanHandler ChannelHandler) (h *Handler) {
	h = new(Handler)
	h.channels = &channelMap{m: make(map[SessionId]*Channel)}
	h.bindPath = DefaultBindPath
	h.testPath = DefaultTestPath
	h.gcChan = make(chan SessionId, 10)
	h.chanHandler = chanHandler
	go h.removeClosedSession()
	return
}

// Sets the cross domain information for this browser channel. The origin is
// used as the Access-Control-Allow-Origin header value and should respect the
// format specified in http://www.w3.org/TR/cors/. The prefix is used to set
// the hostPrefix parameter on the client side.
func (h *Handler) SetCrossDomainPrefix(domain string, prefixes []string) {
	h.corsInfo = &crossDomainInfo{makeOriginMatcher(domain), domain, prefixes}
}

// Removes closed channels from the handler's channel map.
func (h *Handler) removeClosedSession() {
	for {
		sid, ok := <-h.gcChan
		if !ok {
			break
		}

		log.Printf("removing %s from session map\n", sid)

		if channel, ok := h.channels.del(sid); ok {
			channel.dispose()
		} else {
			log.Printf("missing channel for %s in session map\n", sid)
		}
	}
}

func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	// The CORS  spec only supports *, null or the exact domain.
	// http://www.w3.org/TR/cors/#access-control-allow-origin-response-header
	// http://tools.ietf.org/html/rfc6454#section-7.1
	origin := req.Header.Get("origin")
	if len(origin) > 0 && h.corsInfo != nil &&
		h.corsInfo.hostMatcher.MatchString(origin) {
		rw.Header().Set("Access-Control-Allow-Origin", origin)
		rw.Header().Set("Access-Control-Allow-Credentials", "true")
	}

	// The body is parsed before calling ParseForm so the values don't get
	// collapsed into a single collection.
	values, err := parseBody(req.Body)
	if err != nil {
		rw.WriteHeader(400)
		return
	}

	req.ParseForm()

	path := req.URL.Path
	if strings.HasSuffix(path, h.testPath) {
		log.Printf("test:%s\n", req.URL)
		h.handleTestRequest(rw, parseTestParams(req))
	} else if strings.HasSuffix(path, h.bindPath) {
		log.Printf("bind:%s:%s\n", req.Method, req.URL)
		params, err := parseBindParams(req, values)
		if err != nil {
			rw.WriteHeader(400)
			return
		}
		h.handleBindRequest(rw, params)
	} else {
		rw.WriteHeader(404)
	}
}

func (h *Handler) handleTestRequest(rw http.ResponseWriter, params *testParams) {
	if params.ver != SupportedProcolVersion {
		rw.WriteHeader(400)
		io.WriteString(rw, "Unsupported protocol version.")
	} else if params.init {
		setHeaders(rw, &headers)
		rw.WriteHeader(200)
		io.WriteString(rw, "[\""+getHostPrefix(h.corsInfo)+"\",\"\"]")
	} else {
		params.qtype.setContentType(rw)
		setHeaders(rw, &headers)
		rw.WriteHeader(200)

		if params.qtype == queryHtml {
			writeHtmlHead(rw)
			writeHtmlDomain(rw, params.domain)
			writeHtmlRpc(rw, "11111")
			writeHtmlPadding(rw)
		} else {
			io.WriteString(rw, "11111")
		}

		time.Sleep(2 * time.Second)

		if params.qtype == queryHtml {
			writeHtmlRpc(rw, "2")
			writeHtmlDone(rw)
		} else {
			io.WriteString(rw, "2")
		}
	}
}

func (h *Handler) handleBindRequest(rw http.ResponseWriter, params *bindParams) {
	var channel *Channel
	sid := params.sid

	// If the client has specified a session id, lookup the session object in
	// the sessions map. Lookup failure should be signaled to the client using
	// a 400 status code and a message containing 'Unknown SID'. See
	// goog/net/channelrequest.js for more context on how this error is
	// handled.
	if sid != nullSessionId {
		channel = h.channels.get(sid)
		if channel == nil {
			log.Printf("failed to lookup session %s\n", sid)
			setHeaders(rw, &headers)
			rw.WriteHeader(400)
			io.WriteString(rw, "Unknown SID")
			return
		}
	}

	if channel == nil {
		sid, _ = generateSesionId(crand.Reader)
		log.Printf("creating session %s\n", sid)
		channel = newChannel(params.cver, sid, h.gcChan, h.corsInfo)
		h.channels.set(sid, channel)
		go h.chanHandler(channel)
		go channel.start()
	}

	if params.aid != -1 {
		channel.acknowledge(params.aid)
	}

	switch params.method {
	case "POST":
		h.handleBindPost(rw, params, channel)
	case "GET":
		h.handleBindGet(rw, params, channel)
	default:
		rw.WriteHeader(400)
	}
}

func (h *Handler) handleBindPost(rw http.ResponseWriter, params *bindParams, channel *Channel) {
	log.Printf("%s: bind parameters: %v\n", channel.Sid, params.values)

	offset, maps, err := parseIncomingMaps(params.values)
	if err != nil {
		rw.WriteHeader(400)
		return
	}

	channel.receiveMaps(offset, maps)

	if channel.state == channelInit {
		setHeaders(rw, &headers)
		rw.WriteHeader(200)
		rw.(http.Flusher).Flush()

		// The initial forward request is used as a back channel to send the
		// server configuration: ['c', id, host, version]. This payload has to
		// be sent immediately, so the streaming is disabled on the back
		// channel. Note that the first bind request made by IE<10 does not
		// contain a TYPE=html query parameter and therefore receives the same
		// length prefixed array reply as is sent to the XHR streaming clients.
		backChannel := newBackChannel(channel.Sid, rw, false, "", params.rid)
		channel.setBackChannel(backChannel)
		backChannel.wait()
	} else {
		// On normal forward channel request, the session status is returned
		// to the client. The session status contains 3 pieces of information:
		// does this session has a back channel, the last array id sent to the
		// client and the number of outstanding bytes in the back channel.
		b, _ := json.Marshal(channel.getState())
		setHeaders(rw, &headers)
		rw.WriteHeader(200)
		io.WriteString(rw, strconv.FormatInt(int64(len(b)), 10)+"\n")
		rw.Write(b)
	}
}

func (h *Handler) handleBindGet(rw http.ResponseWriter, params *bindParams, channel *Channel) {
	if params.qtype == queryTerminate {
		channel.Close()
	} else {
		params.qtype.setContentType(rw)
		setHeaders(rw, &headers)
		rw.WriteHeader(200)
		rw.(http.Flusher).Flush()

		isHtml := params.qtype == queryHtml
		bc := newBackChannel(channel.Sid, rw, isHtml, params.domain, params.rid)
		bc.setChunked(params.chunked)
		channel.setBackChannel(bc)
		bc.wait()
	}
}
<commit_msg>Add comments.<commit_after>// Copyright (c) 2013 Mathieu Turcotte
// Licensed under the MIT license.

// Package browserchannel provides a server-side browser channel
// implementation. See http://goo.gl/F287G for the client-side API.
package browserchannel

import (
	crand "crypto/rand"
	"encoding/json"
	"io"
	"log"
	"math/rand"
	"net/http"
	"net/url"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"time"
)

// The browser channel protocol version implemented by this library.
const SupportedProcolVersion = 8

const (
	// The path for the channel connection.
	DefaultBindPath = "bind"
	// The path for the test connection.
	DefaultTestPath = "test"
)

type queryType int

// Possible values for the query TYPE parameter.
const (
	queryUnset = iota
	queryTerminate
	queryXmlHttp
	queryHtml
	queryTest
)

func parseQueryType(s string) (qtype queryType) {
	switch s {
	case "html":
		qtype = queryHtml
	case "xmlhttp":
		qtype = queryXmlHttp
	case "terminate":
		qtype = queryTerminate
	case "test":
		qtype = queryTest
	}
	return
}

func (qtype queryType) setContentType(rw http.ResponseWriter) {
	if qtype == queryHtml {
		rw.Header().Set("Content-Type", "text/html")
	} else {
		rw.Header().Set("Content-Type", "text/plain")
	}
}

func parseAid(s string) (aid int, err error) {
	aid = -1
	if len(s) == 0 {
		return
	}
	aid, err = strconv.Atoi(s)
	return
}

func parseProtoVersion(s string) (version int) {
	version, err := strconv.Atoi(s)
	if err != nil {
		version = -1
	}
	return
}

type bindParams struct {
	cver    string
	sid     SessionId
	qtype   queryType
	domain  string
	rid     string
	aid     int
	chunked bool
	values  url.Values
	method  string
}

func parseBindParams(req *http.Request, values url.Values) (params *bindParams, err error) {
	cver := req.Form.Get("VER")
	qtype := parseQueryType(req.Form.Get("TYPE"))
	domain := req.Form.Get("DOMAIN")
	rid := req.Form.Get("zx")
	ci := req.Form.Get("CI") == "1"
	sid, err := parseSessionId(req.Form.Get("SID"))
	if err != nil {
		return
	}
	aid, err := parseAid(req.Form.Get("AID"))
	if err != nil {
		return
	}
	params = &bindParams{cver, sid, qtype, domain, rid, aid, ci, values, req.Method}
	return
}

type testParams struct {
	ver    int
	init   bool
	qtype  queryType
	domain string
}

func parseTestParams(req *http.Request) *testParams {
	version := parseProtoVersion(req.Form.Get("VER"))
	qtype := parseQueryType(req.Form.Get("TYPE"))
	domain := req.Form.Get("DOMAIN")
	init := req.Form.Get("MODE") == "init"
	return &testParams{version, init, qtype, domain}
}

var headers = map[string]string{
	"Cache-Control":          "no-cache, no-store, max-age=0, must-revalidate",
	"Expires":                "Fri, 01 Jan 1990 00:00:00 GMT",
	"X-Content-Type-Options": "nosniff",
	"Transfer-Encoding":      "chunked",
	"Pragma":                 "no-cache",
}

type channelMap struct {
	sync.RWMutex
	m map[SessionId]*Channel
}

func (m *channelMap) get(sid SessionId) *Channel {
	m.RLock()
	defer m.RUnlock()
	return m.m[sid]
}

func (m *channelMap) set(sid SessionId, channel *Channel) {
	m.Lock()
	defer m.Unlock()
	m.m[sid] = channel
}

func (m *channelMap) del(sid SessionId) (c *Channel, deleted bool) {
	m.Lock()
	defer m.Unlock()
	c, deleted = m.m[sid]
	delete(m.m, sid)
	return
}

// Contains the browser channel cross domain info for a single domain.
type crossDomainInfo struct {
	hostMatcher *regexp.Regexp
	domain      string
	prefixes    []string
}

func getHostPrefix(info *crossDomainInfo) string {
	if info != nil {
		return info.prefixes[rand.Intn(len(info.prefixes))]
	}
	return ""
}

// The browser channel HTTP handler will invoke its ChannelHandler in a
// goroutine for each new browser channel connection established.
type ChannelHandler func(*Channel)

// The browser channel http.Handler.
type Handler struct {
	corsInfo    *crossDomainInfo
	prefix      string
	channels    *channelMap
	bindPath    string
	testPath    string
	gcChan      chan SessionId
	chanHandler ChannelHandler
}

// Creates a new browser channel HTTP handler. The last path segment of the
// URL is used to distinguish bind and test connections.
func NewHandler(chanHandler ChannelHandler) (h *Handler) {
	h = new(Handler)
	h.channels = &channelMap{m: make(map[SessionId]*Channel)}
	h.bindPath = DefaultBindPath
	h.testPath = DefaultTestPath
	h.gcChan = make(chan SessionId, 10)
	h.chanHandler = chanHandler
	go h.removeClosedSession()
	return
}

// Sets the cross domain information for this browser channel. The origin is
// used as the Access-Control-Allow-Origin header value and should respect the
// format specified in http://www.w3.org/TR/cors/. The prefix is used to set
// the hostPrefix parameter on the client side.
func (h *Handler) SetCrossDomainPrefix(domain string, prefixes []string) {
	h.corsInfo = &crossDomainInfo{makeOriginMatcher(domain), domain, prefixes}
}

// Removes closed channels from the handler's channel map.
func (h *Handler) removeClosedSession() {
	for {
		sid, ok := <-h.gcChan
		if !ok {
			break
		}

		log.Printf("removing %s from session map\n", sid)

		if channel, ok := h.channels.del(sid); ok {
			channel.dispose()
		} else {
			log.Printf("missing channel for %s in session map\n", sid)
		}
	}
}

func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	// The CORS  spec only supports *, null or the exact domain.
	// http://www.w3.org/TR/cors/#access-control-allow-origin-response-header
	// http://tools.ietf.org/html/rfc6454#section-7.1
	origin := req.Header.Get("origin")
	if len(origin) > 0 && h.corsInfo != nil &&
		h.corsInfo.hostMatcher.MatchString(origin) {
		rw.Header().Set("Access-Control-Allow-Origin", origin)
		rw.Header().Set("Access-Control-Allow-Credentials", "true")
	}

	// The body is parsed before calling ParseForm so the values don't get
	// collapsed into a single collection.
	values, err := parseBody(req.Body)
	if err != nil {
		rw.WriteHeader(400)
		return
	}

	req.ParseForm()

	path := req.URL.Path
	if strings.HasSuffix(path, h.testPath) {
		log.Printf("test:%s\n", req.URL)
		h.handleTestRequest(rw, parseTestParams(req))
	} else if strings.HasSuffix(path, h.bindPath) {
		log.Printf("bind:%s:%s\n", req.Method, req.URL)
		params, err := parseBindParams(req, values)
		if err != nil {
			rw.WriteHeader(400)
			return
		}
		h.handleBindRequest(rw, params)
	} else {
		rw.WriteHeader(404)
	}
}

func (h *Handler) handleTestRequest(rw http.ResponseWriter, params *testParams) {
	if params.ver != SupportedProcolVersion {
		rw.WriteHeader(400)
		io.WriteString(rw, "Unsupported protocol version.")
	} else if params.init {
		setHeaders(rw, &headers)
		rw.WriteHeader(200)
		io.WriteString(rw, "[\""+getHostPrefix(h.corsInfo)+"\",\"\"]")
	} else {
		params.qtype.setContentType(rw)
		setHeaders(rw, &headers)
		rw.WriteHeader(200)

		if params.qtype == queryHtml {
			writeHtmlHead(rw)
			writeHtmlDomain(rw, params.domain)
			writeHtmlRpc(rw, "11111")
			writeHtmlPadding(rw)
		} else {
			io.WriteString(rw, "11111")
		}

		time.Sleep(2 * time.Second)

		if params.qtype == queryHtml {
			writeHtmlRpc(rw, "2")
			writeHtmlDone(rw)
		} else {
			io.WriteString(rw, "2")
		}
	}
}

func (h *Handler) handleBindRequest(rw http.ResponseWriter, params *bindParams) {
	var channel *Channel
	sid := params.sid

	// If the client has specified a session id, lookup the session object in
	// the sessions map. Lookup failure should be signaled to the client using
	// a 400 status code and a message containing 'Unknown SID'. See
	// goog/net/channelrequest.js for more context on how this error is
	// handled.
	if sid != nullSessionId {
		channel = h.channels.get(sid)
		if channel == nil {
			log.Printf("failed to lookup session %s\n", sid)
			setHeaders(rw, &headers)
			rw.WriteHeader(400)
			io.WriteString(rw, "Unknown SID")
			return
		}
	}

	if channel == nil {
		sid, _ = generateSesionId(crand.Reader)
		log.Printf("creating session %s\n", sid)
		channel = newChannel(params.cver, sid, h.gcChan, h.corsInfo)
		h.channels.set(sid, channel)
		go h.chanHandler(channel)
		go channel.start()
	}

	if params.aid != -1 {
		channel.acknowledge(params.aid)
	}

	switch params.method {
	case "POST":
		h.handleBindPost(rw, params, channel)
	case "GET":
		h.handleBindGet(rw, params, channel)
	default:
		rw.WriteHeader(400)
	}
}

func (h *Handler) handleBindPost(rw http.ResponseWriter, params *bindParams, channel *Channel) {
	log.Printf("%s: bind parameters: %v\n", channel.Sid, params.values)

	offset, maps, err := parseIncomingMaps(params.values)
	if err != nil {
		rw.WriteHeader(400)
		return
	}

	channel.receiveMaps(offset, maps)

	if channel.state == channelInit {
		setHeaders(rw, &headers)
		rw.WriteHeader(200)
		rw.(http.Flusher).Flush()

		// The initial forward request is used as a back channel to send the
		// server configuration: ['c', id, host, version]. This payload has to
		// be sent immediately, so the streaming is disabled on the back
		// channel. Note that the first bind request made by IE<10 does not
		// contain a TYPE=html query parameter and therefore receives the same
		// length prefixed array reply as is sent to the XHR streaming clients.
		backChannel := newBackChannel(channel.Sid, rw, false, "", params.rid)
		channel.setBackChannel(backChannel)
		backChannel.wait()
	} else {
		// On normal forward channel request, the session status is returned
		// to the client. The session status contains 3 pieces of information:
		// does this session has a back channel, the last array id sent to the
		// client and the number of outstanding bytes in the back channel.
		b, _ := json.Marshal(channel.getState())
		setHeaders(rw, &headers)
		rw.WriteHeader(200)
		io.WriteString(rw, strconv.FormatInt(int64(len(b)), 10)+"\n")
		rw.Write(b)
	}
}

func (h *Handler) handleBindGet(rw http.ResponseWriter, params *bindParams, channel *Channel) {
	if params.qtype == queryTerminate {
		channel.Close()
	} else {
		params.qtype.setContentType(rw)
		setHeaders(rw, &headers)
		rw.WriteHeader(200)
		rw.(http.Flusher).Flush()

		isHtml := params.qtype == queryHtml
		bc := newBackChannel(channel.Sid, rw, isHtml, params.domain, params.rid)
		bc.setChunked(params.chunked)
		channel.setBackChannel(bc)
		bc.wait()
	}
}
<|endoftext|>


<commit_before>package counters

import (
	"bytes"
	"fmt"
	m20 "github.com/metrics20/go-metrics20"
	"github.com/vimeo/statsdaemon/common"
)

type Counters struct {
	prefixRates string
	values      map[string]float64
}

func New(prefixRates string) *Counters {
	return &Counters{
		prefixRates,
		make(map[string]float64),
	}
}

// Add updates the counters map, adding the metric key if needed
func (c *Counters) Add(metric *common.Metric) {
	_, ok := c.values[metric.Bucket]
	if !ok {
		c.values[metric.Bucket] = 0
	}
	c.values[metric.Bucket] += metric.Value * float64(1/metric.Sampling)
}

// processCounters computes the outbound metrics for counters and puts them in the buffer
func (c *Counters) Process(buffer *bytes.Buffer, now int64, interval int) int64 {
	var num int64
	for key, val := range c.values {
		val := val / float64(interval)
		fmt.Fprintf(buffer, "%s %f %d\n", m20.DeriveCount(key, c.prefixRates), val, now)
		num++
	}
	return num
}
<commit_msg>counter perf improvement: rely on Go empty value<commit_after>package counters

import (
	"bytes"
	"fmt"
	m20 "github.com/metrics20/go-metrics20"
	"github.com/vimeo/statsdaemon/common"
)

type Counters struct {
	prefixRates string
	values      map[string]float64
}

func New(prefixRates string) *Counters {
	return &Counters{
		prefixRates,
		make(map[string]float64),
	}
}

// Add updates the counters map, adding the metric key if needed
func (c *Counters) Add(metric *common.Metric) {
	c.values[metric.Bucket] += metric.Value * float64(1/metric.Sampling)
}

// processCounters computes the outbound metrics for counters and puts them in the buffer
func (c *Counters) Process(buffer *bytes.Buffer, now int64, interval int) int64 {
	var num int64
	for key, val := range c.values {
		val := val / float64(interval)
		fmt.Fprintf(buffer, "%s %f %d\n", m20.DeriveCount(key, c.prefixRates), val, now)
		num++
	}
	return num
}
<|endoftext|>


<commit_before>//   Copyright 2018 MSolution.IO
//
//   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 anomalies

import (
	"context"
	"fmt"
	"net/http"
	"time"

	"github.com/trackit/jsonlog"
	"github.com/trackit/trackit-server/aws"
	"github.com/trackit/trackit-server/db"
	"github.com/trackit/trackit-server/es"
	"github.com/trackit/trackit-server/routes"
	"github.com/trackit/trackit-server/users"

	"gopkg.in/olivere/elastic.v5"
)

// esQueryParams will store the parsed query params
type esQueryParams struct {
	dateBegin         time.Time
	dateEnd           time.Time
	accountList       []string
}

// anomalyQueryArgs allows to get required queryArgs params
var anomalyQueryArgs = []routes.QueryArg{
	routes.AwsAccountsOptionalQueryArg,
	routes.DateBeginQueryArg,
	routes.DateEndQueryArg,
}

func init() {
	routes.MethodMuxer{
		http.MethodGet: routes.H(getAnomaliesData).With(
			db.RequestTransaction{Db: db.Db},
			users.RequireAuthenticatedUser{users.ViewerAsParent},
			routes.QueryArgs(anomalyQueryArgs),
			routes.Documentation{
				Summary:     "get the cost anomalies",
				Description: "Responds with the cost anomalies based on the query args passed to it",
			},
		),
	}.H().Register("/costs/anomalies")
}

// makeElasticSearchRequest prepares and run the request to retrieve the cost anomalies.
// It will return the data, an http status code (as int) and an error.
// Because an error can be generated, but is not critical and is not needed to be known by
// the user (e.g if the index does not exists because it was not yet indexed) the error will
// be returned, but instead of having a 500 status code, it will return the provided status code
// with empty data
func makeElasticSearchRequest(ctx context.Context, parsedParams esQueryParams, user users.User) (*elastic.SearchResult, int, error) {
	l := jsonlog.LoggerFromContextOrDefault(ctx)
	index := es.IndexNameForUser(user, "lineitems")
	searchService := GetElasticSearchParams(
		parsedParams.accountList,
		parsedParams.dateBegin,
		parsedParams.dateEnd,
		"12h",
		es.Client,
		index,
	)
	res, err := searchService.Do(ctx)
	if err != nil {
		if elastic.IsNotFound(err) {
			l.Warning("Query execution failed, ES index does not exists : "+index, err)
			return nil, http.StatusOK, err
		}
		l.Error("Query execution failed : "+err.Error(), nil)
		return nil, http.StatusInternalServerError, fmt.Errorf("could not execute the ElasticSearch query")
	}
	return res, http.StatusOK, nil
}

// getAnomaliesData returns the cost anomalies based on the query params, in JSON format.
func getAnomaliesData(request *http.Request, a routes.Arguments) (int, interface{}) {
	user := a[users.AuthenticatedUser].(users.User)
	parsedParams := esQueryParams{
		accountList:       []string{},
		dateBegin:         a[anomalyQueryArgs[1]].(time.Time),
		dateEnd:           a[anomalyQueryArgs[2]].(time.Time),
	}
	if a[anomalyQueryArgs[0]] != nil {
		parsedParams.accountList = a[anomalyQueryArgs[0]].([]string)
	}
	if err := aws.ValidateAwsAccounts(parsedParams.accountList); err != nil {
		return http.StatusBadRequest, err
	}
	sr, returnCode, err := makeElasticSearchRequest(request.Context(), parsedParams, user)
	if err != nil {
		if returnCode == http.StatusOK {
			return returnCode, nil
		} else {
			return returnCode, err
		}
	}
	res, err := prepareAnomalyData(request.Context(), sr)
	if err != nil {
		return http.StatusInternalServerError, err
	}
	return http.StatusOK, res
}
<commit_msg>Added 23h59m59s to dateEnd in Anomalies Detection.<commit_after>//   Copyright 2018 MSolution.IO
//
//   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 anomalies

import (
	"context"
	"fmt"
	"net/http"
	"time"

	"github.com/trackit/jsonlog"
	"github.com/trackit/trackit-server/aws"
	"github.com/trackit/trackit-server/db"
	"github.com/trackit/trackit-server/es"
	"github.com/trackit/trackit-server/routes"
	"github.com/trackit/trackit-server/users"

	"gopkg.in/olivere/elastic.v5"
)

// esQueryParams will store the parsed query params
type esQueryParams struct {
	dateBegin         time.Time
	dateEnd           time.Time
	accountList       []string
}

// anomalyQueryArgs allows to get required queryArgs params
var anomalyQueryArgs = []routes.QueryArg{
	routes.AwsAccountsOptionalQueryArg,
	routes.DateBeginQueryArg,
	routes.DateEndQueryArg,
}

func init() {
	routes.MethodMuxer{
		http.MethodGet: routes.H(getAnomaliesData).With(
			db.RequestTransaction{Db: db.Db},
			users.RequireAuthenticatedUser{users.ViewerAsParent},
			routes.QueryArgs(anomalyQueryArgs),
			routes.Documentation{
				Summary:     "get the cost anomalies",
				Description: "Responds with the cost anomalies based on the query args passed to it",
			},
		),
	}.H().Register("/costs/anomalies")
}

// makeElasticSearchRequest prepares and run the request to retrieve the cost anomalies.
// It will return the data, an http status code (as int) and an error.
// Because an error can be generated, but is not critical and is not needed to be known by
// the user (e.g if the index does not exists because it was not yet indexed) the error will
// be returned, but instead of having a 500 status code, it will return the provided status code
// with empty data
func makeElasticSearchRequest(ctx context.Context, parsedParams esQueryParams, user users.User) (*elastic.SearchResult, int, error) {
	l := jsonlog.LoggerFromContextOrDefault(ctx)
	index := es.IndexNameForUser(user, "lineitems")
	searchService := GetElasticSearchParams(
		parsedParams.accountList,
		parsedParams.dateBegin,
		parsedParams.dateEnd,
		"12h",
		es.Client,
		index,
	)
	res, err := searchService.Do(ctx)
	if err != nil {
		if elastic.IsNotFound(err) {
			l.Warning("Query execution failed, ES index does not exists : "+index, err)
			return nil, http.StatusOK, err
		}
		l.Error("Query execution failed : "+err.Error(), nil)
		return nil, http.StatusInternalServerError, fmt.Errorf("could not execute the ElasticSearch query")
	}
	return res, http.StatusOK, nil
}

// getAnomaliesData returns the cost anomalies based on the query params, in JSON format.
func getAnomaliesData(request *http.Request, a routes.Arguments) (int, interface{}) {
	user := a[users.AuthenticatedUser].(users.User)
	parsedParams := esQueryParams{
		accountList:       []string{},
		dateBegin:         a[anomalyQueryArgs[1]].(time.Time),
		dateEnd:           a[anomalyQueryArgs[2]].(time.Time).Add(time.Hour*time.Duration(23) + time.Minute*time.Duration(59) + time.Second*time.Duration(59)),
	}
	if a[anomalyQueryArgs[0]] != nil {
		parsedParams.accountList = a[anomalyQueryArgs[0]].([]string)
	}
	if err := aws.ValidateAwsAccounts(parsedParams.accountList); err != nil {
		return http.StatusBadRequest, err
	}
	sr, returnCode, err := makeElasticSearchRequest(request.Context(), parsedParams, user)
	if err != nil {
		if returnCode == http.StatusOK {
			return returnCode, nil
		} else {
			return returnCode, err
		}
	}
	res, err := prepareAnomalyData(request.Context(), sr)
	if err != nil {
		return http.StatusInternalServerError, err
	}
	return http.StatusOK, res
}
<|endoftext|>


<commit_before>package main

import "fmt"
import "time"

const MESSAGES = 5 * 1000 * 1000
const THREADS = 4

type Message = uintptr

func seq(cap int) {
    var c = make(chan Message, cap)

    for i := 0; i < MESSAGES; i++ {
        c <- Message(i)
    }

    for i := 0; i < MESSAGES; i++ {
        <-c
    }
}

func spsc(cap int) {
    var c = make(chan Message, cap)
    var done = make(chan bool)

    go func() {
        for i := 0; i < MESSAGES; i++ {
            c <- Message(i)
        }
        done <- true
    }()

    for i := 0; i < MESSAGES; i++ {
        <-c
    }

    <-done
}

func mpsc(cap int) {
    var c = make(chan Message, cap)
    var done = make(chan bool)

    for t := 0; t < THREADS; t++ {
        go func() {
            for i := 0; i < MESSAGES / THREADS; i++ {
                c <- Message(i)
            }
            done <- true
        }()
    }

    for i := 0; i < MESSAGES; i++ {
        <-c
    }

    for t := 0; t < THREADS; t++ {
        <-done
    }
}

func mpmc(cap int) {
    var c = make(chan Message, cap)
    var done = make(chan bool)

    for t := 0; t < THREADS; t++ {
        go func() {
            for i := 0; i < MESSAGES / THREADS; i++ {
                c <- Message(i)
            }
            done <- true
        }()

    }

    for t := 0; t < THREADS; t++ {
        go func() {
            for i := 0; i < MESSAGES / THREADS; i++ {
                <-c
            }
            done <- true
        }()
    }

    for t := 0; t < THREADS; t++ {
        <-done
        <-done
    }
}

func select_rx(cap int) {
    if THREADS != 4 {
        panic("assumed there are 4 threads")
    }

    var c0 = make(chan Message, cap)
    var c1 = make(chan Message, cap)
    var c2 = make(chan Message, cap)
    var c3 = make(chan Message, cap)
    var done = make(chan bool)

    var producer = func(c chan Message) {
        for i := 0; i < MESSAGES / THREADS; i++ {
            c <- Message(i)
        }
        done <- true
    }
    go producer(c0)
    go producer(c1)
    go producer(c2)
    go producer(c3)

    for i := 0; i < MESSAGES; i++ {
        select {
        case <-c0:
        case <-c1:
        case <-c2:
        case <-c3:
        }
    }

    for t := 0; t < THREADS; t++ {
        <-done
    }
}

func select_both(cap int) {
    if THREADS != 4 {
        panic("assumed there are 4 threads")
    }

    var c0 = make(chan Message, cap)
    var c1 = make(chan Message, cap)
    var c2 = make(chan Message, cap)
    var c3 = make(chan Message, cap)
    var done = make(chan bool)

    var producer = func(c chan Message) {
        for i := 0; i < MESSAGES / THREADS; i++ {
            c <- Message(i)
        }
        done <- true
    }
    go producer(c0)
    go producer(c1)
    go producer(c2)
    go producer(c3)

    for t := 0; t < THREADS; t++ {
        go func() {
            for i := 0; i < MESSAGES / THREADS; i++ {
                select {
                case <-c0:
                case <-c1:
                case <-c2:
                case <-c3:
                }
            }
            done <- true
        }()
    }

    for t := 0; t < THREADS; t++ {
        <-done
        <-done
    }
}

func run(name string, f func(int), cap int) {
    var now = time.Now()
    f(cap)
    var elapsed = time.Now().Sub(now)
    fmt.Printf("%-25v %-15v %7.3f sec\n", name, "Go chan", float64(elapsed) / float64(time.Second))
}

func main() {
    run("bounded0_mpmc", mpmc, 0)
    run("bounded0_mpsc", mpsc, 0)
    run("bounded0_select_both", select_both, 0)
    run("bounded0_select_rx", select_rx, 0)
    run("bounded0_spsc", spsc, 0)

    run("bounded1_mpmc", mpmc, 1)
    run("bounded1_mpsc", mpsc, 1)
    run("bounded1_select_both", select_both, 1)
    run("bounded1_select_rx", select_rx, 1)
    run("bounded1_spsc", spsc, 1)

    run("bounded_mpmc", mpmc, MESSAGES)
    run("bounded_mpsc", mpsc, MESSAGES)
    run("bounded_select_both", select_both, MESSAGES)
    run("bounded_select_rx", select_rx, MESSAGES)
    run("bounded_seq", seq, MESSAGES)
    run("bounded_spsc", spsc, MESSAGES)
}
<commit_msg>fixed go.go to properly select over channels during put in select_both<commit_after>package main

import "fmt"
import "time"

const MESSAGES = 5 * 1000 * 1000
const THREADS = 4

type Message = uintptr

func seq(cap int) {
    var c = make(chan Message, cap)

    for i := 0; i < MESSAGES; i++ {
        c <- Message(i)
    }

    for i := 0; i < MESSAGES; i++ {
        <-c
    }
}

func spsc(cap int) {
    var c = make(chan Message, cap)
    var done = make(chan bool)

    go func() {
        for i := 0; i < MESSAGES; i++ {
            c <- Message(i)
        }
        done <- true
    }()

    for i := 0; i < MESSAGES; i++ {
        <-c
    }

    <-done
}

func mpsc(cap int) {
    var c = make(chan Message, cap)
    var done = make(chan bool)

    for t := 0; t < THREADS; t++ {
        go func() {
            for i := 0; i < MESSAGES / THREADS; i++ {
                c <- Message(i)
            }
            done <- true
        }()
    }

    for i := 0; i < MESSAGES; i++ {
        <-c
    }

    for t := 0; t < THREADS; t++ {
        <-done
    }
}

func mpmc(cap int) {
    var c = make(chan Message, cap)
    var done = make(chan bool)

    for t := 0; t < THREADS; t++ {
        go func() {
            for i := 0; i < MESSAGES / THREADS; i++ {
                c <- Message(i)
            }
            done <- true
        }()

    }

    for t := 0; t < THREADS; t++ {
        go func() {
            for i := 0; i < MESSAGES / THREADS; i++ {
                <-c
            }
            done <- true
        }()
    }

    for t := 0; t < THREADS; t++ {
        <-done
        <-done
    }
}

func select_rx(cap int) {
    if THREADS != 4 {
        panic("assumed there are 4 threads")
    }

    var c0 = make(chan Message, cap)
    var c1 = make(chan Message, cap)
    var c2 = make(chan Message, cap)
    var c3 = make(chan Message, cap)
    var done = make(chan bool)

    var producer = func(c chan Message) {
        for i := 0; i < MESSAGES / THREADS; i++ {
            c <- Message(i)
        }
        done <- true
    }
    go producer(c0)
    go producer(c1)
    go producer(c2)
    go producer(c3)

    for i := 0; i < MESSAGES; i++ {
        select {
        case <-c0:
        case <-c1:
        case <-c2:
        case <-c3:
        }
    }

    for t := 0; t < THREADS; t++ {
        <-done
    }
}

func select_both(cap int) {
    if THREADS != 4 {
        panic("assumed there are 4 threads")
    }

    var c0 = make(chan Message, cap)
    var c1 = make(chan Message, cap)
    var c2 = make(chan Message, cap)
    var c3 = make(chan Message, cap)
    var done = make(chan bool)

    var producer = func(c0 chan Message, c1 chan Message, c2 chan Message, c3 chan Message) {
        for i := 0; i < MESSAGES / THREADS; i++ {
            select {
            case c0 <- Message(i);
            case c1 <- Message(i);
            case c2 <- Message(i);
            case c3 <- Message(i);
            }
        }
        done <- true
    }
    go producer(c0,c1,c2,c3)
    go producer(c0,c1,c2,c3)
    go producer(c0,c1,c2,c3)
    go producer(c0,c1,c2,c3)

    for t := 0; t < THREADS; t++ {
        go func() {
            for i := 0; i < MESSAGES / THREADS; i++ {
                select {
                case <-c0:
                case <-c1:
                case <-c2:
                case <-c3:
                }
            }
            done <- true
        }()
    }

    for t := 0; t < THREADS; t++ {
        <-done
        <-done
    }
}

func run(name string, f func(int), cap int) {
    var now = time.Now()
    f(cap)
    var elapsed = time.Now().Sub(now)
    fmt.Printf("%-25v %-15v %7.3f sec\n", name, "Go chan", float64(elapsed) / float64(time.Second))
}

func main() {
    run("bounded0_mpmc", mpmc, 0)
    run("bounded0_mpsc", mpsc, 0)
    run("bounded0_select_both", select_both, 0)
    run("bounded0_select_rx", select_rx, 0)
    run("bounded0_spsc", spsc, 0)

    run("bounded1_mpmc", mpmc, 1)
    run("bounded1_mpsc", mpsc, 1)
    run("bounded1_select_both", select_both, 1)
    run("bounded1_select_rx", select_rx, 1)
    run("bounded1_spsc", spsc, 1)

    run("bounded_mpmc", mpmc, MESSAGES)
    run("bounded_mpsc", mpsc, MESSAGES)
    run("bounded_select_both", select_both, MESSAGES)
    run("bounded_select_rx", select_rx, MESSAGES)
    run("bounded_seq", seq, MESSAGES)
    run("bounded_spsc", spsc, MESSAGES)
}
<|endoftext|>