Starting new processes with exec.Command

I have written a program that downloads YouTube videos (using a youtube-dl fork: yt-dlp), and then plays the videos using, for example, smplayer, with exec.Command. The problem is that every time I start a video, and then later start a new one, a “dead” process from the first video is left in memory:
image
This is not a huge problem, of course, but after a couple of days there might be a hundred of these dead processes. The only thing that removes the dead processes, is if I restart my program, so these smplayer-processes are for some reason attached to the main process (my program).

I use Linux Mint 20.2, and go 1.15.

Here is the code that starts the video player:

func (v *VideoList) playVideo(video *database.Video) {
	videoPath := v.getVideoPath(video.ID)
   {...}

	command := fmt.Sprintf("smplayer '%s'", videoPath)
	cmd := exec.Command("/bin/bash", "-c", command)

	// Starts a sub process (smplayer)
	err := cmd.Start()
	if err != nil {
		logger.LogError(err)
	}
}

A couple of things that I have tried, including all combinations of the below items:

  • Adding the following code, after exec.Command and before cmd.Start. Apparently this should detach the process somehow, but I can’t get it to work:
	cmd.SysProcAttr = &syscall.SysProcAttr{
		Setpgid: true,
		Pgid:    0,
	}
  • Wrapping the calling function in a goroutine:
go func() {
    v.PlayVideo(video)
}()
  • I have tried using other players, like mpv, instead of smplayer.

Does anyone have any idea how to solve this?

You’re not calling (*os.Cmd).Wait after calling start which will wait for the command to complete and release any resources. You could also use (*os.Cmd).Run instead of Start which will in essence combine the calls to Start and Wait. Because you’re running the process in its own goroutine where you’re not blocking anything, I would recommend switching to Run.

3 Likes