code: func (s *Scheduler) updateStack(ctx context.Context, input *updateStackInput, output chan stackOperationOutput, ss twelvefactor.StatusStream) error {
  	waiter := s.waitFor(ctx, updateStack, ss)
  
  	locked := make(chan struct{})
  	submitted := make(chan error, 1)
  	fn := func() error {
  		close(locked)
  		err := s.executeStackUpdate(input)
  		if err == nil {
  			publish(ctx, ss, "Stack update submitted")
  		}
  		submitted <- err
  		return err
  	}
  
  	go func() {
  		stack, err := s.performStackOperation(ctx, *input.StackName, fn, waiter, ss)
  		output <- stackOperationOutput{stack, err}
  	}()
  
  	var err error
  	select {
  	case <-s.after(lockWait):
  		publish(ctx, ss, "Waiting for existing stack operation to complete")
  		// FIXME: At this point, we don't want to affect UX by waiting
  		// around, so we return. But, if the stack update times out, or
  		// there's an error, that information is essentially silenced.
  		return nil
  	case <-locked:
  		// if a lock is obtained within the time frame, we might as well
  		// just wait for the update to get submitted.
  		err = <-submitted
  	}
  
  	return err
  }
docs: // updateStack updates an existing CloudFormation stack with the given input.
  // If there are no other active updates, this function returns as soon as the
  // stack update has been submitted. If there are other updates, the function
  // returns after `lockTimeout` and the update continues in the background.