<commit_before>package core

import (
	"context"

	"chain/core/accesstoken"
	"chain/core/account"
	"chain/core/account/utxodb"
	"chain/core/asset"
	"chain/core/blocksigner"
	"chain/core/mockhsm"
	"chain/core/query"
	"chain/core/query/filter"
	"chain/core/rpc"
	"chain/core/signers"
	"chain/core/txbuilder"
	"chain/core/txfeed"
	"chain/database/pg"
	"chain/errors"
	"chain/net/http/httpjson"
	"chain/protocol"
)

// errorInfo contains a set of error codes to send to the user.
type errorInfo struct {
	HTTPStatus int    `json:"-"`
	ChainCode  string `json:"code"`
	Message    string `json:"message"`
}

type detailedError struct {
	errorInfo
	Detail    string      `json:"detail,omitempty"`
	Data      interface{} `json:"data,omitempty"`
	Temporary bool        `json:"temporary"`
}

var temporaryErrorCodes = map[string]bool{
	"CH000": true, // internal server error
	"CH001": true, // request timed out
	"CH761": true, // outputs currently reserved
}

var (
	// infoInternal holds the codes we use for an internal error.
	// It is defined here for easy reference.
	infoInternal = errorInfo{500, "CH000", "Chain API Error"}

	// Map error values to standard chain error codes.
	// Missing entries will map to infoInternal.
	// See chain.com/docs.
	errorInfoTab = map[error]errorInfo{
		// General error namespace (0xx)
		context.DeadlineExceeded:     errorInfo{408, "CH001", "Request timed out"},
		pg.ErrUserInputNotFound:      errorInfo{400, "CH002", "Not found"},
		httpjson.ErrBadRequest:       errorInfo{400, "CH003", "Invalid request body"},
		errBadReqHeader:              errorInfo{400, "CH004", "Invalid request header"},
		errNotFound:                  errorInfo{404, "CH006", "Not found"},
		errRateLimited:               errorInfo{429, "CH007", "Request limit exceeded"},
		errLeaderElection:            errorInfo{503, "CH008", "Electing a new leader for the core; try again soon"},
		errNotAuthenticated:          errorInfo{401, "CH009", "Request could not be authenticated"},
		asset.ErrDuplicateAlias:      errorInfo{400, "CH050", "Alias already exists"},
		account.ErrDuplicateAlias:    errorInfo{400, "CH050", "Alias already exists"},
		txfeed.ErrDuplicateAlias:     errorInfo{400, "CH050", "Alias already exists"},
		mockhsm.ErrDuplicateKeyAlias: errorInfo{400, "CH050", "Alias already exists"},

		// Core error namespace
		errUnconfigured:                errorInfo{400, "CH100", "This core still needs to be configured"},
		errAlreadyConfigured:           errorInfo{400, "CH101", "This core has already been configured"},
		errBadGenerator:                errorInfo{400, "CH102", "Generator URL returned an invalid response"},
		errBadBlockPub:                 errorInfo{400, "CH103", "Provided Block XPub is invalid"},
		rpc.ErrWrongNetwork:            errorInfo{502, "CH104", "A peer core is operating on a different blockchain network"},
		protocol.ErrTheDistantFuture:   errorInfo{400, "CH105", "Requested height is too far ahead"},
		errBadSignerURL:                errorInfo{400, "CH106", "Block signer URL is invalid"},
		errBadSignerPubkey:             errorInfo{400, "CH107", "Block signer pubkey is invalid"},
		errBadQuorum:                   errorInfo{400, "CH108", "Quorum must be greater than 0 if there are signers"},
		errProdReset:                   errorInfo{400, "CH110", "Reset can only be called in a development system"},
		errNoClientTokens:              errorInfo{400, "CH120", "Cannot enable client authentication with no client tokens"},
		blocksigner.ErrConsensusChange: errorInfo{400, "CH150", "Refuse to sign block with consensus change"},

		// Signers error namespace (2xx)
		signers.ErrBadQuorum: errorInfo{400, "CH200", "Quorum must be greater than 1 and less than or equal to the length of xpubs"},
		signers.ErrBadXPub:   errorInfo{400, "CH201", "Invalid xpub format"},
		signers.ErrNoXPubs:   errorInfo{400, "CH202", "At least one xpub is required"},
		signers.ErrBadType:   errorInfo{400, "CH203", "Retrieved type does not match expected type"},
		signers.ErrDupeXPub:  errorInfo{400, "CH204", "Root XPubs cannot contain the same key more than once"},

		// Access token error namespace (3xx)
		accesstoken.ErrBadID:       errorInfo{400, "CH300", "Malformed or empty access token id"},
		accesstoken.ErrBadType:     errorInfo{400, "CH301", "Access tokens must be type client or network"},
		accesstoken.ErrDuplicateID: errorInfo{400, "CH302", "Access token id is already in use"},
		errCurrentToken:            errorInfo{400, "CH310", "The access token used to authenticate this request cannot be deleted"},

		// Query error namespace (6xx)
		query.ErrBadAfter:               errorInfo{400, "CH600", "Malformed pagination parameter `after`"},
		query.ErrParameterCountMismatch: errorInfo{400, "CH601", "Incorrect number of parameters to filter"},
		filter.ErrBadFilter:             errorInfo{400, "CH602", "Malformed query filter"},

		// Transaction error namespace (7xx)
		// Build error namespace (70x)
		txbuilder.ErrBadRefData: errorInfo{400, "CH700", "Reference data does not match previous transaction's reference data"},
		errBadActionType:        errorInfo{400, "CH701", "Invalid action type"},
		errBadAlias:             errorInfo{400, "CH702", "Invalid alias on action"},
		errBadAction:            errorInfo{400, "CH703", "Invalid action object"},
		txbuilder.ErrBadAmount:  errorInfo{400, "CH704", "Invalid asset amount"},
		txbuilder.ErrBlankCheck: errorInfo{400, "CH705", "Unsafe transaction: leaves assets to be taken without requiring payment"},
		txbuilder.ErrAction:     errorInfo{400, "CH706", "One or more actions had an error: see attached data"},

		// Submit error namespace (73x)
		txbuilder.ErrMissingRawTx:          errorInfo{400, "CH730", "Missing raw transaction"},
		txbuilder.ErrBadInstructionCount:   errorInfo{400, "CH731", "Too many signing instructions in template for transaction"},
		txbuilder.ErrBadTxInputIdx:         errorInfo{400, "CH732", "Invalid transaction input index"},
		txbuilder.ErrBadWitnessComponent:   errorInfo{400, "CH733", "Invalid witness component"},
		txbuilder.ErrRejected:              errorInfo{400, "CH735", "Transaction rejected"},
		txbuilder.ErrNoTxSighashCommitment: errorInfo{400, "CH736", "Transaction is not final, additional actions still allowed"},

		// account action error namespace (76x)
		utxodb.ErrInsufficient: errorInfo{400, "CH760", "Insufficient funds for tx"},
		utxodb.ErrReserved:     errorInfo{400, "CH761", "Some outputs are reserved; try again"},

		// Mock HSM error namespace (80x)
		mockhsm.ErrInvalidAfter:         errorInfo{400, "CH801", "Invalid `after` in query"},
		mockhsm.ErrTooManyAliasesToList: errorInfo{400, "CH802", "Too many aliases to list"},
	}
)

// errInfo returns the HTTP status code to use
// and a suitable response body describing err
// by consulting the global lookup table.
// If no entry is found, it returns infoInternal.
func errInfo(err error) (body detailedError, info errorInfo) {
	root := errors.Root(err)
	// Some types cannot be used as map keys, for example slices.
	// If an error's underlying type is one of these, don't panic.
	// Just treat it like any other missing entry.
	defer func() {
		if err := recover(); err != nil {
			info = infoInternal
			body = detailedError{infoInternal, "", nil, true}
		}
	}()
	info, ok := errorInfoTab[root]
	if !ok {
		info = infoInternal
	}

	body = detailedError{
		errorInfo: info,
		Detail:    errors.Detail(err),
		Data:      errors.Data(err),
		Temporary: temporaryErrorCodes[info.ChainCode],
	}
	return body, info
}

// errInfoBodyList calls errInfo for each element in errs
// and returns the "body".
func errInfoBodyList(errs []error) (a []detailedError) {
	for _, err := range errs {
		body, _ := errInfo(err)
		a = append(a, body)
	}
	return a
}
<commit_msg>core: set errorInfo 'temporary' field for CH706<commit_after>package core

import (
	"context"

	"chain/core/accesstoken"
	"chain/core/account"
	"chain/core/account/utxodb"
	"chain/core/asset"
	"chain/core/blocksigner"
	"chain/core/mockhsm"
	"chain/core/query"
	"chain/core/query/filter"
	"chain/core/rpc"
	"chain/core/signers"
	"chain/core/txbuilder"
	"chain/core/txfeed"
	"chain/database/pg"
	"chain/errors"
	"chain/net/http/httpjson"
	"chain/protocol"
)

// errorInfo contains a set of error codes to send to the user.
type errorInfo struct {
	HTTPStatus int    `json:"-"`
	ChainCode  string `json:"code"`
	Message    string `json:"message"`
}

type detailedError struct {
	errorInfo
	Detail    string      `json:"detail,omitempty"`
	Data      interface{} `json:"data,omitempty"`
	Temporary bool        `json:"temporary"`
}

func isTemporary(info errorInfo, err error) bool {
	switch info.ChainCode {
	case "CH000": // internal server error
		return true
	case "CH001": // request timed out
		return true
	case "CH761": // outputs currently reserved
		return true
	case "CH706": // 1 or more action errors
		errs := errors.Data(err).([]detailedError)
		temp := true
		for _, actionErr := range errs {
			temp = temp && isTemporary(actionErr.errorInfo, nil)
		}
		return temp
	default:
		return false
	}
}

var (
	// infoInternal holds the codes we use for an internal error.
	// It is defined here for easy reference.
	infoInternal = errorInfo{500, "CH000", "Chain API Error"}

	// Map error values to standard chain error codes.
	// Missing entries will map to infoInternal.
	// See chain.com/docs.
	errorInfoTab = map[error]errorInfo{
		// General error namespace (0xx)
		context.DeadlineExceeded:     errorInfo{408, "CH001", "Request timed out"},
		pg.ErrUserInputNotFound:      errorInfo{400, "CH002", "Not found"},
		httpjson.ErrBadRequest:       errorInfo{400, "CH003", "Invalid request body"},
		errBadReqHeader:              errorInfo{400, "CH004", "Invalid request header"},
		errNotFound:                  errorInfo{404, "CH006", "Not found"},
		errRateLimited:               errorInfo{429, "CH007", "Request limit exceeded"},
		errLeaderElection:            errorInfo{503, "CH008", "Electing a new leader for the core; try again soon"},
		errNotAuthenticated:          errorInfo{401, "CH009", "Request could not be authenticated"},
		asset.ErrDuplicateAlias:      errorInfo{400, "CH050", "Alias already exists"},
		account.ErrDuplicateAlias:    errorInfo{400, "CH050", "Alias already exists"},
		txfeed.ErrDuplicateAlias:     errorInfo{400, "CH050", "Alias already exists"},
		mockhsm.ErrDuplicateKeyAlias: errorInfo{400, "CH050", "Alias already exists"},

		// Core error namespace
		errUnconfigured:                errorInfo{400, "CH100", "This core still needs to be configured"},
		errAlreadyConfigured:           errorInfo{400, "CH101", "This core has already been configured"},
		errBadGenerator:                errorInfo{400, "CH102", "Generator URL returned an invalid response"},
		errBadBlockPub:                 errorInfo{400, "CH103", "Provided Block XPub is invalid"},
		rpc.ErrWrongNetwork:            errorInfo{502, "CH104", "A peer core is operating on a different blockchain network"},
		protocol.ErrTheDistantFuture:   errorInfo{400, "CH105", "Requested height is too far ahead"},
		errBadSignerURL:                errorInfo{400, "CH106", "Block signer URL is invalid"},
		errBadSignerPubkey:             errorInfo{400, "CH107", "Block signer pubkey is invalid"},
		errBadQuorum:                   errorInfo{400, "CH108", "Quorum must be greater than 0 if there are signers"},
		errProdReset:                   errorInfo{400, "CH110", "Reset can only be called in a development system"},
		errNoClientTokens:              errorInfo{400, "CH120", "Cannot enable client authentication with no client tokens"},
		blocksigner.ErrConsensusChange: errorInfo{400, "CH150", "Refuse to sign block with consensus change"},

		// Signers error namespace (2xx)
		signers.ErrBadQuorum: errorInfo{400, "CH200", "Quorum must be greater than 1 and less than or equal to the length of xpubs"},
		signers.ErrBadXPub:   errorInfo{400, "CH201", "Invalid xpub format"},
		signers.ErrNoXPubs:   errorInfo{400, "CH202", "At least one xpub is required"},
		signers.ErrBadType:   errorInfo{400, "CH203", "Retrieved type does not match expected type"},
		signers.ErrDupeXPub:  errorInfo{400, "CH204", "Root XPubs cannot contain the same key more than once"},

		// Access token error namespace (3xx)
		accesstoken.ErrBadID:       errorInfo{400, "CH300", "Malformed or empty access token id"},
		accesstoken.ErrBadType:     errorInfo{400, "CH301", "Access tokens must be type client or network"},
		accesstoken.ErrDuplicateID: errorInfo{400, "CH302", "Access token id is already in use"},
		errCurrentToken:            errorInfo{400, "CH310", "The access token used to authenticate this request cannot be deleted"},

		// Query error namespace (6xx)
		query.ErrBadAfter:               errorInfo{400, "CH600", "Malformed pagination parameter `after`"},
		query.ErrParameterCountMismatch: errorInfo{400, "CH601", "Incorrect number of parameters to filter"},
		filter.ErrBadFilter:             errorInfo{400, "CH602", "Malformed query filter"},

		// Transaction error namespace (7xx)
		// Build error namespace (70x)
		txbuilder.ErrBadRefData: errorInfo{400, "CH700", "Reference data does not match previous transaction's reference data"},
		errBadActionType:        errorInfo{400, "CH701", "Invalid action type"},
		errBadAlias:             errorInfo{400, "CH702", "Invalid alias on action"},
		errBadAction:            errorInfo{400, "CH703", "Invalid action object"},
		txbuilder.ErrBadAmount:  errorInfo{400, "CH704", "Invalid asset amount"},
		txbuilder.ErrBlankCheck: errorInfo{400, "CH705", "Unsafe transaction: leaves assets to be taken without requiring payment"},
		txbuilder.ErrAction:     errorInfo{400, "CH706", "One or more actions had an error: see attached data"},

		// Submit error namespace (73x)
		txbuilder.ErrMissingRawTx:          errorInfo{400, "CH730", "Missing raw transaction"},
		txbuilder.ErrBadInstructionCount:   errorInfo{400, "CH731", "Too many signing instructions in template for transaction"},
		txbuilder.ErrBadTxInputIdx:         errorInfo{400, "CH732", "Invalid transaction input index"},
		txbuilder.ErrBadWitnessComponent:   errorInfo{400, "CH733", "Invalid witness component"},
		txbuilder.ErrRejected:              errorInfo{400, "CH735", "Transaction rejected"},
		txbuilder.ErrNoTxSighashCommitment: errorInfo{400, "CH736", "Transaction is not final, additional actions still allowed"},

		// account action error namespace (76x)
		utxodb.ErrInsufficient: errorInfo{400, "CH760", "Insufficient funds for tx"},
		utxodb.ErrReserved:     errorInfo{400, "CH761", "Some outputs are reserved; try again"},

		// Mock HSM error namespace (80x)
		mockhsm.ErrInvalidAfter:         errorInfo{400, "CH801", "Invalid `after` in query"},
		mockhsm.ErrTooManyAliasesToList: errorInfo{400, "CH802", "Too many aliases to list"},
	}
)

// errInfo returns the HTTP status code to use
// and a suitable response body describing err
// by consulting the global lookup table.
// If no entry is found, it returns infoInternal.
func errInfo(err error) (body detailedError, info errorInfo) {
	root := errors.Root(err)
	// Some types cannot be used as map keys, for example slices.
	// If an error's underlying type is one of these, don't panic.
	// Just treat it like any other missing entry.
	defer func() {
		if err := recover(); err != nil {
			info = infoInternal
			body = detailedError{infoInternal, "", nil, true}
		}
	}()
	info, ok := errorInfoTab[root]
	if !ok {
		info = infoInternal
	}

	body = detailedError{
		errorInfo: info,
		Detail:    errors.Detail(err),
		Data:      errors.Data(err),
		Temporary: isTemporary(info, err),
	}
	return body, info
}

// errInfoBodyList calls errInfo for each element in errs
// and returns the "body".
func errInfoBodyList(errs []error) (a []detailedError) {
	for _, err := range errs {
		body, _ := errInfo(err)
		a = append(a, body)
	}
	return a
}
<|endoftext|>


<commit_before>package backend

import (
	"gopkg.in/mgo.v2"
	"strings"
	"time"
)

type statData struct {
	itC  *mgo.Iter
	lenC int
	itV  *mgo.Iter
	lenV int
}

func loopStat(c chan Message, db *mgo.Database) {
	followed := []string{}
	oneMinute := time.NewTicker(time.Minute).C
	tenMinute := time.NewTicker(10 * time.Minute).C
	oneHour := time.NewTicker(time.Hour).C
	oneDay := time.NewTicker(24 * time.Hour).C

	for {
		select {
		case msg := <-c:
			followed = followedHandler(followed, msg)
		case <-oneMinute:
			go computeStat(db, followed, time.Minute)
		case <-tenMinute:
			go computeStat(db, followed, 10*time.Minute)
		case <-oneHour:
			go computeStat(db, followed, time.Hour)
		case <-oneDay:
			go computeStat(db, followed, 24*time.Hour)
		}

	}
}

func computeStat(db *mgo.Database, channels []string, duration time.Duration) {
	to := time.Now()
	from := to.Add(-duration)

	for _, channel := range channels {
		data, err := fetchStatData(db, channel, from, to)
		if err != nil {
			se := processStatData(from, to, duration, channel, data)
			storeStatEntry(db.C("stat_entries"), se)
		}
	}
}

func processStatData(from time.Time, to time.Time, duration time.Duration, channel string, data statData) (ret StatEntry) {
	ret.Channel = channel
	ret.Duration = duration
	ret.Start = from
	ret.End = to

	var resultC ChatEntry
	uniqueChatter := make(map[string]bool)
	termUsed := make(map[string]int)

	for data.itC.Next(&resultC) {
		if resultC.Sender == "twitchnotify" {
			if strings.Contains(resultC.Text, "just") {
				ret.Newsub += 1
			} else if strings.Contains(resultC.Text, "months") {
				ret.Resub += 1
			}
		} else {
			ret.Messages += 1

			for _, i := range strings.Split(resultC.Text, " ") {
				termUsed[i] += 1
			}

			_, present := uniqueChatter[resultC.Sender]
			if !present {
				uniqueChatter[resultC.Sender] = true
			}
		}
	}
	ret.UniqueChat = len(uniqueChatter)

	var result ViewerCount
	ret.Viewer = 0
	for data.itV.Next(&result) {
		ret.Viewer += result.Viewer
	}
	ret.Viewer /= data.lenV
	return
}
<commit_msg>bugfix<commit_after>package backend

import (
	"gopkg.in/mgo.v2"
	"strings"
	"time"
)

type statData struct {
	itC  *mgo.Iter
	lenC int
	itV  *mgo.Iter
	lenV int
}

func loopStat(c chan Message, db *mgo.Database) {
	followed := []string{}
	oneMinute := time.NewTicker(time.Minute).C
	tenMinute := time.NewTicker(10 * time.Minute).C
	oneHour := time.NewTicker(time.Hour).C
	oneDay := time.NewTicker(24 * time.Hour).C

	for {
		select {
		case msg := <-c:
			followed = followedHandler(followed, msg)
		case <-oneMinute:
			go computeStat(db, followed, time.Minute)
		case <-tenMinute:
			go computeStat(db, followed, 10*time.Minute)
		case <-oneHour:
			go computeStat(db, followed, time.Hour)
		case <-oneDay:
			go computeStat(db, followed, 24*time.Hour)
		}

	}
}

func computeStat(db *mgo.Database, channels []string, duration time.Duration) {
	to := time.Now()
	from := to.Add(-duration)

	for _, channel := range channels {
		data, err := fetchStatData(db, channel, from, to)
		if err == nil {
			se := processStatData(from, to, duration, channel, data)
			storeStatEntry(db.C("stat_entries"), se)
		}
	}
}

func processStatData(from time.Time, to time.Time, duration time.Duration, channel string, data statData) (ret StatEntry) {
	ret.Channel = channel
	ret.Duration = duration
	ret.Start = from
	ret.End = to

	var resultC ChatEntry
	uniqueChatter := make(map[string]bool)
	termUsed := make(map[string]int)

	for data.itC.Next(&resultC) {
		if resultC.Sender == "twitchnotify" {
			if strings.Contains(resultC.Text, "just") {
				ret.Newsub += 1
			} else if strings.Contains(resultC.Text, "months") {
				ret.Resub += 1
			}
		} else {
			ret.Messages += 1

			for _, i := range strings.Split(resultC.Text, " ") {
				termUsed[i] += 1
			}

			_, present := uniqueChatter[resultC.Sender]
			if !present {
				uniqueChatter[resultC.Sender] = true
			}
		}
	}
	ret.UniqueChat = len(uniqueChatter)

	var result ViewerCount
	ret.Viewer = 0
	for data.itV.Next(&result) {
		ret.Viewer += result.Viewer
	}
	ret.Viewer /= data.lenV
	return
}
<|endoftext|>


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

// +build go1.3

package lxdclient

import (
	"fmt"
	"io/ioutil"
	"os"
	"strings"

	"github.com/juju/errors"
	"github.com/lxc/lxd/shared"
)

// TODO(ericsnow) We probably need to address some of the things that
// get handled in container/lxc/clonetemplate.go.

func (client *Client) addInstance(spec InstanceSpec) error {
	// TODO(ericsnow) Default to spec.ImageRemote.
	imageRemote := ""
	//remote := client.remote
	//remote := spec.ImageRemote
	if imageRemote == "" {
		imageRemote = client.remote
	}
	imageAlias := "ubuntu" // TODO(ericsnow) Do not hard-code.
	//imageAlias := spec.Image
	var profiles *[]string
	if len(spec.Profiles) > 0 {
		profiles = &spec.Profiles
	}

	// TODO(ericsnow) Copy the image first?

	resp, err := client.raw.Init(spec.Name, imageRemote, imageAlias, profiles, spec.Ephemeral)
	if err != nil {
		return errors.Trace(err)
	}

	// Init is an async operation, since the tar -xvf (or whatever) might
	// take a while; the result is an LXD operation id, which we can just
	// wait on until it is finished.
	if err := client.raw.WaitForSuccess(resp.Operation); err != nil {
		// TODO(ericsnow) Handle different failures (from the async
		// operation) differently?
		return errors.Trace(err)
	}

	if err := client.initInstanceConfig(spec); err != nil {
		if err := client.removeInstance(spec.Name); err != nil {
			logger.Errorf("could not remove container %q after configuring it failed", spec.Name)
		}
		return errors.Trace(err)
	}

	// TODO(ericsnow) Only do this if it's a state server...
	if err := client.exposeHostAPI(spec); err != nil {
		if err := client.removeInstance(spec.Name); err != nil {
			logger.Errorf("could not remove container %q after exposing the API sock failed", spec.Name)
		}
		return errors.Trace(err)
	}

	return nil
}

func (client *Client) initInstanceConfig(spec InstanceSpec) error {
	config := spec.config()
	for key, value := range config {
		err := client.raw.SetContainerConfig(spec.Name, key, value)
		if err != nil {
			return errors.Trace(err)
		}
	}
	return nil
}

func (client *Client) exposeHostAPI(spec InstanceSpec) error {
	// lxc config device add juju-container lxdsock disk \
	// source=/var/lib/lxd/unix.socket path=var/lib/lxd/unix.socket
	const apiDevName = "lxdsock"
	const devType = "disk"
	const filename = "/var/lib/lxd/unix.socket"
	props := []string{
		// TODO(ericsnow) hard-coded, unix-centric...
		"source=/var/lib/lxd/unix.socket",
		"path=var/lib/lxd/unix.socket",
	}
	resp, err := client.raw.ContainerDeviceAdd(spec.Name, apiDevName, devType, props)
	if err != nil {
		return errors.Trace(err)
	}

	if err := client.raw.WaitForSuccess(resp.Operation); err != nil {
		// TODO(ericsnow) Handle different failures (from the async
		// operation) differently?
		return errors.Trace(err)
	}

	return nil
}

type execFailure struct {
	cmd    string
	code   int
	stderr string
}

// Error returns the string representation of the error.
func (err execFailure) Error() string {
	return fmt.Sprintf("got non-zero code from %q: (%d) %s", err.cmd, err.code, err.stderr)
}

func (client *Client) exec(spec InstanceSpec, cmd []string) error {
	var env map[string]string

	stdin, stdout, stderr, err := ioFiles()
	if err != nil {
		return errors.Trace(err)
	}

	cmdStr := strings.Join(cmd, " ")
	fmt.Println("running", cmdStr)

	rc, err := client.raw.Exec(spec.Name, cmd, env, stdin, stdout, stderr)
	if err != nil {
		return errors.Trace(err)
	} else if rc != 0 {
		msg := "<reason unknown>"
		if _, err := stdout.Seek(0, 0); err == nil {
			data, err := ioutil.ReadAll(stdout)
			if err == nil {
				msg = string(data)
			}
		}
		err := &execFailure{
			cmd:    cmdStr,
			code:   rc,
			stderr: msg,
		}
		return errors.Trace(err)
	}

	return nil
}

// TODO(ericsnow) We *should* be able to use bytes.Buffer instead...
func ioFiles() (*os.File, *os.File, *os.File, error) {
	infile, err := ioutil.TempFile("", "")
	if err != nil {
		return nil, nil, nil, errors.Trace(err)
	}

	outfile, err := ioutil.TempFile("", "")
	if err != nil {
		return nil, nil, nil, errors.Trace(err)
	}

	// We combine stdout and stderr...
	return infile, outfile, outfile, nil
}

func (client *Client) chmod(spec InstanceSpec, filename string, mode os.FileMode) error {
	cmd := []string{
		"/bin/chmod",
		fmt.Sprintf("%s", mode),
		filename,
	}

	if err := client.exec(spec, cmd); err != nil {
		return errors.Trace(err)
	}
	return nil
}

func (client *Client) startInstance(spec InstanceSpec) error {
	timeout := -1
	force := false
	resp, err := client.raw.Action(spec.Name, shared.Start, timeout, force)
	if err != nil {
		return errors.Trace(err)
	}

	if err := client.raw.WaitForSuccess(resp.Operation); err != nil {
		// TODO(ericsnow) Handle different failures (from the async
		// operation) differently?
		return errors.Trace(err)
	}

	return nil
}

func (client *Client) fixSockfile(spec InstanceSpec) error {
	const filename = "/var/lib/lxd/unix.socket"

	//info, err := os.Stat(filename)
	//if err != nil {
	//	return nil, errors.Trace(err)
	//}
	//gid := info.Sys().(*syscall.Stat_t).Gid

	//cmd := []string{
	//	"/usr/sbin/groupadd",
	//	fmt.Sprintf("--gid=%d", gid),
	//	"lxd",
	//}
	//if err := client.exec(spec, cmd); err != nil {
	//	return errors.Trace(err)
	//}

	//cmd = []string{
	//	"/usr/sbin/usermod",
	//	"-a",
	//	"-G", "lxd",
	//	"root",
	//}
	//if err := client.exec(spec, cmd); err != nil {
	//	return errors.Trace(err)
	//}

	// TODO(ericsnow) Instead of modifying the socket file, add the
	// "lxd" group, ensure the GID matches the one on the host, and add
	// the root user to that group.

	// TODO(ericsnow) For now, ensure that your local unix.socket is 0666...
	//if err := client.chmod(spec, filename, 0666); err != nil {
	//	fmt.Println("---- ", err)
	//	//return errors.Trace(err)
	//}

	return nil
}

// AddInstance creates a new instance based on the spec's data and
// returns it. The instance will be created using the client.
func (client *Client) AddInstance(spec InstanceSpec) (*Instance, error) {
	if err := client.addInstance(spec); err != nil {
		return nil, errors.Trace(err)
	}

	if err := client.startInstance(spec); err != nil {
		if err := client.removeInstance(spec.Name); err != nil {
			logger.Errorf("could not remove container %q after starting it failed", spec.Name)
		}
		return nil, errors.Trace(err)
	}

	// TODO(ericsnow) This is a hack tied to exposeHostAPI().
	if err := client.fixSockfile(spec); err != nil {
		return nil, errors.Trace(err)
	}

	inst, err := client.Instance(spec.Name)
	if err != nil {
		return nil, errors.Trace(err)
	}
	inst.spec = &spec

	return inst, nil
}

// Instance gets the up-to-date info about the given instance
// and returns it.
func (client *Client) Instance(name string) (*Instance, error) {
	info, err := client.raw.ContainerStatus(name)
	if err != nil {
		return nil, errors.Trace(err)
	}

	inst := newInstance(info, nil)
	return inst, nil
}

// Instances sends a request to the API for a list of all instances
// (in the Client's namespace) for which the name starts with the
// provided prefix. The result is also limited to those instances with
// one of the specified statuses (if any).
func (client *Client) Instances(prefix string, statuses ...string) ([]Instance, error) {
	infos, err := client.raw.ListContainers()
	if err != nil {
		return nil, errors.Trace(err)
	}

	var insts []Instance
	for _, info := range infos {
		name := info.State.Name
		if prefix != "" && !strings.HasPrefix(name, prefix) {
			continue
		}
		if len(statuses) > 0 && !checkStatus(info, statuses) {
			continue
		}

		inst := newInstance(&info.State, nil)
		insts = append(insts, *inst)
	}
	return insts, nil
}

func checkStatus(info shared.ContainerInfo, statuses []string) bool {
	for _, status := range statuses {
		statusCode := allStatuses[status]
		if info.State.Status.StatusCode == statusCode {
			return true
		}
	}
	return false
}

// removeInstance sends a request to the API to remove the instance
// with the provided ID. The call blocks until the instance is removed
// (or the request fails).
func (client *Client) removeInstance(name string) error {
	info, err := client.raw.ContainerStatus(name)
	if err != nil {
		return errors.Trace(err)
	}

	//if info.Status.StatusCode != 0 && info.Status.StatusCode != shared.Stopped {
	if info.Status.StatusCode != shared.Stopped {
		timeout := -1
		force := true
		resp, err := client.raw.Action(name, shared.Stop, timeout, force)
		if err != nil {
			return errors.Trace(err)
		}

		if err := client.raw.WaitForSuccess(resp.Operation); err != nil {
			// TODO(ericsnow) Handle different failures (from the async
			// operation) differently?
			return errors.Trace(err)
		}
	}

	resp, err := client.raw.Delete(name)
	if err != nil {
		return errors.Trace(err)
	}

	if err := client.raw.WaitForSuccess(resp.Operation); err != nil {
		// TODO(ericsnow) Handle different failures (from the async
		// operation) differently?
		return errors.Trace(err)
	}

	return nil
}

// RemoveInstances sends a request to the API to terminate all
// instances (in the Client's namespace) that match one of the
// provided IDs. If a prefix is provided, only IDs that start with the
// prefix will be considered. The call blocks until all the instances
// are removed or the request fails.
func (client *Client) RemoveInstances(prefix string, names ...string) error {
	if len(names) == 0 {
		return nil
	}

	instances, err := client.Instances(prefix)
	if err != nil {
		return errors.Annotatef(err, "while removing instances %v", names)
	}

	var failed []string
	for _, name := range names {
		if !checkInstanceName(name, instances) {
			// We ignore unknown instance names.
			continue
		}

		if err := client.removeInstance(name); err != nil {
			failed = append(failed, name)
			logger.Errorf("while removing instance %q: %v", name, err)
		}
	}
	if len(failed) != 0 {
		return errors.Errorf("some instance removals failed: %v", failed)
	}
	return nil
}

func checkInstanceName(name string, instances []Instance) bool {
	for _, inst := range instances {
		if inst.Name == name {
			return true
		}
	}
	return false
}
<commit_msg>Only call exposeHostAPI if using the Local remote.<commit_after>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

// +build go1.3

package lxdclient

import (
	"fmt"
	"io/ioutil"
	"os"
	"strings"

	"github.com/juju/errors"
	"github.com/lxc/lxd/shared"
)

// TODO(ericsnow) We probably need to address some of the things that
// get handled in container/lxc/clonetemplate.go.

func (client *Client) addInstance(spec InstanceSpec) error {
	// TODO(ericsnow) Default to spec.ImageRemote.
	imageRemote := ""
	//remote := client.remote
	//remote := spec.ImageRemote
	if imageRemote == "" {
		imageRemote = client.remote
	}
	imageAlias := "ubuntu" // TODO(ericsnow) Do not hard-code.
	//imageAlias := spec.Image
	var profiles *[]string
	if len(spec.Profiles) > 0 {
		profiles = &spec.Profiles
	}

	// TODO(ericsnow) Copy the image first?

	resp, err := client.raw.Init(spec.Name, imageRemote, imageAlias, profiles, spec.Ephemeral)
	if err != nil {
		return errors.Trace(err)
	}

	// Init is an async operation, since the tar -xvf (or whatever) might
	// take a while; the result is an LXD operation id, which we can just
	// wait on until it is finished.
	if err := client.raw.WaitForSuccess(resp.Operation); err != nil {
		// TODO(ericsnow) Handle different failures (from the async
		// operation) differently?
		return errors.Trace(err)
	}

	if err := client.initInstanceConfig(spec); err != nil {
		if err := client.removeInstance(spec.Name); err != nil {
			logger.Errorf("could not remove container %q after configuring it failed", spec.Name)
		}
		return errors.Trace(err)
	}

	if client.remote == Local.ID() {
		// TODO(ericsnow) Only do this if it's a state server...
		if err := client.exposeHostAPI(spec); err != nil {
			if err := client.removeInstance(spec.Name); err != nil {
				logger.Errorf("could not remove container %q after exposing the API sock failed", spec.Name)
			}
			return errors.Trace(err)
		}
	}

	return nil
}

func (client *Client) initInstanceConfig(spec InstanceSpec) error {
	config := spec.config()
	for key, value := range config {
		err := client.raw.SetContainerConfig(spec.Name, key, value)
		if err != nil {
			return errors.Trace(err)
		}
	}
	return nil
}

func (client *Client) exposeHostAPI(spec InstanceSpec) error {
	// lxc config device add juju-container lxdsock disk \
	// source=/var/lib/lxd/unix.socket path=var/lib/lxd/unix.socket
	const apiDevName = "lxdsock"
	const devType = "disk"
	const filename = "/var/lib/lxd/unix.socket"
	props := []string{
		// TODO(ericsnow) hard-coded, unix-centric...
		"source=/var/lib/lxd/unix.socket",
		"path=var/lib/lxd/unix.socket",
	}
	resp, err := client.raw.ContainerDeviceAdd(spec.Name, apiDevName, devType, props)
	if err != nil {
		return errors.Trace(err)
	}

	if err := client.raw.WaitForSuccess(resp.Operation); err != nil {
		// TODO(ericsnow) Handle different failures (from the async
		// operation) differently?
		return errors.Trace(err)
	}

	return nil
}

type execFailure struct {
	cmd    string
	code   int
	stderr string
}

// Error returns the string representation of the error.
func (err execFailure) Error() string {
	return fmt.Sprintf("got non-zero code from %q: (%d) %s", err.cmd, err.code, err.stderr)
}

func (client *Client) exec(spec InstanceSpec, cmd []string) error {
	var env map[string]string

	stdin, stdout, stderr, err := ioFiles()
	if err != nil {
		return errors.Trace(err)
	}

	cmdStr := strings.Join(cmd, " ")
	fmt.Println("running", cmdStr)

	rc, err := client.raw.Exec(spec.Name, cmd, env, stdin, stdout, stderr)
	if err != nil {
		return errors.Trace(err)
	} else if rc != 0 {
		msg := "<reason unknown>"
		if _, err := stdout.Seek(0, 0); err == nil {
			data, err := ioutil.ReadAll(stdout)
			if err == nil {
				msg = string(data)
			}
		}
		err := &execFailure{
			cmd:    cmdStr,
			code:   rc,
			stderr: msg,
		}
		return errors.Trace(err)
	}

	return nil
}

// TODO(ericsnow) We *should* be able to use bytes.Buffer instead...
func ioFiles() (*os.File, *os.File, *os.File, error) {
	infile, err := ioutil.TempFile("", "")
	if err != nil {
		return nil, nil, nil, errors.Trace(err)
	}

	outfile, err := ioutil.TempFile("", "")
	if err != nil {
		return nil, nil, nil, errors.Trace(err)
	}

	// We combine stdout and stderr...
	return infile, outfile, outfile, nil
}

func (client *Client) chmod(spec InstanceSpec, filename string, mode os.FileMode) error {
	cmd := []string{
		"/bin/chmod",
		fmt.Sprintf("%s", mode),
		filename,
	}

	if err := client.exec(spec, cmd); err != nil {
		return errors.Trace(err)
	}
	return nil
}

func (client *Client) startInstance(spec InstanceSpec) error {
	timeout := -1
	force := false
	resp, err := client.raw.Action(spec.Name, shared.Start, timeout, force)
	if err != nil {
		return errors.Trace(err)
	}

	if err := client.raw.WaitForSuccess(resp.Operation); err != nil {
		// TODO(ericsnow) Handle different failures (from the async
		// operation) differently?
		return errors.Trace(err)
	}

	return nil
}

func (client *Client) fixSockfile(spec InstanceSpec) error {
	const filename = "/var/lib/lxd/unix.socket"

	//info, err := os.Stat(filename)
	//if err != nil {
	//	return nil, errors.Trace(err)
	//}
	//gid := info.Sys().(*syscall.Stat_t).Gid

	//cmd := []string{
	//	"/usr/sbin/groupadd",
	//	fmt.Sprintf("--gid=%d", gid),
	//	"lxd",
	//}
	//if err := client.exec(spec, cmd); err != nil {
	//	return errors.Trace(err)
	//}

	//cmd = []string{
	//	"/usr/sbin/usermod",
	//	"-a",
	//	"-G", "lxd",
	//	"root",
	//}
	//if err := client.exec(spec, cmd); err != nil {
	//	return errors.Trace(err)
	//}

	// TODO(ericsnow) Instead of modifying the socket file, add the
	// "lxd" group, ensure the GID matches the one on the host, and add
	// the root user to that group.

	// TODO(ericsnow) For now, ensure that your local unix.socket is 0666...
	//if err := client.chmod(spec, filename, 0666); err != nil {
	//	fmt.Println("---- ", err)
	//	//return errors.Trace(err)
	//}

	return nil
}

// AddInstance creates a new instance based on the spec's data and
// returns it. The instance will be created using the client.
func (client *Client) AddInstance(spec InstanceSpec) (*Instance, error) {
	if err := client.addInstance(spec); err != nil {
		return nil, errors.Trace(err)
	}

	if err := client.startInstance(spec); err != nil {
		if err := client.removeInstance(spec.Name); err != nil {
			logger.Errorf("could not remove container %q after starting it failed", spec.Name)
		}
		return nil, errors.Trace(err)
	}

	// TODO(ericsnow) This is a hack tied to exposeHostAPI().
	if err := client.fixSockfile(spec); err != nil {
		return nil, errors.Trace(err)
	}

	inst, err := client.Instance(spec.Name)
	if err != nil {
		return nil, errors.Trace(err)
	}
	inst.spec = &spec

	return inst, nil
}

// Instance gets the up-to-date info about the given instance
// and returns it.
func (client *Client) Instance(name string) (*Instance, error) {
	info, err := client.raw.ContainerStatus(name)
	if err != nil {
		return nil, errors.Trace(err)
	}

	inst := newInstance(info, nil)
	return inst, nil
}

// Instances sends a request to the API for a list of all instances
// (in the Client's namespace) for which the name starts with the
// provided prefix. The result is also limited to those instances with
// one of the specified statuses (if any).
func (client *Client) Instances(prefix string, statuses ...string) ([]Instance, error) {
	infos, err := client.raw.ListContainers()
	if err != nil {
		return nil, errors.Trace(err)
	}

	var insts []Instance
	for _, info := range infos {
		name := info.State.Name
		if prefix != "" && !strings.HasPrefix(name, prefix) {
			continue
		}
		if len(statuses) > 0 && !checkStatus(info, statuses) {
			continue
		}

		inst := newInstance(&info.State, nil)
		insts = append(insts, *inst)
	}
	return insts, nil
}

func checkStatus(info shared.ContainerInfo, statuses []string) bool {
	for _, status := range statuses {
		statusCode := allStatuses[status]
		if info.State.Status.StatusCode == statusCode {
			return true
		}
	}
	return false
}

// removeInstance sends a request to the API to remove the instance
// with the provided ID. The call blocks until the instance is removed
// (or the request fails).
func (client *Client) removeInstance(name string) error {
	info, err := client.raw.ContainerStatus(name)
	if err != nil {
		return errors.Trace(err)
	}

	//if info.Status.StatusCode != 0 && info.Status.StatusCode != shared.Stopped {
	if info.Status.StatusCode != shared.Stopped {
		timeout := -1
		force := true
		resp, err := client.raw.Action(name, shared.Stop, timeout, force)
		if err != nil {
			return errors.Trace(err)
		}

		if err := client.raw.WaitForSuccess(resp.Operation); err != nil {
			// TODO(ericsnow) Handle different failures (from the async
			// operation) differently?
			return errors.Trace(err)
		}
	}

	resp, err := client.raw.Delete(name)
	if err != nil {
		return errors.Trace(err)
	}

	if err := client.raw.WaitForSuccess(resp.Operation); err != nil {
		// TODO(ericsnow) Handle different failures (from the async
		// operation) differently?
		return errors.Trace(err)
	}

	return nil
}

// RemoveInstances sends a request to the API to terminate all
// instances (in the Client's namespace) that match one of the
// provided IDs. If a prefix is provided, only IDs that start with the
// prefix will be considered. The call blocks until all the instances
// are removed or the request fails.
func (client *Client) RemoveInstances(prefix string, names ...string) error {
	if len(names) == 0 {
		return nil
	}

	instances, err := client.Instances(prefix)
	if err != nil {
		return errors.Annotatef(err, "while removing instances %v", names)
	}

	var failed []string
	for _, name := range names {
		if !checkInstanceName(name, instances) {
			// We ignore unknown instance names.
			continue
		}

		if err := client.removeInstance(name); err != nil {
			failed = append(failed, name)
			logger.Errorf("while removing instance %q: %v", name, err)
		}
	}
	if len(failed) != 0 {
		return errors.Errorf("some instance removals failed: %v", failed)
	}
	return nil
}

func checkInstanceName(name string, instances []Instance) bool {
	for _, inst := range instances {
		if inst.Name == name {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>package kcp

import (
	"sync"

	"github.com/v2ray/v2ray-core/common/alloc"
)

type SendingWindow struct {
	start uint32
	cap   uint32
	len   uint32
	last  uint32

	data []*DataSegment
	prev []uint32
	next []uint32

	totalInFlightSize uint32
	writer            SegmentWriter
	onPacketLoss      func(uint32)
}

func NewSendingWindow(size uint32, writer SegmentWriter, onPacketLoss func(uint32)) *SendingWindow {
	window := &SendingWindow{
		start:        0,
		cap:          size,
		len:          0,
		last:         0,
		data:         make([]*DataSegment, size),
		prev:         make([]uint32, size),
		next:         make([]uint32, size),
		writer:       writer,
		onPacketLoss: onPacketLoss,
	}
	return window
}

func (this *SendingWindow) Len() int {
	return int(this.len)
}

func (this *SendingWindow) Size() uint32 {
	return this.cap
}

func (this *SendingWindow) IsFull() bool {
	return this.len == this.cap
}

func (this *SendingWindow) Push(seg *DataSegment) {
	pos := (this.start + this.len) % this.cap
	this.data[pos] = seg
	if this.len > 0 {
		this.next[this.last] = pos
		this.prev[pos] = this.last
	}
	this.last = pos
	this.len++
}

func (this *SendingWindow) First() *DataSegment {
	return this.data[this.start]
}

func (this *SendingWindow) Clear(una uint32) {
	for this.Len() > 0 && this.data[this.start].Number < una {
		this.Remove(0)
	}
}

func (this *SendingWindow) Remove(idx uint32) {
	if this.len == 0 {
		return
	}

	pos := (this.start + idx) % this.cap
	seg := this.data[pos]
	if seg == nil {
		return
	}
	this.totalInFlightSize--
	seg.Release()
	this.data[pos] = nil
	if pos == this.start && pos == this.last {
		this.len = 0
		this.start = 0
		this.last = 0
	} else if pos == this.start {
		delta := this.next[pos] - this.start
		if this.next[pos] < this.start {
			delta = this.next[pos] + this.cap - this.start
		}
		this.start = this.next[pos]
		this.len -= delta
	} else if pos == this.last {
		this.last = this.prev[pos]
	} else {
		this.next[this.prev[pos]] = this.next[pos]
		this.prev[this.next[pos]] = this.prev[pos]
	}
}

func (this *SendingWindow) HandleFastAck(number uint32) {
	if this.len == 0 {
		return
	}

	for i := this.start; ; i = this.next[i] {
		seg := this.data[i]
		if number-seg.Number > 0x7FFFFFFF {
			break
		}
		if number != seg.Number {
			seg.ackSkipped++
		}
		if i == this.last {
			break
		}
	}
}

func (this *SendingWindow) Flush(current uint32, resend uint32, rto uint32, maxInFlightSize uint32) {
	if this.Len() == 0 {
		return
	}

	var lost uint32
	var inFlightSize uint32

	for i := this.start; ; i = this.next[i] {
		segment := this.data[i]
		needsend := false
		if segment.transmit == 0 {
			needsend = true
			segment.transmit++
			segment.timeout = current + rto
			this.totalInFlightSize++
		} else if current-segment.timeout < 0x7FFFFFFF {
			needsend = true
			segment.transmit++
			segment.timeout = current + rto
			lost++
		} else if segment.ackSkipped >= resend {
			needsend = true
			segment.transmit++
			segment.ackSkipped = 0
			segment.timeout = current + rto
		}

		if needsend {
			segment.Timestamp = current
			this.writer.Write(segment)
			inFlightSize++
			if inFlightSize >= maxInFlightSize {
				break
			}
		}
		if i == this.last {
			break
		}
	}

	if inFlightSize > 0 && this.totalInFlightSize != 0 {
		rate := lost * 100 / this.totalInFlightSize
		this.onPacketLoss(rate)
	}
}

type SendingQueue struct {
	start uint32
	cap   uint32
	len   uint32
	list  []*DataSegment
}

func NewSendingQueue(size uint32) *SendingQueue {
	return &SendingQueue{
		start: 0,
		cap:   size,
		list:  make([]*DataSegment, size),
		len:   0,
	}
}

func (this *SendingQueue) IsFull() bool {
	return this.len == this.cap
}

func (this *SendingQueue) IsEmpty() bool {
	return this.len == 0
}

func (this *SendingQueue) Pop() *DataSegment {
	if this.IsEmpty() {
		return nil
	}
	seg := this.list[this.start]
	this.list[this.start] = nil
	this.len--
	this.start++
	if this.start == this.cap {
		this.start = 0
	}
	return seg
}

func (this *SendingQueue) Push(seg *DataSegment) {
	if this.IsFull() {
		return
	}
	this.list[(this.start+this.len)%this.cap] = seg
	this.len++
}

func (this *SendingQueue) Clear() {
	for i := uint32(0); i < this.len; i++ {
		this.list[(i+this.start)%this.cap].Release()
		this.list[(i+this.start)%this.cap] = nil
	}
	this.start = 0
	this.len = 0
}

func (this *SendingQueue) Len() uint32 {
	return this.len
}

type SendingWorker struct {
	sync.Mutex
	conn                *Connection
	window              *SendingWindow
	queue               *SendingQueue
	firstUnacknowledged uint32
	nextNumber          uint32
	remoteNextNumber    uint32
	controlWindow       uint32
	fastResend          uint32
	updated             bool
}

func NewSendingWorker(kcp *Connection) *SendingWorker {
	worker := &SendingWorker{
		conn:             kcp,
		queue:            NewSendingQueue(effectiveConfig.GetSendingQueueSize()),
		fastResend:       2,
		remoteNextNumber: 32,
		controlWindow:    effectiveConfig.GetSendingInFlightSize(),
	}
	worker.window = NewSendingWindow(effectiveConfig.GetSendingWindowSize(), worker, worker.OnPacketLoss)
	return worker
}

func (this *SendingWorker) ProcessReceivingNext(nextNumber uint32) {
	this.Lock()
	defer this.Unlock()

	this.window.Clear(nextNumber)
	this.FindFirstUnacknowledged()
}

// @Private
func (this *SendingWorker) FindFirstUnacknowledged() {
	prevUna := this.firstUnacknowledged
	if this.window.Len() > 0 {
		this.firstUnacknowledged = this.window.First().Number
	} else {
		this.firstUnacknowledged = this.nextNumber
	}
	if this.firstUnacknowledged != prevUna {
		this.updated = true
	}
}

func (this *SendingWorker) ProcessAck(number uint32) {
	if number-this.firstUnacknowledged > this.window.Size() {
		return
	}

	this.Lock()
	defer this.Unlock()
	this.window.Remove(number - this.firstUnacknowledged)
	this.FindFirstUnacknowledged()
}

func (this *SendingWorker) ProcessSegment(current uint32, seg *AckSegment) {
	if this.remoteNextNumber < seg.ReceivingWindow {
		this.remoteNextNumber = seg.ReceivingWindow
	}
	this.ProcessReceivingNext(seg.ReceivingNext)
	var maxack uint32
	for i := 0; i < int(seg.Count); i++ {
		timestamp := seg.TimestampList[i]
		number := seg.NumberList[i]
		if current-timestamp < 10000 {
			this.conn.roundTrip.Update(current - timestamp)
		}
		this.ProcessAck(number)
		if maxack < number {
			maxack = number
		}
	}
	this.Lock()
	this.window.HandleFastAck(maxack)
	this.Unlock()
}

func (this *SendingWorker) Push(b []byte) int {
	nBytes := 0
	this.Lock()
	defer this.Unlock()
	for len(b) > 0 && !this.queue.IsFull() {
		var size int
		if len(b) > int(this.conn.mss) {
			size = int(this.conn.mss)
		} else {
			size = len(b)
		}
		seg := NewDataSegment()
		seg.Data = alloc.NewSmallBuffer().Clear().Append(b[:size])
		this.queue.Push(seg)
		b = b[size:]
		nBytes += size
	}
	return nBytes
}

func (this *SendingWorker) Write(seg Segment) {
	dataSeg := seg.(*DataSegment)

	dataSeg.Conv = this.conn.conv
	dataSeg.SendingNext = this.firstUnacknowledged
	dataSeg.Opt = 0
	if this.conn.State() == StateReadyToClose {
		dataSeg.Opt = SegmentOptionClose
	}

	this.conn.output.Write(dataSeg)
	this.updated = false
}

func (this *SendingWorker) PingNecessary() bool {
	return this.updated
}

func (this *SendingWorker) OnPacketLoss(lossRate uint32) {
	if !effectiveConfig.Congestion || this.conn.roundTrip.Timeout() == 0 {
		return
	}

	if lossRate >= 15 {
		this.controlWindow = 3 * this.controlWindow / 4
	} else if lossRate <= 5 {
		this.controlWindow += this.controlWindow / 4
	}
	if this.controlWindow < 16 {
		this.controlWindow = 16
	}
	if this.controlWindow > 2*effectiveConfig.GetSendingInFlightSize() {
		this.controlWindow = 2 * effectiveConfig.GetSendingInFlightSize()
	}
}

func (this *SendingWorker) Flush(current uint32) {
	this.Lock()
	defer this.Unlock()

	cwnd := this.firstUnacknowledged + effectiveConfig.GetSendingInFlightSize()
	if cwnd > this.remoteNextNumber {
		cwnd = this.remoteNextNumber
	}
	if effectiveConfig.Congestion && cwnd > this.firstUnacknowledged+this.controlWindow {
		cwnd = this.firstUnacknowledged + this.controlWindow
	}

	for !this.queue.IsEmpty() && !this.window.IsFull() {
		seg := this.queue.Pop()
		seg.Number = this.nextNumber
		seg.timeout = current
		seg.ackSkipped = 0
		seg.transmit = 0
		this.window.Push(seg)
		this.nextNumber++
	}

	this.window.Flush(current, this.conn.fastresend, this.conn.roundTrip.Timeout(), cwnd)
}

func (this *SendingWorker) CloseWrite() {
	this.Lock()
	defer this.Unlock()

	this.window.Clear(0xFFFFFFFF)
	this.queue.Clear()
}
<commit_msg>refine locks in sending worker<commit_after>package kcp

import (
	"sync"

	"github.com/v2ray/v2ray-core/common/alloc"
)

type SendingWindow struct {
	start uint32
	cap   uint32
	len   uint32
	last  uint32

	data []*DataSegment
	prev []uint32
	next []uint32

	totalInFlightSize uint32
	writer            SegmentWriter
	onPacketLoss      func(uint32)
}

func NewSendingWindow(size uint32, writer SegmentWriter, onPacketLoss func(uint32)) *SendingWindow {
	window := &SendingWindow{
		start:        0,
		cap:          size,
		len:          0,
		last:         0,
		data:         make([]*DataSegment, size),
		prev:         make([]uint32, size),
		next:         make([]uint32, size),
		writer:       writer,
		onPacketLoss: onPacketLoss,
	}
	return window
}

func (this *SendingWindow) Len() int {
	return int(this.len)
}

func (this *SendingWindow) Size() uint32 {
	return this.cap
}

func (this *SendingWindow) IsFull() bool {
	return this.len == this.cap
}

func (this *SendingWindow) Push(seg *DataSegment) {
	pos := (this.start + this.len) % this.cap
	this.data[pos] = seg
	if this.len > 0 {
		this.next[this.last] = pos
		this.prev[pos] = this.last
	}
	this.last = pos
	this.len++
}

func (this *SendingWindow) First() *DataSegment {
	return this.data[this.start]
}

func (this *SendingWindow) Clear(una uint32) {
	for this.Len() > 0 && this.data[this.start].Number < una {
		this.Remove(0)
	}
}

func (this *SendingWindow) Remove(idx uint32) {
	if this.len == 0 {
		return
	}

	pos := (this.start + idx) % this.cap
	seg := this.data[pos]
	if seg == nil {
		return
	}
	this.totalInFlightSize--
	seg.Release()
	this.data[pos] = nil
	if pos == this.start && pos == this.last {
		this.len = 0
		this.start = 0
		this.last = 0
	} else if pos == this.start {
		delta := this.next[pos] - this.start
		if this.next[pos] < this.start {
			delta = this.next[pos] + this.cap - this.start
		}
		this.start = this.next[pos]
		this.len -= delta
	} else if pos == this.last {
		this.last = this.prev[pos]
	} else {
		this.next[this.prev[pos]] = this.next[pos]
		this.prev[this.next[pos]] = this.prev[pos]
	}
}

func (this *SendingWindow) HandleFastAck(number uint32) {
	if this.len == 0 {
		return
	}

	for i := this.start; ; i = this.next[i] {
		seg := this.data[i]
		if number-seg.Number > 0x7FFFFFFF {
			break
		}
		if number != seg.Number {
			seg.ackSkipped++
		}
		if i == this.last {
			break
		}
	}
}

func (this *SendingWindow) Flush(current uint32, resend uint32, rto uint32, maxInFlightSize uint32) {
	if this.Len() == 0 {
		return
	}

	var lost uint32
	var inFlightSize uint32

	for i := this.start; ; i = this.next[i] {
		segment := this.data[i]
		needsend := false
		if segment.transmit == 0 {
			needsend = true
			segment.transmit++
			segment.timeout = current + rto
			this.totalInFlightSize++
		} else if current-segment.timeout < 0x7FFFFFFF {
			needsend = true
			segment.transmit++
			segment.timeout = current + rto
			lost++
		} else if segment.ackSkipped >= resend {
			needsend = true
			segment.transmit++
			segment.ackSkipped = 0
			segment.timeout = current + rto
		}

		if needsend {
			segment.Timestamp = current
			this.writer.Write(segment)
			inFlightSize++
			if inFlightSize >= maxInFlightSize {
				break
			}
		}
		if i == this.last {
			break
		}
	}

	if inFlightSize > 0 && this.totalInFlightSize != 0 {
		rate := lost * 100 / this.totalInFlightSize
		this.onPacketLoss(rate)
	}
}

type SendingQueue struct {
	start uint32
	cap   uint32
	len   uint32
	list  []*DataSegment
}

func NewSendingQueue(size uint32) *SendingQueue {
	return &SendingQueue{
		start: 0,
		cap:   size,
		list:  make([]*DataSegment, size),
		len:   0,
	}
}

func (this *SendingQueue) IsFull() bool {
	return this.len == this.cap
}

func (this *SendingQueue) IsEmpty() bool {
	return this.len == 0
}

func (this *SendingQueue) Pop() *DataSegment {
	if this.IsEmpty() {
		return nil
	}
	seg := this.list[this.start]
	this.list[this.start] = nil
	this.len--
	this.start++
	if this.start == this.cap {
		this.start = 0
	}
	return seg
}

func (this *SendingQueue) Push(seg *DataSegment) {
	if this.IsFull() {
		return
	}
	this.list[(this.start+this.len)%this.cap] = seg
	this.len++
}

func (this *SendingQueue) Clear() {
	for i := uint32(0); i < this.len; i++ {
		this.list[(i+this.start)%this.cap].Release()
		this.list[(i+this.start)%this.cap] = nil
	}
	this.start = 0
	this.len = 0
}

func (this *SendingQueue) Len() uint32 {
	return this.len
}

type SendingWorker struct {
	sync.Mutex
	conn                *Connection
	window              *SendingWindow
	queue               *SendingQueue
	firstUnacknowledged uint32
	nextNumber          uint32
	remoteNextNumber    uint32
	controlWindow       uint32
	fastResend          uint32
	updated             bool
}

func NewSendingWorker(kcp *Connection) *SendingWorker {
	worker := &SendingWorker{
		conn:             kcp,
		queue:            NewSendingQueue(effectiveConfig.GetSendingQueueSize()),
		fastResend:       2,
		remoteNextNumber: 32,
		controlWindow:    effectiveConfig.GetSendingInFlightSize(),
	}
	worker.window = NewSendingWindow(effectiveConfig.GetSendingWindowSize(), worker, worker.OnPacketLoss)
	return worker
}

func (this *SendingWorker) ProcessReceivingNext(nextNumber uint32) {
	this.Lock()
	defer this.Unlock()

	this.ProcessReceivingNextWithoutLock(nextNumber)
}

func (this *SendingWorker) ProcessReceivingNextWithoutLock(nextNumber uint32) {
	this.window.Clear(nextNumber)
	this.FindFirstUnacknowledged()
}

// @Private
func (this *SendingWorker) FindFirstUnacknowledged() {
	prevUna := this.firstUnacknowledged
	if this.window.Len() > 0 {
		this.firstUnacknowledged = this.window.First().Number
	} else {
		this.firstUnacknowledged = this.nextNumber
	}
	if this.firstUnacknowledged != prevUna {
		this.updated = true
	}
}

// @Private
func (this *SendingWorker) ProcessAck(number uint32) {
	if number-this.firstUnacknowledged > this.window.Size() {
		return
	}

	this.window.Remove(number - this.firstUnacknowledged)
	this.FindFirstUnacknowledged()
}

func (this *SendingWorker) ProcessSegment(current uint32, seg *AckSegment) {
	this.Lock()
	defer this.Unlock()

	if this.remoteNextNumber < seg.ReceivingWindow {
		this.remoteNextNumber = seg.ReceivingWindow
	}
	this.ProcessReceivingNextWithoutLock(seg.ReceivingNext)
	var maxack uint32
	for i := 0; i < int(seg.Count); i++ {
		timestamp := seg.TimestampList[i]
		number := seg.NumberList[i]
		if current-timestamp < 10000 {
			this.conn.roundTrip.Update(current - timestamp)
		}
		this.ProcessAck(number)
		if maxack < number {
			maxack = number
		}
	}

	this.window.HandleFastAck(maxack)
}

func (this *SendingWorker) Push(b []byte) int {
	nBytes := 0
	this.Lock()
	defer this.Unlock()
	for len(b) > 0 && !this.queue.IsFull() {
		var size int
		if len(b) > int(this.conn.mss) {
			size = int(this.conn.mss)
		} else {
			size = len(b)
		}
		seg := NewDataSegment()
		seg.Data = alloc.NewSmallBuffer().Clear().Append(b[:size])
		this.queue.Push(seg)
		b = b[size:]
		nBytes += size
	}
	return nBytes
}

// @Private
func (this *SendingWorker) Write(seg Segment) {
	dataSeg := seg.(*DataSegment)

	dataSeg.Conv = this.conn.conv
	dataSeg.SendingNext = this.firstUnacknowledged
	dataSeg.Opt = 0
	if this.conn.State() == StateReadyToClose {
		dataSeg.Opt = SegmentOptionClose
	}

	this.conn.output.Write(dataSeg)
	this.updated = false
}

func (this *SendingWorker) PingNecessary() bool {
	return this.updated
}

func (this *SendingWorker) OnPacketLoss(lossRate uint32) {
	if !effectiveConfig.Congestion || this.conn.roundTrip.Timeout() == 0 {
		return
	}

	if lossRate >= 15 {
		this.controlWindow = 3 * this.controlWindow / 4
	} else if lossRate <= 5 {
		this.controlWindow += this.controlWindow / 4
	}
	if this.controlWindow < 16 {
		this.controlWindow = 16
	}
	if this.controlWindow > 2*effectiveConfig.GetSendingInFlightSize() {
		this.controlWindow = 2 * effectiveConfig.GetSendingInFlightSize()
	}
}

func (this *SendingWorker) Flush(current uint32) {
	this.Lock()
	defer this.Unlock()

	cwnd := this.firstUnacknowledged + effectiveConfig.GetSendingInFlightSize()
	if cwnd > this.remoteNextNumber {
		cwnd = this.remoteNextNumber
	}
	if effectiveConfig.Congestion && cwnd > this.firstUnacknowledged+this.controlWindow {
		cwnd = this.firstUnacknowledged + this.controlWindow
	}

	for !this.queue.IsEmpty() && !this.window.IsFull() {
		seg := this.queue.Pop()
		seg.Number = this.nextNumber
		seg.timeout = current
		seg.ackSkipped = 0
		seg.transmit = 0
		this.window.Push(seg)
		this.nextNumber++
	}

	this.window.Flush(current, this.conn.fastresend, this.conn.roundTrip.Timeout(), cwnd)
}

func (this *SendingWorker) CloseWrite() {
	this.Lock()
	defer this.Unlock()

	this.window.Clear(0xFFFFFFFF)
	this.queue.Clear()
}
<|endoftext|>


<commit_before>package multistep

import "sync"

// BasicRunner is a Runner that just runs the given slice of steps.
type BasicRunner struct {
	// Steps is a slice of steps to run. Once set, this should _not_ be
	// modified.
	Steps []Step

	cancelCond *sync.Cond
	cancelChs  []chan<- bool
	running    bool
	l          sync.Mutex
}

func (b *BasicRunner) Run(state map[string]interface{}) {
	// Make sure we only run one at a time
	b.l.Lock()
	if b.running {
		panic("already running")
	}
	b.cancelChs = nil
	b.cancelCond = sync.NewCond(&sync.Mutex{})
	b.running = true
	b.l.Unlock()

	// cancelReady is used to signal that the cancellation goroutine
	// started and is waiting. The cancelEnded channel is used to
	// signal the goroutine actually ended.
	cancelReady := make(chan bool, 1)
	cancelEnded := make(chan bool)
	go func() {
		b.cancelCond.L.Lock()
		cancelReady <- true
		b.cancelCond.Wait()
		b.cancelCond.L.Unlock()

		if b.cancelChs != nil {
			state[StateCancelled] = true
		}

		cancelEnded <- true
	}()

	// Create the channel that we'll say we're done on in the case of
	// interrupts here. We do this here so that this deferred statement
	// runs last, so all the Cleanup methods are able to run.
	defer func() {
		b.l.Lock()
		defer b.l.Unlock()

		if b.cancelChs != nil {
			for _, doneCh := range b.cancelChs {
				doneCh <- true
			}
		}

		// Make sure the cancellation goroutine cleans up properly. This
		// is a bit complicated. Basically, we first wait until the goroutine
		// waiting for cancellation is actually waiting. Then we broadcast
		// to it so it can unlock. Then we wait for it to tell us it finished.
		<-cancelReady
		b.cancelCond.L.Lock()
		b.cancelCond.Broadcast()
		b.cancelCond.L.Unlock()
		<-cancelEnded

		b.running = false
	}()

	for _, step := range b.Steps {
		// We also check for cancellation here since we can't be sure
		// the goroutine that is running to set it actually ran.
		if b.cancelChs != nil {
			state[StateCancelled] = true
			break
		}

		action := step.Run(state)
		defer step.Cleanup(state)

		if _, ok := state[StateCancelled]; ok {
			break
		}

		if action == ActionHalt {
			state[StateHalted] = true
			break
		}
	}
}

func (b *BasicRunner) Cancel() {
	b.l.Lock()

	if !b.running {
		b.l.Unlock()
		return
	}

	if b.cancelChs == nil {
		b.cancelChs = make([]chan<- bool, 0, 5)
	}

	done := make(chan bool)
	b.cancelChs = append(b.cancelChs, done)
	b.cancelCond.Broadcast()
	b.l.Unlock()

	<-done
}
<commit_msg>Send the done notifications after finishing cancel routine<commit_after>package multistep

import "sync"

// BasicRunner is a Runner that just runs the given slice of steps.
type BasicRunner struct {
	// Steps is a slice of steps to run. Once set, this should _not_ be
	// modified.
	Steps []Step

	cancelCond *sync.Cond
	cancelChs  []chan<- bool
	running    bool
	l          sync.Mutex
}

func (b *BasicRunner) Run(state map[string]interface{}) {
	// Make sure we only run one at a time
	b.l.Lock()
	if b.running {
		panic("already running")
	}
	b.cancelChs = nil
	b.cancelCond = sync.NewCond(&sync.Mutex{})
	b.running = true
	b.l.Unlock()

	// cancelReady is used to signal that the cancellation goroutine
	// started and is waiting. The cancelEnded channel is used to
	// signal the goroutine actually ended.
	cancelReady := make(chan bool, 1)
	cancelEnded := make(chan bool)
	go func() {
		b.cancelCond.L.Lock()
		cancelReady <- true
		b.cancelCond.Wait()
		b.cancelCond.L.Unlock()

		if b.cancelChs != nil {
			state[StateCancelled] = true
		}

		cancelEnded <- true
	}()

	// Create the channel that we'll say we're done on in the case of
	// interrupts here. We do this here so that this deferred statement
	// runs last, so all the Cleanup methods are able to run.
	defer func() {
		b.l.Lock()
		defer b.l.Unlock()

		// Make sure the cancellation goroutine cleans up properly. This
		// is a bit complicated. Basically, we first wait until the goroutine
		// waiting for cancellation is actually waiting. Then we broadcast
		// to it so it can unlock. Then we wait for it to tell us it finished.
		<-cancelReady
		b.cancelCond.L.Lock()
		b.cancelCond.Broadcast()
		b.cancelCond.L.Unlock()
		<-cancelEnded

		if b.cancelChs != nil {
			for _, doneCh := range b.cancelChs {
				doneCh <- true
			}
		}

		b.running = false
	}()

	for _, step := range b.Steps {
		// We also check for cancellation here since we can't be sure
		// the goroutine that is running to set it actually ran.
		if b.cancelChs != nil {
			state[StateCancelled] = true
			break
		}

		action := step.Run(state)
		defer step.Cleanup(state)

		if _, ok := state[StateCancelled]; ok {
			break
		}

		if action == ActionHalt {
			state[StateHalted] = true
			break
		}
	}
}

func (b *BasicRunner) Cancel() {
	b.l.Lock()

	if !b.running {
		b.l.Unlock()
		return
	}

	if b.cancelChs == nil {
		b.cancelChs = make([]chan<- bool, 0, 5)
	}

	done := make(chan bool)
	b.cancelChs = append(b.cancelChs, done)
	b.cancelCond.Broadcast()
	b.l.Unlock()

	<-done
}
<|endoftext|>


<commit_before>package ari

import (
	"strings"

	"github.com/pkg/errors"
	uuid "github.com/satori/go.uuid"
)

// Channel represents a communication path interacting with an Asterisk server.
type Channel interface {
	// Get returns a handle to a channel for further interaction
	Get(id string) *ChannelHandle

	// List lists the channels in asterisk
	List() ([]*ChannelHandle, error)

	// Create creates a new channel, returning a handle to it or an
	// error, if the creation failed
	Create(OriginateRequest) (*ChannelHandle, error)

	// Data returns the channel data for a given channel
	Data(id string) (ChannelData, error)

	// Continue tells Asterisk to return a channel to the dialplan
	Continue(id, context, extension, priority string) error

	// Busy hangs up the channel with the "busy" cause code
	Busy(id string) error

	// Congestion hangs up the channel with the "congestion" cause code
	Congestion(id string) error

	// Answer answers the channel
	Answer(id string) error

	// Hangup hangs up the given channel
	Hangup(id string, reason string) error

	// Ring indicates ringing to the channel
	Ring(id string) error

	// StopRing stops ringing on the channel
	StopRing(id string) error

	// SendDTMF sends DTMF to the channel
	SendDTMF(id string, dtmf string) error

	// Hold puts the channel on hold
	Hold(id string) error

	// StopHold retrieves the channel from hold
	StopHold(id string) error

	// Mute mutes a channel in the given direction (in,out,both)
	Mute(id string, dir string) error

	// Unmute unmutes a channel in the given direction (in,out,both)
	Unmute(id string, dir string) error

	// MOH plays music on hold
	MOH(id string, moh string) error

	// StopMOH stops music on hold
	StopMOH(id string) error

	// Silence plays silence to the channel
	Silence(id string) error

	// StopSilence stops the silence on the channel
	StopSilence(id string) error

	// Play plays the media URI to the channel
	Play(id string, playbackID string, mediaURI string) (*PlaybackHandle, error)

	// Subscribe subscribes on the channel events
	Subscribe(id string, n ...string) Subscription
}

// ChannelData is the data for a specific channel
type ChannelData struct {
	ID           string      `json:"id"`    // Unique id for this channel (same as for AMI)
	Name         string      `json:"name"`  // Name of this channel (tech/name-id format)
	State        string      `json:"state"` // State of the channel
	Accountcode  string      `json:"accountcode"`
	Caller       CallerID    `json:"caller"`    // CallerId of the calling endpoint
	Connected    CallerID    `json:"connected"` // CallerId of the connected line
	Creationtime DateTime    `json:"creationtime"`
	Dialplan     DialplanCEP `json:"dialplan"` // Current location in the dialplan
}

// NewChannelHandle returns a handle to the given ARI channel
func NewChannelHandle(id string, c Channel) *ChannelHandle {
	return &ChannelHandle{
		id: id,
		c:  c,
	}
}

// ChannelHandle provides a wrapper to a Channel interface for
// operations on a particular channel ID
type ChannelHandle struct {
	id string  // id of the channel on which we are operating
	c  Channel // the Channel interface with which we are operating
}

// ID returns the identifier for the channel handle
func (ch *ChannelHandle) ID() string {
	return ch.id
}

// Data returns the channel's data
func (ch *ChannelHandle) Data() (ChannelData, error) {
	return ch.c.Data(ch.id)
}

// Continue tells Asterisk to return the channel to the dialplan
func (ch *ChannelHandle) Continue(context, extension, priority string) error {
	return ch.c.Continue(ch.id, context, extension, priority)
}

//---
// Play operations
//---

// Play initiates playback of the specified media uri
// to the channel, returning the Playback's ID
func (ch *ChannelHandle) Play(mediaURI string) (ph *PlaybackHandle, err error) {
	id := uuid.NewV1().String()
	ph, err = ch.c.Play(ch.id, id, mediaURI)
	return
}

//---
// Hangup Operations
//---

// Busy hangs up the channel with the "busy" cause code
func (ch *ChannelHandle) Busy() error {
	return ch.c.Busy(ch.id)
}

// Congestion hangs up the channel with the congestion cause code
func (ch *ChannelHandle) Congestion() error {
	return ch.c.Congestion(ch.id)
}

// Hangup hangs up the channel with the normal cause code
func (ch *ChannelHandle) Hangup() error {
	return ch.c.Hangup(ch.id, "normal")
}

//--

// --
// Answer operations
// --

// Answer answers the channel
func (ch *ChannelHandle) Answer() error {
	return ch.c.Answer(ch.id)
}

// IsAnswered checks the current state of the channel to see if it is "Up"
func (ch *ChannelHandle) IsAnswered() (bool, error) {
	updated, err := ch.Data()
	if err != nil {
		return false, errors.Wrap(err, "Failed to get updated channel")
	}
	return strings.ToLower(updated.State) == "up", nil
}

// ------

// --
// Ring Operations
// --

// Ring indicates ringing to the channel
func (ch *ChannelHandle) Ring() error {
	return ch.c.Ring(ch.id)
}

// StopRing stops ringing on the channel
func (ch *ChannelHandle) StopRing() error {
	return ch.c.StopRing(ch.id)
}

// ------

// --
// Mute operations
// --

// Mute mutes the channel in the given direction (in, out, both)
func (ch *ChannelHandle) Mute(dir string) (err error) {
	if err = normalizeDirection(&dir); err != nil {
		return
	}

	return ch.c.Mute(ch.id, dir)
}

// Unmute unmutes the channel in the given direction (in, out, both)
func (ch *ChannelHandle) Unmute(dir string) (err error) {
	if err = normalizeDirection(&dir); err != nil {
		return
	}

	return ch.c.Unmute(ch.id, dir)
}

// ----

// --
// Hold operations
// --

// Hold puts the channel on hold
func (ch *ChannelHandle) Hold() error {
	return ch.c.Hold(ch.id)
}

// StopHold retrieves the channel from hold
func (ch *ChannelHandle) StopHold() error {
	return ch.c.StopHold(ch.id)
}

// ----

// --
// Music on hold operations
// --

// MOH plays music on hold of the given class
// to the channel
func (ch *ChannelHandle) MOH(mohClass string) error {
	return ch.c.MOH(ch.id, mohClass)
}

// StopMOH stops playing of music on hold to the channel
func (ch *ChannelHandle) StopMOH() error {
	return ch.c.StopMOH(ch.id)
}

// ----

// --
// Silence operations
// --

// Silence plays silence to the channel
func (ch *ChannelHandle) Silence() error {
	return ch.c.Silence(ch.id)
}

// StopSilence stops silence to the channel
func (ch *ChannelHandle) StopSilence() error {
	return ch.c.StopSilence(ch.id)
}

// ----

// --
// Subscription
// --

// Subscribe subscribes the list of channel events
func (ch *ChannelHandle) Subscribe(n ...string) Subscription {
	return ch.c.Subscribe(ch.id, n...)
}

// TODO: rest of ChannelHandle
<commit_msg>v3 - Add SendDTMF to channel handle<commit_after>package ari

import (
	"strings"

	"github.com/pkg/errors"
	uuid "github.com/satori/go.uuid"
)

// Channel represents a communication path interacting with an Asterisk server.
type Channel interface {
	// Get returns a handle to a channel for further interaction
	Get(id string) *ChannelHandle

	// List lists the channels in asterisk
	List() ([]*ChannelHandle, error)

	// Create creates a new channel, returning a handle to it or an
	// error, if the creation failed
	Create(OriginateRequest) (*ChannelHandle, error)

	// Data returns the channel data for a given channel
	Data(id string) (ChannelData, error)

	// Continue tells Asterisk to return a channel to the dialplan
	Continue(id, context, extension, priority string) error

	// Busy hangs up the channel with the "busy" cause code
	Busy(id string) error

	// Congestion hangs up the channel with the "congestion" cause code
	Congestion(id string) error

	// Answer answers the channel
	Answer(id string) error

	// Hangup hangs up the given channel
	Hangup(id string, reason string) error

	// Ring indicates ringing to the channel
	Ring(id string) error

	// StopRing stops ringing on the channel
	StopRing(id string) error

	// SendDTMF sends DTMF to the channel
	SendDTMF(id string, dtmf string) error

	// Hold puts the channel on hold
	Hold(id string) error

	// StopHold retrieves the channel from hold
	StopHold(id string) error

	// Mute mutes a channel in the given direction (in,out,both)
	Mute(id string, dir string) error

	// Unmute unmutes a channel in the given direction (in,out,both)
	Unmute(id string, dir string) error

	// MOH plays music on hold
	MOH(id string, moh string) error

	// StopMOH stops music on hold
	StopMOH(id string) error

	// Silence plays silence to the channel
	Silence(id string) error

	// StopSilence stops the silence on the channel
	StopSilence(id string) error

	// Play plays the media URI to the channel
	Play(id string, playbackID string, mediaURI string) (*PlaybackHandle, error)

	// Subscribe subscribes on the channel events
	Subscribe(id string, n ...string) Subscription
}

// ChannelData is the data for a specific channel
type ChannelData struct {
	ID           string      `json:"id"`    // Unique id for this channel (same as for AMI)
	Name         string      `json:"name"`  // Name of this channel (tech/name-id format)
	State        string      `json:"state"` // State of the channel
	Accountcode  string      `json:"accountcode"`
	Caller       CallerID    `json:"caller"`    // CallerId of the calling endpoint
	Connected    CallerID    `json:"connected"` // CallerId of the connected line
	Creationtime DateTime    `json:"creationtime"`
	Dialplan     DialplanCEP `json:"dialplan"` // Current location in the dialplan
}

// NewChannelHandle returns a handle to the given ARI channel
func NewChannelHandle(id string, c Channel) *ChannelHandle {
	return &ChannelHandle{
		id: id,
		c:  c,
	}
}

// ChannelHandle provides a wrapper to a Channel interface for
// operations on a particular channel ID
type ChannelHandle struct {
	id string  // id of the channel on which we are operating
	c  Channel // the Channel interface with which we are operating
}

// ID returns the identifier for the channel handle
func (ch *ChannelHandle) ID() string {
	return ch.id
}

// Data returns the channel's data
func (ch *ChannelHandle) Data() (ChannelData, error) {
	return ch.c.Data(ch.id)
}

// Continue tells Asterisk to return the channel to the dialplan
func (ch *ChannelHandle) Continue(context, extension, priority string) error {
	return ch.c.Continue(ch.id, context, extension, priority)
}

//---
// Play operations
//---

// Play initiates playback of the specified media uri
// to the channel, returning the Playback's ID
func (ch *ChannelHandle) Play(mediaURI string) (ph *PlaybackHandle, err error) {
	id := uuid.NewV1().String()
	ph, err = ch.c.Play(ch.id, id, mediaURI)
	return
}

//---
// Hangup Operations
//---

// Busy hangs up the channel with the "busy" cause code
func (ch *ChannelHandle) Busy() error {
	return ch.c.Busy(ch.id)
}

// Congestion hangs up the channel with the congestion cause code
func (ch *ChannelHandle) Congestion() error {
	return ch.c.Congestion(ch.id)
}

// Hangup hangs up the channel with the normal cause code
func (ch *ChannelHandle) Hangup() error {
	return ch.c.Hangup(ch.id, "normal")
}

//--

// --
// Answer operations
// --

// Answer answers the channel
func (ch *ChannelHandle) Answer() error {
	return ch.c.Answer(ch.id)
}

// IsAnswered checks the current state of the channel to see if it is "Up"
func (ch *ChannelHandle) IsAnswered() (bool, error) {
	updated, err := ch.Data()
	if err != nil {
		return false, errors.Wrap(err, "Failed to get updated channel")
	}
	return strings.ToLower(updated.State) == "up", nil
}

// ------

// --
// Ring Operations
// --

// Ring indicates ringing to the channel
func (ch *ChannelHandle) Ring() error {
	return ch.c.Ring(ch.id)
}

// StopRing stops ringing on the channel
func (ch *ChannelHandle) StopRing() error {
	return ch.c.StopRing(ch.id)
}

// ------

// --
// Mute operations
// --

// Mute mutes the channel in the given direction (in, out, both)
func (ch *ChannelHandle) Mute(dir string) (err error) {
	if err = normalizeDirection(&dir); err != nil {
		return
	}

	return ch.c.Mute(ch.id, dir)
}

// Unmute unmutes the channel in the given direction (in, out, both)
func (ch *ChannelHandle) Unmute(dir string) (err error) {
	if err = normalizeDirection(&dir); err != nil {
		return
	}

	return ch.c.Unmute(ch.id, dir)
}

// ----

// --
// Hold operations
// --

// Hold puts the channel on hold
func (ch *ChannelHandle) Hold() error {
	return ch.c.Hold(ch.id)
}

// StopHold retrieves the channel from hold
func (ch *ChannelHandle) StopHold() error {
	return ch.c.StopHold(ch.id)
}

// ----

// --
// Music on hold operations
// --

// MOH plays music on hold of the given class
// to the channel
func (ch *ChannelHandle) MOH(mohClass string) error {
	return ch.c.MOH(ch.id, mohClass)
}

// StopMOH stops playing of music on hold to the channel
func (ch *ChannelHandle) StopMOH() error {
	return ch.c.StopMOH(ch.id)
}

// ----

// --
// Silence operations
// --

// Silence plays silence to the channel
func (ch *ChannelHandle) Silence() error {
	return ch.c.Silence(ch.id)
}

// StopSilence stops silence to the channel
func (ch *ChannelHandle) StopSilence() error {
	return ch.c.StopSilence(ch.id)
}

// ----

// --
// Subscription
// --

// Subscribe subscribes the list of channel events
func (ch *ChannelHandle) Subscribe(n ...string) Subscription {
	return ch.c.Subscribe(ch.id, n...)
}

// TODO: rest of ChannelHandle

// --
// DTMF
// --

// SendDTMF sends the DTMF information to the server
func (ch *ChannelHandle) SendDTMF(dtmf string) error {
	return ch.c.SendDTMF(ch.id, dtmf)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"strings"
)

// Command represents a CLI command.
// For example, in the command `heroku apps:create` the command would be `create`.
// They must have a Topic name that links to a real topic's name.
type Command struct {
	Topic        string             `json:"topic"`
	Command      string             `json:"command,omitempty"`
	Plugin       string             `json:"plugin"`
	Usage        string             `json:"usage"`
	Description  string             `json:"description"`
	Help         string             `json:"help"`
	FullHelp     string             `json:"fullHelp"`
	Hidden       bool               `json:"hidden"`
	NeedsApp     bool               `json:"needsApp"`
	NeedsAuth    bool               `json:"needsAuth"`
	VariableArgs bool               `json:"variableArgs"`
	Args         []Arg              `json:"args"`
	Flags        []Flag             `json:"flags"`
	Run          func(ctx *Context) `json:"-"`
}

func (c *Command) String() string {
	if c.Command == "" {
		return c.Topic
	}
	return c.Topic + ":" + c.Command
}

func commandUsage(c *Command) string {
	return c.String() + argsString(c.Args)
}

func (c *Command) buildFullHelp() string {
	if len(c.Flags) == 0 {
		return c.Help
	}
	lines := make([]string, 0, len(c.Flags))
	for _, flag := range c.Flags {
		if flag.Description == "" {
			lines = append(lines, flag.String())
		} else {
			lines = append(lines, fmt.Sprintf("%-20s # %s", flag.String(), flag.Description))
		}
	}
	return strings.Join(lines, "\n") + "\n\n" + c.Help
}

// CommandSet is a slice of Command structs with some helper methods.
type CommandSet []*Command

// ByTopicAndCommand returns a command that matches the passed topic and command.
func (commands CommandSet) ByTopicAndCommand(topic, command string) *Command {
	for _, c := range commands {
		if c.Topic == topic && c.Command == command {
			return c
		}
	}
	return nil
}

func (commands CommandSet) loadUsages() {
	for _, c := range commands {
		if c.Usage == "" {
			c.Usage = commandUsage(c)
		}
	}
}

func (commands CommandSet) loadFullHelp() {
	for _, c := range commands {
		if c.FullHelp == "" {
			c.FullHelp = c.buildFullHelp()
		}
	}
}

// Arg defines an argument for a command.
// These will be parsed in Go and passed to the Run method in the Context struct.
type Arg struct {
	Name     string `json:"name"`
	Optional bool   `json:"optional"`
}

func (a *Arg) String() string {
	if a.Optional {
		return "[" + strings.ToUpper(a.Name) + "]"
	}
	return strings.ToUpper(a.Name)
}

func argsString(args []Arg) string {
	var buffer bytes.Buffer
	for _, arg := range args {
		if arg.Optional {
			buffer.WriteString(" [" + strings.ToUpper(arg.Name) + "]")
		} else {
			buffer.WriteString(" " + strings.ToUpper(arg.Name))
		}
	}
	return buffer.String()
}

// Flag defines a flag for a command.
// These will be parsed in Go and passed to the Run method in the Context struct.
type Flag struct {
	Name        string `json:"name"`
	Char        string `json:"char"`
	Description string `json:"description"`
	HasValue    bool   `json:"hasValue"`
}

func (f *Flag) String() string {
	s := " "
	switch {
	case f.Char != "" && f.Name != "":
		s = s + "-" + f.Char + ", --" + f.Name
	case f.Char != "":
		s = s + "-" + f.Char
	case f.Name != "":
		s = s + "--" + f.Name
	}
	if f.HasValue {
		s = s + " " + strings.ToUpper(f.Name)
	}
	return s
}

var commandsTopic = &Topic{
	Name:        "commands",
	Description: "list all commands",
	Hidden:      true,
}

var commandsListCmd = &Command{
	Topic:       "commands",
	Description: "list all commands",
	Flags:       []Flag{{Name: "json"}},
	Run: func(ctx *Context) {
		if ctx.Flags["json"] == true {
			cli.LoadPlugins(GetPlugins())
			cli.Commands.loadUsages()
			cli.Commands.loadFullHelp()
			doc := map[string]interface{}{"topics": cli.Topics, "commands": cli.Commands}
			s, _ := json.Marshal(doc)
			Println(string(s))
			return
		}
		for _, command := range cli.Commands {
			if command.Command == "" {
				Printf("%s\n", command.Topic)
			} else {
				Printf("%s:%s\n", command.Topic, command.Command)
			}
		}
	},
}
<commit_msg>load plugins for command listing<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"strings"
)

// Command represents a CLI command.
// For example, in the command `heroku apps:create` the command would be `create`.
// They must have a Topic name that links to a real topic's name.
type Command struct {
	Topic        string             `json:"topic"`
	Command      string             `json:"command,omitempty"`
	Plugin       string             `json:"plugin"`
	Usage        string             `json:"usage"`
	Description  string             `json:"description"`
	Help         string             `json:"help"`
	FullHelp     string             `json:"fullHelp"`
	Hidden       bool               `json:"hidden"`
	NeedsApp     bool               `json:"needsApp"`
	NeedsAuth    bool               `json:"needsAuth"`
	VariableArgs bool               `json:"variableArgs"`
	Args         []Arg              `json:"args"`
	Flags        []Flag             `json:"flags"`
	Run          func(ctx *Context) `json:"-"`
}

func (c *Command) String() string {
	if c.Command == "" {
		return c.Topic
	}
	return c.Topic + ":" + c.Command
}

func commandUsage(c *Command) string {
	return c.String() + argsString(c.Args)
}

func (c *Command) buildFullHelp() string {
	if len(c.Flags) == 0 {
		return c.Help
	}
	lines := make([]string, 0, len(c.Flags))
	for _, flag := range c.Flags {
		if flag.Description == "" {
			lines = append(lines, flag.String())
		} else {
			lines = append(lines, fmt.Sprintf("%-20s # %s", flag.String(), flag.Description))
		}
	}
	return strings.Join(lines, "\n") + "\n\n" + c.Help
}

// CommandSet is a slice of Command structs with some helper methods.
type CommandSet []*Command

// ByTopicAndCommand returns a command that matches the passed topic and command.
func (commands CommandSet) ByTopicAndCommand(topic, command string) *Command {
	for _, c := range commands {
		if c.Topic == topic && c.Command == command {
			return c
		}
	}
	return nil
}

func (commands CommandSet) loadUsages() {
	for _, c := range commands {
		if c.Usage == "" {
			c.Usage = commandUsage(c)
		}
	}
}

func (commands CommandSet) loadFullHelp() {
	for _, c := range commands {
		if c.FullHelp == "" {
			c.FullHelp = c.buildFullHelp()
		}
	}
}

// Arg defines an argument for a command.
// These will be parsed in Go and passed to the Run method in the Context struct.
type Arg struct {
	Name     string `json:"name"`
	Optional bool   `json:"optional"`
}

func (a *Arg) String() string {
	if a.Optional {
		return "[" + strings.ToUpper(a.Name) + "]"
	}
	return strings.ToUpper(a.Name)
}

func argsString(args []Arg) string {
	var buffer bytes.Buffer
	for _, arg := range args {
		if arg.Optional {
			buffer.WriteString(" [" + strings.ToUpper(arg.Name) + "]")
		} else {
			buffer.WriteString(" " + strings.ToUpper(arg.Name))
		}
	}
	return buffer.String()
}

// Flag defines a flag for a command.
// These will be parsed in Go and passed to the Run method in the Context struct.
type Flag struct {
	Name        string `json:"name"`
	Char        string `json:"char"`
	Description string `json:"description"`
	HasValue    bool   `json:"hasValue"`
}

func (f *Flag) String() string {
	s := " "
	switch {
	case f.Char != "" && f.Name != "":
		s = s + "-" + f.Char + ", --" + f.Name
	case f.Char != "":
		s = s + "-" + f.Char
	case f.Name != "":
		s = s + "--" + f.Name
	}
	if f.HasValue {
		s = s + " " + strings.ToUpper(f.Name)
	}
	return s
}

var commandsTopic = &Topic{
	Name:        "commands",
	Description: "list all commands",
	Hidden:      true,
}

var commandsListCmd = &Command{
	Topic:       "commands",
	Description: "list all commands",
	Flags:       []Flag{{Name: "json"}},
	Run: func(ctx *Context) {
		cli.LoadPlugins(GetPlugins())
		if ctx.Flags["json"] == true {
			cli.Commands.loadUsages()
			cli.Commands.loadFullHelp()
			doc := map[string]interface{}{"topics": cli.Topics, "commands": cli.Commands}
			s, _ := json.Marshal(doc)
			Println(string(s))
			return
		}
		for _, command := range cli.Commands {
			if command.Command == "" {
				Printf("%s\n", command.Topic)
			} else {
				Printf("%s:%s\n", command.Topic, command.Command)
			}
		}
	},
}
<|endoftext|>


<commit_before>package actions

import (
	"fmt"
	"sync"

	"github.com/tcolar/goed/core"
)

// TODO : group together quick succesive undos (insert a, insert b, insert c) + Flushing

var maxUndos = 1000

// viewId keyed map of undo actions
var undos map[int64][]actionTuple = map[int64][]actionTuple{}

// viewId keyed map of redo actions
var redos map[int64][]actionTuple = map[int64][]actionTuple{}

var lock sync.Mutex

// a do/undo combo
type actionTuple struct {
	do   core.Action
	undo core.Action
}

// or group by alphanum sequence ??
func Undo(viewId int64) error {
	action, err := func() (core.Action, error) {
		lock.Lock()
		defer lock.Unlock()
		tuples, found := undos[viewId]
		if !found || len(tuples) == 0 {
			return nil, fmt.Errorf("Nothing to undo.")
		}
		tuple := tuples[len(tuples)-1]
		undos[viewId] = undos[viewId][:len(tuples)-1]
		redos[viewId] = append(redos[viewId], tuple)
		return tuple.undo, nil
	}()
	if err != nil {
		return err
	}
	return action.Run()
}

func Redo(viewId int64) error {
	action, err := func() (core.Action, error) {
		lock.Lock()
		defer lock.Unlock()
		tuples, found := redos[viewId]
		if !found || len(tuples) == 0 {
			return nil, fmt.Errorf("Nothing to redo.")
		}
		tuple := tuples[len(tuples)-1]
		redos[viewId] = redos[viewId][:len(tuples)-1]
		undos[viewId] = append(undos[viewId], tuple)
		return tuple.do, nil
	}()
	if err != nil {
		return err
	}
	return action.Run()
}

func UndoAdd(viewId int64, do, undo core.Action) {
	lock.Lock()
	defer lock.Unlock()
	delete(redos, viewId)
	if len(undos[viewId]) < maxUndos {
		undos[viewId] = append(undos[viewId], actionTuple{do, undo})
	} else {
		copy(undos[viewId], undos[viewId][1:])
		undos[viewId][len(undos[viewId])-1] = actionTuple{do, undo}
	}
}

func UndoClear(viewId int64) {
	lock.Lock()
	defer lock.Unlock()
	delete(undos, viewId)
	delete(redos, viewId)
}

// Dump prints out the undo/redo stack of a view, for debugging
func Dump(viewId int64) {
	fmt.Printf("Undos:\n")
	for _, u := range undos[viewId] {
		fmt.Printf("\t %#v\n", u)
	}
	fmt.Printf("Redos:\n")
	for _, r := range redos[viewId] {
		fmt.Printf("\t %#v\n", r)
	}
}
<commit_msg>Undo/Redo TODO's<commit_after>package actions

import (
	"fmt"
	"sync"

	"github.com/tcolar/goed/core"
)

// TODO : This is kind of memory heavy ....
// TODO : group together quick succesive undos (insert a, insert b, insert c) + Flushing

var maxUndos = 500

// viewId keyed map of undo actions
var undos map[int64][]actionTuple = map[int64][]actionTuple{}

// viewId keyed map of redo actions
var redos map[int64][]actionTuple = map[int64][]actionTuple{}

var lock sync.Mutex

// a do/undo combo
type actionTuple struct {
	do   core.Action
	undo core.Action
}

// or group by alphanum sequence ??
func Undo(viewId int64) error {
	action, err := func() (core.Action, error) {
		lock.Lock()
		defer lock.Unlock()
		tuples, found := undos[viewId]
		if !found || len(tuples) == 0 {
			return nil, fmt.Errorf("Nothing to undo.")
		}
		tuple := tuples[len(tuples)-1]
		undos[viewId] = undos[viewId][:len(tuples)-1]
		redos[viewId] = append(redos[viewId], tuple)
		return tuple.undo, nil
	}()
	if err != nil {
		return err
	}
	return action.Run()
}

func Redo(viewId int64) error {
	action, err := func() (core.Action, error) {
		lock.Lock()
		defer lock.Unlock()
		tuples, found := redos[viewId]
		if !found || len(tuples) == 0 {
			return nil, fmt.Errorf("Nothing to redo.")
		}
		tuple := tuples[len(tuples)-1]
		redos[viewId] = redos[viewId][:len(tuples)-1]
		undos[viewId] = append(undos[viewId], tuple)
		return tuple.do, nil
	}()
	if err != nil {
		return err
	}
	return action.Run()
}

func UndoAdd(viewId int64, do, undo core.Action) {
	lock.Lock()
	defer lock.Unlock()
	delete(redos, viewId)
	if len(undos[viewId]) < maxUndos {
		undos[viewId] = append(undos[viewId], actionTuple{do, undo})
	} else {
		copy(undos[viewId], undos[viewId][1:])
		undos[viewId][len(undos[viewId])-1] = actionTuple{do, undo}
	}
}

func UndoClear(viewId int64) {
	lock.Lock()
	defer lock.Unlock()
	delete(undos, viewId)
	delete(redos, viewId)
}

// Dump prints out the undo/redo stack of a view, for debugging
func Dump(viewId int64) {
	fmt.Printf("Undos:\n")
	for _, u := range undos[viewId] {
		fmt.Printf("\t %#v\n", u)
	}
	fmt.Printf("Redos:\n")
	for _, r := range redos[viewId] {
		fmt.Printf("\t %#v\n", r)
	}
}
<|endoftext|>