src/worker/wkr_main.c in webroar-0.2.2 vs src/worker/wkr_main.c in webroar-0.2.3
- old
+ new
@@ -1,7 +1,7 @@
/* WebROaR - Ruby Application Server - http://webroar.in/
- * Copyright (C) 2009 WebROaR
+ * Copyright (C) 2009 Goonj LLC
*
* This file is part of WebROaR.
*
* WebROaR is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -19,14 +19,20 @@
#include <assert.h>
#include <string.h>
#include <worker.h>
#include <ruby.h>
+#include <rubysig.h>
+#include <execinfo.h>
+#ifdef __linux__
+#include <sys/prctl.h>
+#endif
-//static wkr_t *worker = NULL;
+static wkr_t *worker = NULL;
struct ev_loop *loop; // Event loop
+struct ev_idle idle_watcher;
int is_alive = 1;
static int drop_privileges(wkr_t *w) {
change_log_file_owner(w->tmp->uid, w->tmp->gid);
//setting read, effective, saved group and user id
@@ -40,10 +46,17 @@
}
LOG_DEBUG(DEBUG,"Passed userid=%d and groupid=%d",
w->tmp->uid, w->tmp->gid);
LOG_DEBUG(DEBUG,"effective userid=%d and groupid=%d",geteuid(),getegid());
+#ifdef __linux__
+ int rv = prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
+ LOG_DEBUG(DEBUG,"prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) = %d", rv);
+ if (rv < 0) {
+ LOG_ERROR(SEVERE,"error setting prctl(PR_SET_DUMPABLE, 1, 0, 0, 0), errno = %d, desc = %s", errno, strerror(errno));
+ }
+#endif
return 0;
}
/** Usage */
static inline void print_usage(char *appname) {
@@ -66,10 +79,71 @@
// file_log("/tmp/too_many_worker.log","Webroar-Worker of %s getting close\n", worker->tmp->name.str);
LOG_DEBUG(4,"**************Caught Interrupt Signal************");
is_alive = 0;
}
+void cleanup() {
+ LOG_FUNCTION
+ stop_idle_watcher();
+ LOG_DEBUG(DEBUG,"stoping event loop");
+ ev_unloop(loop,EVUNLOOP_ALL);
+ //TODO: send worker stopping signal
+ worker_free(&worker);
+ LOG_INFO("Worker stopped and exiting gracefully.");
+ close_logger();
+ exit(0);
+}
+
+/** Handle segmentation fault */
+void crash_handler(int sig) {
+ void *array[WR_STACKTRACE_SIZE];
+ size_t size;
+ char **bt_symbols;
+ char bt_string[WR_STACKTRACE_SIZE * WR_LONG_STR_LEN * 2];
+ int i;
+
+ LOG_ERROR(FATAL, "Got %d signal, trying to create core file.", sig);
+ signal(sig, SIG_DFL);
+ //kill(getpid(), sig);
+ if(fork() == 0) { // child
+ char cmd[64], core_file_name[48], timestamp[24];
+ int rv;
+ signal(SIGCHLD, SIG_DFL);
+ //TODO: add application name
+ sprintf(core_file_name, "/tmp/webroar-worker");
+ if ( get_timestamp(timestamp) == 0 ) {
+ strcat(core_file_name, "-");
+ strcat(core_file_name, timestamp);
+ }
+#ifdef __APPLE__
+ sprintf(cmd, "gcore -c %s %ld", core_file_name, (long) getppid() );
+#else
+ sprintf(cmd, "gcore -o %s %ld", core_file_name, (long) getppid() );
+#endif
+ rv = system(cmd);
+ if ( rv < 0 ) {
+ LOG_ERROR(FATAL, "Core file creation failed, gcore might be missing... rv = %d, errno = %d, error = %s", rv, errno, strerror(errno));
+ } else {
+ LOG_INFO("Core file - %s created", core_file_name);
+ }
+ exit(0);
+ }
+ sleep(5);
+ // get void*'s for all entries on the stack
+ size = backtrace(array, WR_STACKTRACE_SIZE);
+ bt_symbols = backtrace_symbols(array, size);
+ strcpy(bt_string, "\n");
+ for(i = 0; i < size; i++) {
+ strcat(bt_string, bt_symbols[i]);
+ strcat(bt_string, "\n");
+ }
+ LOG_ERROR(FATAL, "Obtained %zd stack frames.%s", size, bt_string);
+ free(bt_symbols);
+ //TODO: carefully dump worker state, current request
+ cleanup();
+}
+
/** Parse command line arguments */
static inline wkr_tmp_t* parse_args(int argc, char **argv) {
int option;
extern char *optarg;
size_t len;
@@ -156,35 +230,81 @@
if (invalid_arg_flag>0 || app_path_flag == 0) {
print_usage(argv[0]);
wkr_tmp_free(&tmp);
return NULL;
}
+ if(strcmp(tmp->name.str, WR_STATIC_FILE_SERVER_NAME) == 0){
+ tmp->is_static = 1;
+ }
return tmp;
}
+void start_idle_watcher() {
+ LOG_FUNCTION
+ if(!ev_is_active(&idle_watcher)) {
+ ev_idle_start (loop, &idle_watcher);
+ }
+}
+
+void stop_idle_watcher() {
+ LOG_FUNCTION
+ ev_idle_stop(loop, &idle_watcher);
+}
+
+void idle_cb (struct ev_loop *loop, struct ev_idle *w, int revents) {
+ LOG_FUNCTION
+ /* Calling libev's blocking call ev_loop() between TRAP_* macros were working on Ruby 1.8,
+ * but didn't worked on Ruby 1.9, looks we need to schedule ruby threads on our own
+ */
+ if(rb_thread_alone()) {
+ /* Stop scheduling ruby threads, there is only one! */
+ stop_idle_watcher();
+ } else {
+ /* TODO: Found following three api to schedule ruby threads
+ * rb_thread_schedule() was getting called infinitely and eating most of the CPU.
+ * rb_thread_polling() was getting called, for approximately 16 times in a second on 1.8
+ * and 10 times on 1.9
+ * rb_thread_select() takes delay(in struct timeval) as last argument, useful to control
+ * number of call in a second, but need to checkout best value for that argument
+ * Ruby 1.9 have rb_thread_blocking_region() to execute C blocking call, need to explore it
+ * Checkout the best strategy to combine ruby thread scheduling with libev.
+ * Currently rb_thread_polling looks suitable to use */
+ rb_thread_polling();
+ //rb_thread_schedule();
+ }
+}
+
int main(int argc, char **argv) {
int port, retval = 0;
wkr_t* w = NULL;
if(argc==1) {
print_usage(argv[0]);
return -1;
}
+
+ signal(SIGSEGV, crash_handler); /* set our handler for segfault */
+// signal(SIGQUIT, crash_handler);
+// signal(SIGILL, crash_handler);
+// signal(SIGABRT, crash_handler);
+// signal(SIGFPE, crash_handler);
+
wkr_tmp_t *tmp = parse_args(argc, argv);
if(tmp == NULL)
return -1;
loop = ev_default_loop (0);
w = worker_new(loop, tmp);
if(w==NULL)
goto err;
- // worker = w;
+ worker = w;
// assert(w!=NULL);
initialize_logger(w->tmp->log_file.str);
-
+ redirect_standard_io();
+
LOG_DEBUG(DEBUG,"control path = %s, Application baseuri = %s",
w->tmp->ctl_path.str, w->tmp->resolver.str);
if((retval = drop_privileges(w))!=0) {
goto err;
@@ -201,16 +321,10 @@
LOG_ERROR(WARN,"Error Initializing Workers.");
retval = -1;
goto err;
}
- // w->http = http_new(w);
- // if(w->http == NULL)
- // {
- // goto err;
- // }
-
//loading adapter according to application type
LOG_DEBUG(DEBUG,"ruby lib = %s and webroar_root = %s",
w->tmp->ruby_path.str, w->tmp->root_path.str);
LOG_DEBUG(DEBUG,"path = %s, name = %s, type = %s, environment = %s, baseuri = %s, analytics = %c",
w->tmp->path.str, w->tmp->name.str, w->tmp->type.str,
@@ -218,31 +332,33 @@
LOG_INFO("Successfully loaded rack application=%s with environment=%s",
w->tmp->path.str, w->tmp->env.str);
//TODO: Windows Portability?
- signal(SIGHUP, SIG_IGN); /* catch hangup signal */
- signal(SIGINT, SIG_IGN);
- signal(SIGTERM, SIG_IGN);
+ signal(SIGHUP, sigproc); /* catch hangup signal */
+ signal(SIGINT, sigproc);
+ signal(SIGTERM, sigproc);
signal(SIGCHLD, SIG_IGN);
signal(SIGTSTP, SIG_IGN);
signal(SIGTTOU, SIG_IGN);
- signal(SIGTTIN, SIG_IGN);
- signal(SIGKILL, sigproc);
+ signal(SIGTTIN, SIG_IGN);
signal(SIGPIPE, SIG_IGN);
worker_accept_requests(w);
LOG_INFO("Worker ready for serving requests.");
+ if(!w->http->is_static){
+ ev_idle_init (&idle_watcher, idle_cb);
+ start_idle_watcher();
+ }
+
while(is_alive ==1) {
+ /* TODO: wrapping ev_loop() between TARP_* macros didn't worked in Ruby 1.9 */
+ //TRAP_BEG;
ev_loop(loop,EVLOOP_ONESHOT);
+ //TRAP_END;
}
err:
- LOG_DEBUG(DEBUG,"stoping event loop");
- ev_unloop(loop,EVUNLOOP_ALL);
- //TODO: send worker stopping signal
- worker_free(&w);
- LOG_INFO("Worker stopped and exiting gracefully.");
- close_logger();
+ cleanup();
return retval;
}