Skip to content

Commit

Permalink
- Fix some crasher bugs in DispatcherBucket.
Browse files Browse the repository at this point in the history
- Fix wrong timeout calculation in DispatcherBucket.
- Add a DummyRequestHandler, which is probably the fastest possible request handler implementation. This, together with DummySpawnManager, allows one to benchmark the Apache module's performance.
  • Loading branch information
FooBarWidget committed Feb 3, 2008
1 parent 456b4c1 commit a06160d
Show file tree
Hide file tree
Showing 10 changed files with 233 additions and 22 deletions.
54 changes: 54 additions & 0 deletions benchmark/DummyRequestHandler.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#include "MessageChannel.h"
#include "Utils.cpp"
#include <vector>

using namespace std;
using namespace Passenger;

static bool
handleRequest(MessageChannel &reader, MessageChannel &writer) {
vector<string> headers;

P_TRACE("Reading request headers");
if (!reader.read(headers)) {
return true;
}
P_TRACE("Done reading request headers");

string content("<b>Using C++ DummyRequestHandler</b><br>\n");
unsigned int i;
for (i = 0; i < headers.size(); i += 2) {
content += "<tt>";
content += headers[i];
content += " = ";
content += headers[i + 1];
content += "</tt><br>\n";
}

string header(
"Status: 200 OK\r\n"
"Content-Type: text/html\r\n"
"Content-Length: "
);
header += toString(content.size());
header += "\r\n\r\n";
P_TRACE("Sending response header");
writer.writeScalar(header);
P_TRACE("Sending response content");
writer.writeScalar(content);
P_TRACE("All done");
writer.writeScalar("", 0);
return false;
}

int
main() {
MessageChannel reader(STDIN_FILENO);
MessageChannel writer(STDOUT_FILENO);
bool done = false;
initDebugging();
while (!done) {
done = handleRequest(reader, writer);
}
return 0;
}
5 changes: 5 additions & 0 deletions benchmark/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
CXX=g++
CXXFLAGS=-Wall -g -I../ext/apache2 `pkg-config --cflags apr-1`

DummyRequestHandler: DummyRequestHandler.cpp ../ext/apache2/MessageChannel.h
$(CXX) $(CXXFLAGS) -o DummyRequestHandler DummyRequestHandler.cpp
2 changes: 0 additions & 2 deletions ext/apache2/Application.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,13 @@ class Application {
if (reader != -1) {
close(reader);
reader = -1;
P_TRACE("Application " << this << ": reader closed.");
}
}

void closeWriter() {
if (writer != -1) {
close(writer);
writer = -1;
P_TRACE("Application " << this << ": writer closed.");
}
}
};
Expand Down
17 changes: 14 additions & 3 deletions ext/apache2/ApplicationPool.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@
#include <string>
#include <map>

#include "SpawnManager.h"
#ifdef PASSENGER_USE_DUMMY_SPAWN_MANAGER
#include "DummySpawnManager.h"
#else
#include "SpawnManager.h"
#endif

namespace Passenger {

Expand All @@ -26,7 +30,11 @@ class StandardApplicationPool: public ApplicationPool {
private:
typedef map<string, ApplicationPtr> ApplicationMap;

SpawnManager spawnManager;
#ifdef PASSENGER_USE_DUMMY_SPAWN_MANAGER
DummySpawnManager spawnManager;
#else
SpawnManager spawnManager;
#endif
ApplicationMap apps;
mutex lock;
bool threadSafe;
Expand All @@ -41,7 +49,10 @@ class StandardApplicationPool: public ApplicationPool {
const string &logFile = "",
const string &environment = "production",
const string &rubyCommand = "ruby")
: spawnManager(spawnManagerCommand, logFile, environment, rubyCommand) {
#ifndef PASSENGER_USE_DUMMY_SPAWN_MANAGER
: spawnManager(spawnManagerCommand, logFile, environment, rubyCommand)
#endif
{
threadSafe = false;
}

Expand Down
1 change: 1 addition & 0 deletions ext/apache2/ApplicationPoolClientServer.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <unistd.h>

#include "ApplicationPool.h"
#include "MessageChannel.h"
#include "Exceptions.h"
#include "Utils.h"

Expand Down
18 changes: 14 additions & 4 deletions ext/apache2/DispatcherBucket.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#include <string>
#include <algorithm>

#include <poll.h>
#include <errno.h>
#include <unistd.h>
Expand Down Expand Up @@ -67,10 +69,10 @@ class DispatcherBucket {
return errno_to_apr_status(errno);
}
}

apr_time_t begin = apr_time_now();
tmp = ::read(pipe, (char *) buffer + already_read, size - already_read);
timeout -= apr_time_now() - begin;
timeout = max((apr_time_t) 0, timeout - (apr_time_now() - begin));
if (tmp > 0) {
// Data has been read.
already_read += tmp;
Expand Down Expand Up @@ -141,7 +143,11 @@ class DispatcherBucket {
*str = (const char *) b->data;
return APR_SUCCESS;
} else if (result != APR_SUCCESS) {
P_TRACE("DispatcherBucket " << this << ": APR error " << result);
char buf[1024];
P_TRACE("DispatcherBucket " << this << ": APR error " << result
<< ": " << apr_strerror(result, buf, sizeof(buf)));
b = apr_bucket_immortal_make(b, "", 0);
*str = (const char *) b->data;
return result;
}

Expand All @@ -162,7 +168,11 @@ class DispatcherBucket {
*str = (const char *) b->data;
return APR_SUCCESS;
} else {
P_TRACE("DispatcherBucket " << this << ": APR error " << result);
char buf[1024];
P_TRACE("DispatcherBucket " << this << ": APR error " << result
<< ": " << apr_strerror(result, buf, sizeof(buf)));
b = apr_bucket_immortal_make(b, "", 0);
*str = (const char *) b->data;
return result;
}
}
Expand Down
69 changes: 69 additions & 0 deletions ext/apache2/DummySpawnManager.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#ifndef _PASSENGER_DUMMY_SPAWN_MANAGER_H_
#define _PASSENGER_DUMMY_SPAWN_MANAGER_H_

#define DUMMY_REQUEST_HANDLER_EXECUTABLE "/home/hongli/Projects/mod_rails/benchmark/DummyRequestHandler"

#include <string>

#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#include "Application.h"

namespace Passenger {

using namespace std;

/**
* This class implements a dummy spawn manager. This spawn manager will spawn
* benchmark/DummyRequestHandler, which is probably the fastest possible
* implementation of a request handler. The purpose of this class to benchmark
* the performance of the Apache module (i.e. not benchmarking the Ruby request
* handler or Rails itself).
*
* This header file is not used by default. Modify ApplicationPool to make use
* of this file/class.
*
* Of course, don't forget to compile benchmark/DummyRequestHandler before you
* use this class!
*/
class DummySpawnManager {
public:
ApplicationPtr spawn(const string &appRoot, const string &user = "", const string &group = "") {
int fd1[2], fd2[2];
pid_t pid;

pipe(fd1);
pipe(fd2);
pid = fork();
if (pid == 0) {
pid = fork();
if (pid == 0) {
dup2(fd1[0], 0);
dup2(fd2[1], 1);
close(fd1[0]);
close(fd1[1]);
close(fd2[0]);
close(fd2[1]);
execlp(DUMMY_REQUEST_HANDLER_EXECUTABLE, DUMMY_REQUEST_HANDLER_EXECUTABLE, NULL);
_exit(1);
} else {
_exit(0);
}
} else {
int reader, writer;

close(fd1[0]);
close(fd2[1]);
reader = fd2[0];
writer = fd1[1];
waitpid(pid, NULL, 0);
return ApplicationPtr(new Application(appRoot, pid, reader, writer));
}
}
};

} // namespace Passenger

#endif /* _PASSENGER_DUMMY_SPAWN_MANAGER_H_ */
10 changes: 9 additions & 1 deletion ext/apache2/Hooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -260,11 +260,17 @@ class Hooks {
return httpStatus;
} */

/*
* TODO: fix these bugs:
* - If the request handler dies, it does not get removed from the application pool. It should.
* - If Apache dies, then the request handler's protocol state is left in an inconsistent state.
*/

try {
apr_bucket_brigade *bb;
apr_bucket *b;

P_DEBUG("Processing HTTP request on process " << getpid() << ": " << r->uri);
P_DEBUG("Processing HTTP request: " << r->uri);
ApplicationPtr app(applicationPool->get(string(railsDir) + "/.."));
P_TRACE("Connected to application: reader FD = " << app->getReader() << ", writer FD = " << app->getWriter());
sendHeaders(r, app->getWriter());
Expand All @@ -279,6 +285,8 @@ class Hooks {

ap_scan_script_header_err_brigade(r, bb, NULL);
ap_pass_brigade(r->output_filters, bb);

P_TRACE("Dispatcher brigade passed to output filters");

return OK;
} catch (const exception &e) {
Expand Down
2 changes: 1 addition & 1 deletion ext/apache2/Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
APXS=apxs2
APACHECTL=apache2ctl
CXX=g++
CXXFLAGS=-Wall -fPIC -g -DPASSENGER_DEBUG `pkg-config --cflags apr-1 apr-util-1` `$(APXS) -q CFLAGS` -I`$(APXS) -q INCLUDEDIR`
CXXFLAGS=-Wall -fPIC -g -DPASSENGER_DEBUG -DPASSENGER_USE_DUMMY_SPAWN_MANAGER `pkg-config --cflags apr-1 apr-util-1` `$(APXS) -q CFLAGS` -I`$(APXS) -q INCLUDEDIR`
OBJECTS=Configuration.o Hooks.o DispatcherBucket.o Utils.o
LIBTOOL_OBJECTS=Configuration.o,Hooks.o,DispatcherBucket.o,Utils.o

Expand Down
Loading

0 comments on commit a06160d

Please sign in to comment.