diff --git a/BUILD b/BUILD
index f7136e4b846ad549435eb9d37e57865a60667af6..11285c2576a238ee3d9a4a56da3adf5e4da8bbff 100644
--- a/BUILD
+++ b/BUILD
@@ -74,6 +74,7 @@ cc_library(
     "src/core/support/string.c",
     "src/core/support/string_posix.c",
     "src/core/support/string_win32.c",
+    "src/core/support/subprocess_posix.c",
     "src/core/support/sync.c",
     "src/core/support/sync_posix.c",
     "src/core/support/sync_win32.c",
diff --git a/Makefile b/Makefile
index fa954849100cca019b546b2ae70df7111dc842e0..e44f030cefa98b084ed51f7fff05bbbb17b59866 100644
--- a/Makefile
+++ b/Makefile
@@ -2313,6 +2313,7 @@ LIBGPR_SRC = \
     src/core/support/string.c \
     src/core/support/string_posix.c \
     src/core/support/string_win32.c \
+    src/core/support/subprocess_posix.c \
     src/core/support/sync.c \
     src/core/support/sync_posix.c \
     src/core/support/sync_win32.c \
diff --git a/build.json b/build.json
index 39e61d2822a8f353133cf52f0a7cf7381b0daac7..4c800a2740194338566dede950f38465d2a14645 100644
--- a/build.json
+++ b/build.json
@@ -362,6 +362,7 @@
         "src/core/support/string.c",
         "src/core/support/string_posix.c",
         "src/core/support/string_win32.c",
+        "src/core/support/subprocess_posix.c",
         "src/core/support/sync.c",
         "src/core/support/sync_posix.c",
         "src/core/support/sync_win32.c",
diff --git a/include/grpc/support/port_platform.h b/include/grpc/support/port_platform.h
index df7861c7b6db81411d42033f0cac38488ffa1dd0..a695acf2056a58571bb8c77d214b6a6b59bdf0c1 100644
--- a/include/grpc/support/port_platform.h
+++ b/include/grpc/support/port_platform.h
@@ -77,6 +77,7 @@
 #define GPR_POSIX_ENV 1
 #define GPR_POSIX_FILE 1
 #define GPR_POSIX_STRING 1
+#define GPR_POSIX_SUBPROCESS 1
 #define GPR_POSIX_SYNC 1
 #define GPR_POSIX_TIME 1
 #define GPR_GETPID_IN_UNISTD_H 1
@@ -122,6 +123,7 @@
 #endif
 #define GPR_POSIX_FILE 1
 #define GPR_POSIX_STRING 1
+#define GPR_POSIX_SUBPROCESS 1
 #define GPR_POSIX_SYNC 1
 #define GPR_POSIX_TIME 1
 #define GPR_GETPID_IN_UNISTD_H 1
@@ -154,6 +156,7 @@
 #define GPR_POSIX_ENV 1
 #define GPR_POSIX_FILE 1
 #define GPR_POSIX_STRING 1
+#define GPR_POSIX_SUBPROCESS 1
 #define GPR_POSIX_SYNC 1
 #define GPR_POSIX_TIME 1
 #define GPR_GETPID_IN_UNISTD_H 1
@@ -180,6 +183,7 @@
 #define GPR_POSIX_ENV 1
 #define GPR_POSIX_FILE 1
 #define GPR_POSIX_STRING 1
+#define GPR_POSIX_SUBPROCESS 1
 #define GPR_POSIX_SYNC 1
 #define GPR_POSIX_TIME 1
 #define GPR_GETPID_IN_UNISTD_H 1
diff --git a/include/grpc/support/subprocess.h b/include/grpc/support/subprocess.h
new file mode 100644
index 0000000000000000000000000000000000000000..c59751da83e62ef84dcd824d6bbec6e62bcdd7fd
--- /dev/null
+++ b/include/grpc/support/subprocess.h
@@ -0,0 +1,49 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef GRPC_SUPPORT_SUBPROCESS_H
+#define GRPC_SUPPORT_SUBPROCESS_H
+
+typedef struct gpr_subprocess gpr_subprocess;
+
+/* .exe on windows, empty on unices */
+char *gpr_subprocess_binary_extension();
+
+gpr_subprocess *gpr_subprocess_create(int argc, char **argv);
+/* if subprocess has not been joined, kill it */
+void gpr_subprocess_destroy(gpr_subprocess *p);
+/* returns exit status; can be called at most once */
+int gpr_subprocess_join(gpr_subprocess *p);
+void gpr_subprocess_interrupt(gpr_subprocess *p);
+
+#endif
diff --git a/src/core/support/subprocess_posix.c b/src/core/support/subprocess_posix.c
new file mode 100644
index 0000000000000000000000000000000000000000..4580537aa855f9f560307bdd57370876e79f7e6e
--- /dev/null
+++ b/src/core/support/subprocess_posix.c
@@ -0,0 +1,108 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_SUBPROCESS
+
+#include <grpc/support/subprocess.h>
+
+#include <unistd.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+struct gpr_subprocess {
+  int pid;
+  int joined;
+};
+
+char *gpr_subprocess_binary_extension() { return ""; }
+
+gpr_subprocess *gpr_subprocess_create(int argc, char **argv) {
+  gpr_subprocess *r;
+  int pid;
+  char **exec_args;
+
+  pid = fork();
+  if (pid == -1) {
+    return NULL;
+  } else if (pid == 0) {
+    exec_args = gpr_malloc((argc + 1) * sizeof(char *));
+    memcpy(exec_args, argv, argc * sizeof(char *));
+    exec_args[argc] = NULL;
+    execv(exec_args[0], exec_args);
+    /* if we reach here, an error has occurred */
+    gpr_log(GPR_ERROR, "execv '%s' failed: %s", exec_args[0], strerror(errno));
+    _exit(1);
+    return NULL;
+  } else {
+    r = gpr_malloc(sizeof(gpr_subprocess));
+    memset(r, 0, sizeof(*r));
+    r->pid = pid;
+    return r;
+  }
+}
+
+void gpr_subprocess_destroy(gpr_subprocess *p) {
+  if (!p->joined) {
+    kill(p->pid, SIGKILL);
+    gpr_subprocess_join(p);
+  }
+  gpr_free(p);
+}
+
+int gpr_subprocess_join(gpr_subprocess *p) {
+  int status;
+  if (waitpid(p->pid, &status, 0) == -1) {
+    gpr_log(GPR_ERROR, "waitpid failed: %s", strerror(errno));
+    return -1;
+  }
+  return status;
+}
+
+void gpr_subprocess_interrupt(gpr_subprocess *p) {
+  if (!p->joined) {
+    kill(p->pid, SIGINT);
+  }
+}
+
+#endif /* GPR_POSIX_SUBPROCESS */
diff --git a/test/core/fling/fling_test.c b/test/core/fling/fling_test.c
index c0066cf1013953c4085655c826807cccc088ad0a..489f3a385c6473ecc8be674eb9a95361b812f353 100644
--- a/test/core/fling/fling_test.c
+++ b/test/core/fling/fling_test.c
@@ -31,20 +31,11 @@
  *
  */
 
-#ifndef _POSIX_SOURCE
-#define _POSIX_SOURCE
-#endif
-
-#include <unistd.h>
-#include <assert.h>
-#include <stdio.h>
 #include <string.h>
-#include <signal.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/wait.h>
+#include <stdio.h>
 
 #include <grpc/support/alloc.h>
+#include <grpc/support/subprocess.h>
 #include <grpc/support/host_port.h>
 #include "src/core/support/string.h"
 #include "test/core/util/port.h"
@@ -56,10 +47,7 @@ int main(int argc, char **argv) {
   int port = grpc_pick_unused_port_or_die();
   char *args[10];
   int status;
-  pid_t svr, cli;
-  /* seed rng with pid, so we don't end up with the same random numbers as a
-     concurrently running test binary */
-  srand(getpid());
+  gpr_subprocess *svr, *cli;
   /* figure out where we are */
   if (lslash) {
     memcpy(root, me, lslash - me);
@@ -68,45 +56,36 @@ int main(int argc, char **argv) {
     strcpy(root, ".");
   }
   /* start the server */
-  svr = fork();
-  if (svr == 0) {
-    gpr_asprintf(&args[0], "%s/fling_server", root);
-    args[1] = "--bind";
-    gpr_join_host_port(&args[2], "::", port);
-    args[3] = "--no-secure";
-    args[4] = 0;
-    execv(args[0], args);
+  gpr_asprintf(&args[0], "%s/fling_server", root);
+  args[1] = "--bind";
+  gpr_join_host_port(&args[2], "::", port);
+  args[3] = "--no-secure";
+  svr = gpr_subprocess_create(4, args);
+  gpr_free(args[0]);
+  gpr_free(args[2]);
 
-    gpr_free(args[0]);
-    gpr_free(args[2]);
-    return 1;
-  }
-  /* wait a little */
-  sleep(2);
   /* start the client */
-  cli = fork();
-  if (cli == 0) {
-    gpr_asprintf(&args[0], "%s/fling_client", root);
-    args[1] = "--target";
-    gpr_join_host_port(&args[2], "127.0.0.1", port);
-    args[3] = "--scenario=ping-pong-request";
-    args[4] = "--no-secure";
-    args[5] = 0;
-    execv(args[0], args);
+  gpr_asprintf(&args[0], "%s/fling_client", root);
+  args[1] = "--target";
+  gpr_join_host_port(&args[2], "127.0.0.1", port);
+  args[3] = "--scenario=ping-pong-request";
+  args[4] = "--no-secure";
+  args[5] = 0;
+  cli = gpr_subprocess_create(6, args);
+  gpr_free(args[0]);
+  gpr_free(args[2]);
 
-    gpr_free(args[0]);
-    gpr_free(args[2]);
-    return 1;
-  }
   /* wait for completion */
   printf("waiting for client\n");
-  if (waitpid(cli, &status, 0) == -1) return 2;
-  if (!WIFEXITED(status)) return 4;
-  if (WEXITSTATUS(status)) return WEXITSTATUS(status);
-  printf("waiting for server\n");
-  kill(svr, SIGINT);
-  if (waitpid(svr, &status, 0) == -1) return 2;
-  if (!WIFEXITED(status)) return 4;
-  if (WEXITSTATUS(status)) return WEXITSTATUS(status);
-  return 0;
+  if ((status = gpr_subprocess_join(cli))) {
+    gpr_subprocess_destroy(cli);
+    gpr_subprocess_destroy(svr);
+    return status;
+  }
+  gpr_subprocess_destroy(cli);
+
+  gpr_subprocess_interrupt(svr);
+  status = gpr_subprocess_join(svr);
+  gpr_subprocess_destroy(svr);
+  return status;
 }
diff --git a/vsprojects/gpr/gpr.vcxproj b/vsprojects/gpr/gpr.vcxproj
index e42c656fdcd54713ef270832f6910bf3ceeb9a88..46932aa151b16101db565c4c9eaf1fa8a2083214 100644
--- a/vsprojects/gpr/gpr.vcxproj
+++ b/vsprojects/gpr/gpr.vcxproj
@@ -234,6 +234,8 @@
     </ClCompile>
     <ClCompile Include="..\..\src\core\support\string_win32.c">
     </ClCompile>
+    <ClCompile Include="..\..\src\core\support\subprocess_posix.c">
+    </ClCompile>
     <ClCompile Include="..\..\src\core\support\sync.c">
     </ClCompile>
     <ClCompile Include="..\..\src\core\support\sync_posix.c">
diff --git a/vsprojects/gpr/gpr.vcxproj.filters b/vsprojects/gpr/gpr.vcxproj.filters
index 13fdb3fef84549df8a9da8b0b01db03702f757d2..ef0dfee3ba144383e434dfd0c02423c13cdd2d55 100644
--- a/vsprojects/gpr/gpr.vcxproj.filters
+++ b/vsprojects/gpr/gpr.vcxproj.filters
@@ -79,6 +79,9 @@
     <ClCompile Include="..\..\src\core\support\string_win32.c">
       <Filter>src\core\support</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\src\core\support\subprocess_posix.c">
+      <Filter>src\core\support</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\src\core\support\sync.c">
       <Filter>src\core\support</Filter>
     </ClCompile>