<commit_before>package main

import (
	"errors"
	"fmt"
	"os"
	"os/signal"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"time"
)

const shutdownGraceTime = 3 * time.Second

var flagPort int
var flagConcurrency string
var flagRestart bool
var shutdownNow bool

var processes = map[string]*Process{}
var shutdown_mutex = new(sync.Mutex)
var wg sync.WaitGroup

var cmdStart = &Command{
	Run:   runStart,
	Usage: "start [process name] [-f procfile] [-e env] [-c concurrency] [-p port] [-r]",
	Short: "Start the application",
	Long: `
Start the application specified by a Procfile (defaults to ./Procfile)

Examples:

  forego start
  forego start web
  forego start -f Procfile.test -e .env.test
`,
}

func init() {
	cmdStart.Flag.StringVar(&flagProcfile, "f", "Procfile", "procfile")
	cmdStart.Flag.StringVar(&flagEnv, "e", "", "env")
	cmdStart.Flag.IntVar(&flagPort, "p", 5000, "port")
	cmdStart.Flag.StringVar(&flagConcurrency, "c", "", "concurrency")
	cmdStart.Flag.BoolVar(&flagRestart, "r", false, "restart")
}

func parseConcurrency(value string) (map[string]int, error) {
	concurrency := map[string]int{}
	if strings.TrimSpace(value) == "" {
		return concurrency, nil
	}

	parts := strings.Split(value, ",")
	for _, part := range parts {
		if !strings.Contains(part, "=") {
			return concurrency, errors.New("Parsing concurency")
		}

		nameValue := strings.Split(part, "=")
		n, v := strings.TrimSpace(nameValue[0]), strings.TrimSpace(nameValue[1])
		if n == "" || v == "" {
			return concurrency, errors.New("Parsing concurency")
		}

		numProcs, err := strconv.ParseInt(v, 10, 16)
		if err != nil {
			return concurrency, err
		}

		concurrency[n] = int(numProcs)
	}
	return concurrency, nil
}

func startProcess(idx, procNum int, proc ProcfileEntry, env Env, of *OutletFactory) {
	shutdown_mutex.Lock()
	port := flagPort + (idx * 100)
	ps := NewProcess(proc.Command, env)
	procName := fmt.Sprint(proc.Name, ".", procNum+1)
	processes[procName] = ps
	ps.Env["PORT"] = strconv.Itoa(port)
	ps.Root = filepath.Dir(flagProcfile)
	ps.Stdin = nil
	ps.Stdout = of.CreateOutlet(procName, idx, false)
	ps.Stderr = of.CreateOutlet(procName, idx, true)
	ps.Start()

	of.SystemOutput(fmt.Sprintf("starting %s on port %d", procName, port))

	wg.Add(1)
	go func(proc ProcfileEntry, ps *Process) {
		defer wg.Done()
		ps.Wait()

		if flagRestart && !shutdownNow {
			delete(processes, proc.Name)
			startProcess(idx, procNum, proc, env, of)
			return
		}

		delete(processes, proc.Name)
		ShutdownProcesses(of)

	}(proc, ps)
	shutdown_mutex.Unlock()
}

func runStart(cmd *Command, args []string) {
	root := filepath.Dir(flagProcfile)

	if flagEnv == "" {
		flagEnv = filepath.Join(root, ".env")
	}

	pf, err := ReadProcfile(flagProcfile)
	handleError(err)

	env, err := ReadEnv(flagEnv)
	handleError(err)

	concurrency, err := parseConcurrency(flagConcurrency)
	handleError(err)

	of := NewOutletFactory()
	of.Padding = pf.LongestProcessName()

	handler := make(chan os.Signal, 1)<commit_msg>Use defer to unlock mutex<commit_after>package main

import (
	"errors"
	"fmt"
	"os"
	"os/signal"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"time"
)

const shutdownGraceTime = 3 * time.Second

var flagPort int
var flagConcurrency string
var flagRestart bool
var shutdownNow bool

var processes = map[string]*Process{}
var shutdown_mutex = new(sync.Mutex)
var wg sync.WaitGroup

var cmdStart = &Command{
	Run:   runStart,
	Usage: "start [process name] [-f procfile] [-e env] [-c concurrency] [-p port] [-r]",
	Short: "Start the application",
	Long: `
Start the application specified by a Procfile (defaults to ./Procfile)

Examples:

  forego start
  forego start web
  forego start -f Procfile.test -e .env.test
`,
}

func init() {
	cmdStart.Flag.StringVar(&flagProcfile, "f", "Procfile", "procfile")
	cmdStart.Flag.StringVar(&flagEnv, "e", "", "env")
	cmdStart.Flag.IntVar(&flagPort, "p", 5000, "port")
	cmdStart.Flag.StringVar(&flagConcurrency, "c", "", "concurrency")
	cmdStart.Flag.BoolVar(&flagRestart, "r", false, "restart")
}

func parseConcurrency(value string) (map[string]int, error) {
	concurrency := map[string]int{}
	if strings.TrimSpace(value) == "" {
		return concurrency, nil
	}

	parts := strings.Split(value, ",")
	for _, part := range parts {
		if !strings.Contains(part, "=") {
			return concurrency, errors.New("Parsing concurency")
		}

		nameValue := strings.Split(part, "=")
		n, v := strings.TrimSpace(nameValue[0]), strings.TrimSpace(nameValue[1])
		if n == "" || v == "" {
			return concurrency, errors.New("Parsing concurency")
		}

		numProcs, err := strconv.ParseInt(v, 10, 16)
		if err != nil {
			return concurrency, err
		}

		concurrency[n] = int(numProcs)
	}
	return concurrency, nil
}

func startProcess(idx, procNum int, proc ProcfileEntry, env Env, of *OutletFactory) {
	shutdown_mutex.Lock()
	defer shutdown_mutex.Unlock()

	port := flagPort + (idx * 100)

	ps := NewProcess(proc.Command, env)
	procName := fmt.Sprint(proc.Name, ".", procNum+1)
	processes[procName] = ps
	ps.Env["PORT"] = strconv.Itoa(port)
	ps.Root = filepath.Dir(flagProcfile)
	ps.Stdin = nil
	ps.Stdout = of.CreateOutlet(procName, idx, false)
	ps.Stderr = of.CreateOutlet(procName, idx, true)
	ps.Start()

	of.SystemOutput(fmt.Sprintf("starting %s on port %d", procName, port))

	wg.Add(1)
	go func(proc ProcfileEntry, ps *Process) {
		defer wg.Done()
		ps.Wait()

		if flagRestart && !shutdownNow {
			delete(processes, proc.Name)
			startProcess(idx, procNum, proc, env, of)
			return
		}

		delete(processes, proc.Name)
		ShutdownProcesses(of)
	}(proc, ps)
}

func runStart(cmd *Command, args []string) {
	root := filepath.Dir(flagProcfile)

	if flagEnv == "" {
		flagEnv = filepath.Join(root, ".env")
	}

	pf, err := ReadProcfile(flagProcfile)
	handleError(err)

	env, err := ReadEnv(flagEnv)
	handleError(err)

	concurrency, err := parseConcurrency(flagConcurrency)
	handleError(err)

	of := NewOutletFactory()
	of.Padding = pf.LongestProcessName()

	handler := make(chan os.Signal, 1)
{'fraction_non_alphanumeric': 0.15256217659823068, 'fraction_numerical': 0.005675179435820397, 'mean_word_length': 4.52258064516129, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2779476', 'n_tokens_mistral': 2319, 'n_tokens_neox': 2172, 'n_words': 520}