Example #1
0
func (cp *compiler) pipeline(n *parse.Pipeline) OpFunc {
	ops := cp.formOps(n.Forms)

	return func(ec *EvalCtx) {
		bg := n.Background
		if bg {
			ec = ec.fork("background job " + n.SourceText())
			ec.intCh = nil
			ec.background = true

			if ec.Editor != nil {
				// TODO: Redirect output in interactive mode so that the line
				// editor does not get messed up.
			}
		}

		nforms := len(ops)

		var wg sync.WaitGroup
		wg.Add(nforms)
		errors := make([]Error, nforms)
		var verdict bool

		var nextIn *Port

		// 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 < nforms-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 {
					throwf("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
			thisError := &errors[i]
			isLast := i == nforms-1
			go func() {
				err := newEc.PEval(thisOp)
				// Logger.Printf("closing ports of %s", newEc.context)
				ClosePorts(newEc.ports)
				if isLast {
					verdict = newEc.verdict
				}
				*thisError = Error{err}
				wg.Done()
			}()
		}

		if bg {
			// Background job, wait for form termination asynchronously.
			go func() {
				wg.Wait()
				msg := "job " + n.SourceText() + " finished"
				if !allok(errors) {
					msg += ", errors = " + makeCompositeError(errors).Error()
				}
				if !verdict {
					msg += ", pred = false"
				}
				if ec.Editor != nil {
					m := ec.Editor.ActiveMutex()
					m.Lock()
					defer m.Unlock()

					if ec.Editor.Active() {
						ec.Editor.Notify("%s", msg)
					} else {
						ec.ports[2].File.WriteString(msg)
					}
				} else {
					ec.ports[2].File.WriteString(msg)
				}
			}()
		} else {
			wg.Wait()
			maybeThrow(makeCompositeError(errors))
			ec.verdict = verdict
		}
	}
}