Remnant zombie processes when executing wine64 from Go code

Questions about Wine on Linux
Locked
Shugyousha
Newbie
Newbie
Posts: 2
Joined: Thu Dec 07, 2017 4:35 am

Remnant zombie processes when executing wine64 from Go code

Post by Shugyousha »

Hi

We want to run a Windows-native C++ console application through wine on a
headless Linux server (amd64 Debian 9) within a Docker container (running
wine-1.8.7 (Debian 1.8.7-2)). Our worker server process (written in Go and
running in the container) runs tasks (~5000 a day per server) which execute
our native Windows binary which works very well and we seem to get comparable
performance as when we run the native C++ binary on our Windows servers.

However, after about a few days of continuously running, the server stopped
doing any work. After rebooting the server we realized that there are a lot of
"explorer.exe" processes being left in a zombie state (about 2000 zombie
instances after one day which are attached to our Go worker process). It looks
like our servers would run out of process table entries because they end up
with too many zombie processes and then are not able to spawn any new
processes to do work.

We have tried executing wine64 like this

args := []string{"cmd", "/c", "/opt/path-to-our-wine-binary/binary.exe", "-t",
arg1, arg2}
cmd := exec.CommandContext(ctxTask, "wine64", args...)
err := cmd.Run()

(see https://golang.org/pkg/os/exec/#CommandContext)

Calling Wine this way is synchronous so we are waiting for the process to
complete and then return but we still end up with processes in a zombie state
(note that our Go process runs up to 8 threads which concurrently spawn this
wine64 process). Weirldly enough, when calling wine64 like above we end up
with a lot of "binary." processes in the zombie state (not sure where the
"exe" suffix has gone to). ps -eF output looks something like this:

root 5773 3584 23 0 0 3 10:38 ? 00:00:54 [binary.] <defunct>
root 5858 3584 0 0 0 1 10:38 ? 00:00:00 [binary.] <defunct>
root 6003 3584 0 0 0 0 10:38 ? 00:00:00 [binary.] <defunct>
root 6046 3584 0 0 0 0 10:38 ? 00:00:00 [binary.] <defunct>
... hundreds more like those

When we call wine64 directly on the binary like this

args := []string{"/opt/path-to-our-wine-binary/binary.exe", "-t", arg1, arg2}
cmd := exec.CommandContext(ctxTask, "wine64", args...)
err := cmd.Run()

we end up with a lot of "explorer.exe" processes in the zombie state instead
(and not the "binary." ones):

root 3716 3584 0 0 0 2 10:31 ? 00:00:00 [explorer.exe] <defunct>
root 3718 3584 0 0 0 2 10:31 ? 00:00:00 [explorer.exe] <defunct>
root 3823 3584 0 0 0 2 10:31 ? 00:00:00 [explorer.exe] <defunct>
root 3834 3584 0 0 0 3 10:31 ? 00:00:00 [explorer.exe] <defunct>
... etc.

All of the zombie processes get attached to our Go server process which we
don't want to kill to get rid of the zombies...

We also are now running "wineserver -p" before starting wine for the first
time but this does not get rid of the zombie processes either.

What we assume is happening is that "wine64" starts the "explorer.exe" process
which for some reason isn't killed when wine64 returns. Since the
"explorer.exe" is still hanging around after wine64 returns and the Go code
resumes, the "explorer.exe" process gets attached to the Go parent process as
a zombie. Those zombies do not seem to come into existence on every wine64
call but only on every third call or so.

Is there a way to make sure that wine64 reaps all it's children processes
and/or reuses them? Maybe we have to call wine in a different way?

Any pointers would be very welcome!
Shugyousha
Newbie
Newbie
Posts: 2
Joined: Thu Dec 07, 2017 4:35 am

Re: Remnant zombie processes when executing wine64 from Go c

Post by Shugyousha »

So we may have "fixed" this issue now.

The zombie processes are due to wine not (being able to?) waitpid for the children it spawns before terminating. The resulting zombie processes get then attached to our Go worker.

The reason for that is that by default in a Docker container there is no proper "init" process. A proper init process is traditionally responsible to reap zombie processes which the Docker run command (used to run the Docker container) does not provide unless you pass it the "--init" flag. After we added this flag, the zombie processes resulting from running wine64 get reaped and our servers should now not run out of process table entries anymore.

This feels a little bit like a workaround. A cleaner way would be to make sure that wine64 reaps the child processes that it spawns (or to just not spawn them when not necessary?) after they are done. For now this takes care of our issue though.
Locked