code: func (c *client) createIO(fifos *cio.FIFOSet, containerID, processID string, stdinCloseSync chan struct{}, attachStdio libcontainerdtypes.StdioCallback) (cio.IO, error) {
  	var (
  		io  *cio.DirectIO
  		err error
  	)
  	io, err = c.newDirectIO(context.Background(), fifos)
  	if err != nil {
  		return nil, err
  	}
  
  	if io.Stdin != nil {
  		var (
  			err       error
  			stdinOnce sync.Once
  		)
  		pipe := io.Stdin
  		io.Stdin = ioutils.NewWriteCloserWrapper(pipe, func() error {
  			stdinOnce.Do(func() {
  				err = pipe.Close()
  				// Do the rest in a new routine to avoid a deadlock if the
  				// Exec/Start call failed.
  				go func() {
  					<-stdinCloseSync
  					p, err := c.getProcess(context.Background(), containerID, processID)
  					if err == nil {
  						err = p.CloseIO(context.Background(), containerd.WithStdinCloser)
  						if err != nil && strings.Contains(err.Error(), "transport is closing") {
  							err = nil
  						}
  					}
  				}()
  			})
  			return err
  		})
  	}
  
  	rio, err := attachStdio(io)
  	if err != nil {
  		io.Cancel()
  		io.Close()
  	}
  	return rio, err
  }
docs: // createIO creates the io to be used by a process
  // This needs to get a pointer to interface as upon closure the process may not have yet been registered