0
@@ -23,8 +23,27 @@ using namespace std;
0
- * This class is responsible for spawning Ruby on Rails applications.
0
- * TODO: write better documentation
0
+ * This class is responsible for spawning new instances of Ruby on Rails applications.
0
+ * Use the spawn() method to do so.
0
+ * <h2>Implementation details</h2>
0
+ * Internally, it makes use of a spawn server, which is written in Ruby. This server
0
+ * is automatically started when a SpawnManager instance is created, and automatically
0
+ * shutdown when that instance is destroyed. Spawning requests are sent to the server,
0
+ * and details about the spawned process is returned.
0
+ * The communication channel with the server is anonymous, i.e. no other processes
0
+ * can access the communication channel, so communication is guaranteed to be safe
0
+ * (unless, of course, if the spawn server itself is a trojan).
0
+ * The server will try to keep the spawning time as small as possible, by keeping
0
+ * corresponding Ruby on Rails frameworks and application code in memory. So the second
0
+ * time an instance of the same application is spawned, the spawn time is significantly
0
+ * lower than the first time. Nevertheless, spawning is a relatively expensive operation
0
+ * (compared to the processing of a typical HTTP request/response), and so should be
0
+ * avoided whenever possible.
0
+ * See the documentation of the spawn server for full implementation details.
0
@@ -32,20 +51,33 @@ private:
0
- SpawnManager(const string &spawnManagerCommand,
0
+ * Construct a new SpawnManager.
0
+ * @param spawnServerCommand The filename of the spawn server to use.
0
+ * @param logFile Specify a log file that the spawn manager should use.
0
+ * Messages on its standard output and standard error channels
0
+ * will be written to this log file. If an empty string is
0
+ * specified, no log file will be used, and the spawn server
0
+ * will use the same standard output/error channels as the
0
+ * @param environment The RAILS_ENV environment that all RoR applications
0
+ * should use. If an empty string is specified, the current value
0
+ * of the RAILS_ENV environment variable will be used.
0
+ * @param rubyCommand The Ruby interpreter's command.
0
+ * @param SystemException An error occured while trying to setup the spawn server.
0
+ * @throws IOException The specified log file could not be opened.
0
+ SpawnManager(const string &spawnServerCommand,
0
const string &logFile = "",
0
const string &environment = "production",
0
const string &rubyCommand = "ruby") {
0
FILE *logFileHandle = NULL;
0
if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == -1) {
0
throw SystemException("Cannot create a Unix socket", errno);
0
- if (apr_snprintf(fd_string, sizeof(fd_string), "%d", fds[1]) <= 0) {
0
- throw MemoryException();
0
if (!logFile.empty()) {
0
logFileHandle = fopen(logFile.c_str(), "a");
0
if (logFileHandle == NULL) {
0
@@ -58,19 +90,28 @@ public:
0
- // TODO: redirect stderr to a log file
0
if (!logFile.empty()) {
0
- dup2(fileno(logFileHandle), STDOUT_FILENO);
0
dup2(fileno(logFileHandle), STDERR_FILENO);
0
+ dup2(STDERR_FILENO, STDOUT_FILENO);
0
if (!environment.empty()) {
0
setenv("RAILS_ENV", environment.c_str(), true);
0
+ dup2(fds[1], STDIN_FILENO);
0
- execlp(rubyCommand.c_str(), rubyCommand.c_str(), spawnManagerCommand.c_str(), fd_string, NULL);
0
+ // Close all other file descriptors
0
+ for (int i = sysconf(_SC_OPEN_MAX); i >= 0; i--) {
0
+ if (i != STDIN_FILENO && i != STDOUT_FILENO && i != STDERR_FILENO) {
0
+ execlp(rubyCommand.c_str(), rubyCommand.c_str(), spawnServerCommand.c_str(), NULL);
0
fprintf(stderr, "Unable to run %s: %s\n", rubyCommand.c_str(), strerror(errno));
0
} else if (pid == -1) {
0
@@ -82,6 +123,9 @@ public:
0
} else if (pid == -1) {
0
+ if (logFileHandle != NULL) {
0
+ fclose(logFileHandle);
0
throw SystemException("Unable to fork a process", errno);
0
@@ -93,7 +137,7 @@ public:
0
+ ~SpawnManager()
throw() {
Comments
No one has commented yet.