func TestAsyncReaderDeadlock(t *testing.T) { done := make(chan struct{}) isatty := sys.IsATTY(1) rand.Seed(time.Now().UTC().UnixNano()) timer := time.NewTimer(DeadlockTimeout) for i := 0; i < DeadlockRun; i++ { if isatty { fmt.Printf("\r%d/%d ", i+1, DeadlockRun) } go f(done) select { case <-done: // no deadlock trigerred case <-timer.C: // deadlock t.Errorf("%s", sys.DumpStack()) t.Fatalf("AsyncReader deadlock trigerred on run %d/%d, stack trace:\n%s", i, DeadlockRun, sys.DumpStack()) } timer.Reset(DeadlockTimeout) } if isatty { fmt.Print("\r \r") } }
// Main is the entry point of elvish. func Main() { defer rescue() flag.Usage = usage flag.Parse() args := flag.Args() if len(args) > 1 { usage() os.Exit(2) } if *help { usage() os.Exit(0) } if *log != "" { err := util.SetOutputFile(*log) if err != nil { fmt.Println(err) } } if *cpuprofile != "" { f, err := os.Create(*cpuprofile) if err != nil { Logger.Fatal(err) } pprof.StartCPUProfile(f) defer pprof.StopCPUProfile() } handleHupAndQuit() logSignals() ev, st := newEvalerAndStore() defer func() { err := st.Close() if err != nil { fmt.Println("failed to close database:", err) } }() if len(args) == 1 { if *cmd { evalText(ev, "code from -c", args[0]) } else { script(ev, args[0]) } } else if !sys.IsATTY(0) { script(ev, "/dev/stdin") } else { interact(ev, st) } }
// Eval sets up the Evaler and evaluates a chunk. The supplied name and text are // used in diagnostic messages. func (ev *Evaler) Eval(op Op, name, text string) error { inCh := make(chan Value) close(inCh) outCh := make(chan Value, outChanSize) outDone := make(chan struct{}) go func() { for v := range outCh { fmt.Printf("%s%s\n", outChanLeader, v.Repr(initIndent)) } close(outDone) }() ports := []*Port{ {File: os.Stdin, Chan: inCh}, {File: os.Stdout, Chan: outCh}, {File: os.Stderr, Chan: BlackholeChan}, } // signal.Ignore(syscall.SIGTTIN) // signal.Ignore(syscall.SIGTTOU) stopSigGoroutine := make(chan struct{}) sigGoRoutineDone := make(chan struct{}) // Set up intCh. ev.intCh = make(chan struct{}) sigCh := make(chan os.Signal) signal.Notify(sigCh, syscall.SIGINT, syscall.SIGQUIT) go func() { closedIntCh := false loop: for { select { case <-sigCh: if !closedIntCh { close(ev.intCh) closedIntCh = true } case <-stopSigGoroutine: break loop } } ev.intCh = nil signal.Stop(sigCh) close(sigGoRoutineDone) }() ret, err := ev.eval(op, ports, name, text) close(outCh) <-outDone close(stopSigGoroutine) <-sigGoRoutineDone if !ret { fmt.Println(falseIndicator) } // Put myself in foreground, in case some command has put me in background. // XXX Should probably use fd of /dev/tty instead of 0. if sys.IsATTY(0) { err := sys.Tcsetpgrp(0, syscall.Getpgrp()) if err != nil { fmt.Println("failed to put myself in foreground:", err) } } return err }
// TODO(xiaq): Currently only the editor deals with signals. func interact() { ev, st := newEvalerAndStore() sigch := make(chan os.Signal, sigchSize) signal.Notify(sigch) ed := edit.NewEditor(os.Stdin, sigch, ev, st) datadir, err := store.EnsureDataDir() printError(err) if err == nil { // XXX err := ev.Source(datadir + "/rc.elv") if err != nil && !os.IsNotExist(err) { printError(err) } } cmdNum := 0 username := "******" user, err := user.Current() if err == nil { username = user.Username } hostname, err := os.Hostname() if err != nil { hostname = "???" } rpromptStr := username + "@" + hostname prompt := func() string { return osutil.Getwd() + "> " } rprompt := func() string { return rpromptStr } readLine := func() edit.LineRead { return ed.ReadLine(prompt, rprompt) } usingBasic := false if !sys.IsATTY(0) { readLine = basicReadLine usingBasic = true } for { cmdNum++ // name := fmt.Sprintf("<tty %d>", cmdNum) lr := readLine() // signal.Stop(sigch) if lr.EOF { break } else if lr.Err != nil { fmt.Println("Editor error:", lr.Err) if !usingBasic { fmt.Println("Falling back to basic line editor") readLine = basicReadLine usingBasic = true } continue } n, err := parse.Parse(lr.Line) printError(err) if err == nil { err := ev.EvalInteractive(lr.Line, n) printError(err) } } }
func (cp *compiler) pipeline(n *parse.Pipeline) Op { ops := cp.forms(n.Forms) p := n.Begin() return func(ec *EvalCtx) { var nextIn *Port errorChans := make([]chan Error, len(ops)) // For each form, create a dedicated evalCtx and run asynchronously for i, op := range ops { newEc := ec.fork(fmt.Sprintf("form op %v", op)) if i > 0 { newEc.ports[0] = nextIn } if i < len(ops)-1 { // Each internal port pair consists of a (byte) pipe pair and a // channel. // os.Pipe sets O_CLOEXEC, which is what we want. reader, writer, e := os.Pipe() if e != nil { ec.errorf(p, "failed to create pipe: %s", e) } ch := make(chan Value, pipelineChanBufferSize) newEc.ports[1] = &Port{ File: writer, Chan: ch, CloseFile: true, CloseChan: true} nextIn = &Port{ File: reader, Chan: ch, CloseFile: true, CloseChan: false} } thisOp := op errorChans[i] = make(chan Error) thisErrorChan := errorChans[i] go func() { err := newEc.PEval(thisOp) // Logger.Printf("closing ports of %s", newEc.context) ClosePorts(newEc.ports) thisErrorChan <- Error{err} }() } intCh := make(chan os.Signal) signal.Notify(intCh, syscall.SIGINT) interrupted := make(chan struct{}) cancel := make(chan struct{}, 1) go func() { // When SIGINT is received, sleep for InterruptDeadline before the // closing interrupted channel. select { case <-intCh: case <-cancel: return } select { case <-time.After(InterruptDeadline): case <-cancel: return } close(interrupted) }() // Wait for all forms to finish and collect error returns, unless an // interrupt was received and the form didn't quit within // InterruptDeadline. errors := make([]Error, len(ops)) for i, errorChan := range errorChans { select { case errors[i] = <-errorChan: case <-interrupted: errors[i] = Error{ErrStillRunning} } } // Make sure the SIGINT listener exits. close(cancel) signal.Stop(intCh) // Make sure I am in foreground. if PutInForeground && sys.IsATTY(0) { err := sys.Tcsetpgrp(0, syscall.Getpgrp()) if err != nil { throw(err) } } if !allok(errors) { if len(errors) == 1 { throw(errors[0].inner) } else { throw(multiError{errors}) } } } }
func printErrorString(errtype, s string) { if sys.IsATTY(2) { s = "\033[1;31m" + s + "\033[m" } fmt.Fprintln(os.Stderr, errtype+": "+s) }
func printErrorString(s string) { if sys.IsATTY(2) { s = "\033[1;31m" + s + "\033[m" } fmt.Fprintln(os.Stderr, s) }