| /* | 
 |  * Copyright 2017 Google Inc. | 
 |  * | 
 |  * Use of this source code is governed by a BSD-style license that can be | 
 |  * found in the LICENSE file. | 
 |  */ | 
 |  | 
 | #include "ok.h" | 
 | #include <stdlib.h> | 
 |  | 
 | struct SerialEngine : Engine { | 
 |     static std::unique_ptr<Engine> Factory(Options) { | 
 |         SerialEngine engine; | 
 |         return move_unique(engine); | 
 |     } | 
 |  | 
 |     bool crashproof() override { return false; } | 
 |  | 
 |     std::future<Status> spawn(std::function<Status(void)> fn) override { | 
 |         return std::async(std::launch::deferred, fn); | 
 |     } | 
 | }; | 
 | static Register serial("serial", | 
 |                        "Run tasks serially on the main thread of a single process.", | 
 |                        SerialEngine::Factory); | 
 |  | 
 | struct ThreadEngine : Engine { | 
 |     static std::unique_ptr<Engine> Factory(Options) { | 
 |         ThreadEngine engine; | 
 |         return move_unique(engine); | 
 |     } | 
 |  | 
 |     bool crashproof() override { return false; } | 
 |  | 
 |     std::future<Status> spawn(std::function<Status(void)> fn) override { | 
 |         return std::async(std::launch::async, fn); | 
 |     } | 
 | }; | 
 | static Register thread("thread", | 
 |                        "Run each task on its own thread of a single process.", | 
 |                        ThreadEngine::Factory); | 
 |  | 
 | #if !defined(_MSC_VER) | 
 |     #include <sys/wait.h> | 
 |     #include <unistd.h> | 
 |  | 
 |     struct ForkEngine : Engine { | 
 |         int limit;      // How many concurrent subprocesses do we allow to run at max? | 
 |         int alive = 0;  // How many concurrent subprocesses do we have running right now? | 
 |  | 
 |         static std::unique_ptr<Engine> Factory(Options options) { | 
 |             ForkEngine engine; | 
 |             engine.limit = atoi(options("limit", "0").c_str()); | 
 |             if (engine.limit < 1) { | 
 |                 engine.limit = std::thread::hardware_concurrency(); | 
 |             } | 
 |             return move_unique(engine); | 
 |         } | 
 |  | 
 |         bool crashproof() override { return true; } | 
 |  | 
 |         std::future<Status> spawn(std::function<Status(void)> fn) override { | 
 |             if (alive == limit) { | 
 |                 // The caller will wait for a child process to finish then try again. | 
 |                 return std::future<Status>(); | 
 |             } | 
 |  | 
 |             switch (fork()) { | 
 |                 case  0: | 
 |                     // We are the spawned child process. | 
 |                     // Run fn() and exit() with its Status as our return code. | 
 |                     _exit((int)fn()); | 
 |  | 
 |                 case -1: | 
 |                     // The OS won't let us fork() another process right now. | 
 |                     // We'll need to wait for at least one live task to finish and try again. | 
 |                     return std::future<Status>(); | 
 |  | 
 |                 default: | 
 |                     // We succesfully spawned a child process! | 
 |                     // This will wait for any spawned process to finish and return its Status. | 
 |                     alive++; | 
 |                     return std::async(std::launch::deferred, [&] { | 
 |                         do { | 
 |                             int status; | 
 |                             if (wait(&status) > 0) { | 
 |                                 alive--; | 
 |                                 return WIFEXITED(status) ? (Status)WEXITSTATUS(status) | 
 |                                                          : Status::Crashed; | 
 |                             } | 
 |                         } while (errno == EINTR); | 
 |                         return Status::None; | 
 |                     }); | 
 |             } | 
 |         } | 
 |     }; | 
 |     static Register _fork("fork", | 
 |                           "Run each task in an independent process with fork(), limit=ncpus.", | 
 |                           ForkEngine::Factory); | 
 | #endif |