digitalmars.D - [WEKA] Calling fork() and D runtime
- Jonathan Shamir (89/89) May 15 2017 Hey,
- Daniel Kozak via Digitalmars-d (4/86) May 15 2017 This is how I use fork on one of my project
- Jonathan Shamir (2/2) May 15 2017 OK well, seems like calling _exit() (instead of exit()) did the
- Kagamin (2/2) May 16 2017 D runtime is incompatible with fork, enforce and catch won't work
- Petar Kirov [ZombineDev] (8/98) May 21 2017 Some links to related discussions:
Hey, This is my first time writing in the D forums! I have an application written in D that runs as a linux daemon (some python service script is in charge of running and daemonizing it). This "agent" works similar to docker - a service that accepts commands and runs in the background, and starts our application container. The container is itself a daemon (ppid = 1) with unshared namespaces etc. So, normally, implementing such an application would look something like: 1. Main "agent" process runs fork() to create a child process (child_1). All memory is copy-on-write. 2. Child_1 malloc()s a stack, and calls clone() to create yet another child (child_2), which will eventually become the container pid 1. 3. Child_2 initializes the container (mounts, unshare, chroot, etc) then eventually exec()s into the container init process. 4. child_1 exit()s, which causes child_2 to become a daemon. 5. The agent main process should wait() on the forked pid since it's impolite to leave zombies (I do this in a thread). The problem I encounter is with the forked process (child_1). Here is the code I wrote handling the fork() (Note: this functionality should really be provided by core.threads or something, for unix environments). ```private void deferToForkProcess(int delegate() entryPoint, Timeout timeout = Timeout.infinite) { import core.runtime : Runtime; import core.sys.posix.unistd : fork, pid_t; import core.sys.posix.sys.wait; import core.stdc.stdlib : exit; int rc = theReactor.deferToThread({ pid_t pid = fork(); errnoEnforce(pid >= 0, "Fork failed"); // child process if (pid == 0) { try { int rc = entryPoint(); exit(rc); } catch (Throwable ex) { try { LOG_ERROR(ex.toString); } catch (Throwable) {} exit(1); } //assert(false); } // parent - wait for child to exit int status = 0; do { errnoEnforce(waitpid(pid, &status, 0) != -1, "Waitpid failed"); } while (!WIFEXITED(status)); int rc = WEXITSTATUS(status); return rc; }, timeout); enforce(rc == 0, "Child process failed (rc = %d)".format(rc)); } ``` entryPoint() returns 0, but the exit(0) raises an OutOfMemoryError: ```0x4e6472 exit ??:0 0x4e6428 __run_exit_handlers ??:0 0x4df976 __libc_csu_fini ??:0 0x40327e ldc.register_dso crtstuff.c:0 0x4caee4 _d_dso_registry ??:0 0x4ccdba _D2rt4util9container6common8xreallocFNbNiPvmZPv ??:0 0x4b873d onOutOfMemoryError ??:0``` I tried to call Runtime.initialize() and Runtime.terminate() surrounding the call to entryPoint(), but this didn't help. I suspect calling initialize() was a no-op since the forked process shares (copy-on-write) the VM space with it's parent, that already initialized the runtime. (Note: confirmed, initialize() returns true indicating it was already inited). What is the correct way to handle fork() with the D runtime?
May 15 2017
This is how I use fork on one of my project http://gitlab.eurosat.cz/ekj/esatd/raw/master/source/app.d On Mon, May 15, 2017 at 1:33 PM, Jonathan Shamir via Digitalmars-d < digitalmars-d puremagic.com> wrote:Hey, This is my first time writing in the D forums! I have an application written in D that runs as a linux daemon (some python service script is in charge of running and daemonizing it). This "agent" works similar to docker - a service that accepts commands and runs in the background, and starts our application container. The container is itself a daemon (ppid = 1) with unshared namespaces etc. So, normally, implementing such an application would look something like: 1. Main "agent" process runs fork() to create a child process (child_1). All memory is copy-on-write. 2. Child_1 malloc()s a stack, and calls clone() to create yet another child (child_2), which will eventually become the container pid 1. 3. Child_2 initializes the container (mounts, unshare, chroot, etc) then eventually exec()s into the container init process. 4. child_1 exit()s, which causes child_2 to become a daemon. 5. The agent main process should wait() on the forked pid since it's impolite to leave zombies (I do this in a thread). The problem I encounter is with the forked process (child_1). Here is the code I wrote handling the fork() (Note: this functionality should really be provided by core.threads or something, for unix environments). ```private void deferToForkProcess(int delegate() entryPoint, Timeout timeout = Timeout.infinite) { import core.runtime : Runtime; import core.sys.posix.unistd : fork, pid_t; import core.sys.posix.sys.wait; import core.stdc.stdlib : exit; int rc = theReactor.deferToThread({ pid_t pid = fork(); errnoEnforce(pid >= 0, "Fork failed"); // child process if (pid == 0) { try { int rc = entryPoint(); exit(rc); } catch (Throwable ex) { try { LOG_ERROR(ex.toString); } catch (Throwable) {} exit(1); } //assert(false); } // parent - wait for child to exit int status = 0; do { errnoEnforce(waitpid(pid, &status, 0) != -1, "Waitpid failed"); } while (!WIFEXITED(status)); int rc = WEXITSTATUS(status); return rc; }, timeout); enforce(rc == 0, "Child process failed (rc = %d)".format(rc)); } ``` entryPoint() returns 0, but the exit(0) raises an OutOfMemoryError: ```0x4e6472 exit ??:0 0x4e6428 __run_exit_handlers ??:0 0x4df976 __libc_csu_fini ??:0 0x40327e ldc.register_dso crtstuff.c:0 0x4caee4 _d_dso_registry ??:0 0x4ccdba _D2rt4util9container6common8xreallocFNbNiPvmZPv ??:0 0x4b873d onOutOfMemoryError ??:0``` I tried to call Runtime.initialize() and Runtime.terminate() surrounding the call to entryPoint(), but this didn't help. I suspect calling initialize() was a no-op since the forked process shares (copy-on-write) the VM space with it's parent, that already initialized the runtime. (Note: confirmed, initialize() returns true indicating it was already inited). What is the correct way to handle fork() with the D runtime?
May 15 2017
OK well, seems like calling _exit() (instead of exit()) did the trick. Thanks for the help!
May 15 2017
D runtime is incompatible with fork, enforce and catch won't work in the child process after fork, stick with betterC style.
May 16 2017
On Monday, 15 May 2017 at 11:33:29 UTC, Jonathan Shamir wrote:Hey, This is my first time writing in the D forums! I have an application written in D that runs as a linux daemon (some python service script is in charge of running and daemonizing it). This "agent" works similar to docker - a service that accepts commands and runs in the background, and starts our application container. The container is itself a daemon (ppid = 1) with unshared namespaces etc. So, normally, implementing such an application would look something like: 1. Main "agent" process runs fork() to create a child process (child_1). All memory is copy-on-write. 2. Child_1 malloc()s a stack, and calls clone() to create yet another child (child_2), which will eventually become the container pid 1. 3. Child_2 initializes the container (mounts, unshare, chroot, etc) then eventually exec()s into the container init process. 4. child_1 exit()s, which causes child_2 to become a daemon. 5. The agent main process should wait() on the forked pid since it's impolite to leave zombies (I do this in a thread). The problem I encounter is with the forked process (child_1). Here is the code I wrote handling the fork() (Note: this functionality should really be provided by core.threads or something, for unix environments). ```private void deferToForkProcess(int delegate() entryPoint, Timeout timeout = Timeout.infinite) { import core.runtime : Runtime; import core.sys.posix.unistd : fork, pid_t; import core.sys.posix.sys.wait; import core.stdc.stdlib : exit; int rc = theReactor.deferToThread({ pid_t pid = fork(); errnoEnforce(pid >= 0, "Fork failed"); // child process if (pid == 0) { try { int rc = entryPoint(); exit(rc); } catch (Throwable ex) { try { LOG_ERROR(ex.toString); } catch (Throwable) {} exit(1); } //assert(false); } // parent - wait for child to exit int status = 0; do { errnoEnforce(waitpid(pid, &status, 0) != -1, "Waitpid failed"); } while (!WIFEXITED(status)); int rc = WEXITSTATUS(status); return rc; }, timeout); enforce(rc == 0, "Child process failed (rc = %d)".format(rc)); } ``` entryPoint() returns 0, but the exit(0) raises an OutOfMemoryError: ```0x4e6472 exit ??:0 0x4e6428 __run_exit_handlers ??:0 0x4df976 __libc_csu_fini ??:0 0x40327e ldc.register_dso crtstuff.c:0 0x4caee4 _d_dso_registry ??:0 0x4ccdba _D2rt4util9container6common8xreallocFNbNiPvmZPv ??:0 0x4b873d onOutOfMemoryError ??:0``` I tried to call Runtime.initialize() and Runtime.terminate() surrounding the call to entryPoint(), but this didn't help. I suspect calling initialize() was a no-op since the forked process shares (copy-on-write) the VM space with it's parent, that already initialized the runtime. (Note: confirmed, initialize() returns true indicating it was already inited). What is the correct way to handle fork() with the D runtime?Some links to related discussions: https://issues.dlang.org/show_bug.cgi?id=14205 http://forum.dlang.org/thread/ksqubftqniwznqbmurrk forum.dlang.org https://issues.dlang.org/show_bug.cgi?id=14770 https://issues.dlang.org/show_bug.cgi?id=16006 https://github.com/dlang/phobos/pull/4294 https://github.com/dlang/druntime/pull/1569
May 21 2017