Roxen.git / server / base_server / roxen.pike

version» Context lines:

Roxen.git/server/base_server/roxen.pike:1:   // This file is part of Roxen WebServer.   // Copyright © 1996 - 2004, Roxen IS.   //   // The Roxen WebServer main program.   //   // Per Hedbor, Henrik Grubbström, Pontus Hagland, David Hedbor and others.   // ABS and suicide systems contributed freely by Francesco Chemolli    - constant cvs_version="$Id: roxen.pike,v 1.990 2008/09/25 21:38:11 mast Exp $"; + constant cvs_version="$Id: roxen.pike,v 1.991 2008/09/29 15:57:33 mast Exp $";      //! @appears roxen   //!   //! The Roxen WebServer main program.      // The argument cache. Used by the image cache.   ArgCache argcache;      // Some headerfiles   #define IN_ROXEN
Roxen.git/server/base_server/roxen.pike:464: Inside #if 0
   if (exit_code && !once_mode)    report_notice("Restarting Roxen.\n");    else    report_notice("Shutting down Roxen.\n");    })    werror (describe_backtrace (err));   #endif    roxenloader.real_exit( exit_code ); // Now we die...   }    - private int _recurse; + private int shutdown_recurse;      // Shutdown Roxen   // exit_code = 0 True shutdown   // exit_code = -1 Restart   private void low_shutdown(int exit_code)   { -  if(_recurse >= 4) +  if(shutdown_recurse >= 4)    {    if (mixed err =    catch (report_notice("Exiting roxen (spurious signals received).\n")) ||    catch (stop_all_configurations()))    werror (describe_backtrace (err));    destruct(cache);   #ifdef THREADS    if (mixed err = catch (stop_handler_threads()))    werror (describe_backtrace (err));   #endif /* THREADS */    roxenloader.real_exit(exit_code);    } -  if (_recurse++) return; +  if (shutdown_recurse++) return;    -  +  // Turn off the backend thread monitor while we're shutting down. +  slow_be_timeout_changed(); +     if (mixed err = catch(stop_all_configurations()))    werror (describe_backtrace (err));      #ifdef SNMP_AGENT    if(objectp(snmpagent)) {    snmpagent->stop_trap();    snmpagent->disable();    }   #endif   
Roxen.git/server/base_server/roxen.pike:604: Inside #if defined(THREADS)
   buffer[w_ptr++]=v;    r_cond::signal();    }   }      #ifndef NO_SLOW_REQ_BT   // This is a system to dump all threads whenever a request takes   // longer than a configurable timeout.      protected Pike.Backend slow_req_monitor; // Set iff slow req bt is enabled. - protected float slow_req_timeout; + protected float slow_req_timeout, slow_be_timeout;      protected void slow_req_monitor_thread (Pike.Backend my_monitor)   {    // my_monitor is just a safeguard to ensure we don't get multiple    // monitor threads.    while (slow_req_monitor == my_monitor)    slow_req_monitor (3600);   }    -  + protected mixed slow_be_call_out; +  + protected void slow_be_before_cb() + { + #ifdef DEBUG +  if (this_thread() != backend_thread) error ("Run from wrong thread.\n"); + #endif +  if (Pike.Backend monitor = slow_be_call_out && slow_req_monitor) { +  monitor->remove_call_out (slow_be_call_out); +  slow_be_call_out = 0; +  } + } +  + protected void slow_be_after_cb() + { + #ifdef DEBUG +  if (this_thread() != backend_thread) error ("Run from wrong thread.\n"); + #endif +  if (slow_be_timeout > 0.0) +  if (Pike.Backend monitor = slow_req_monitor) +  slow_be_call_out = monitor->call_out (dump_slow_req, slow_be_timeout, +  this_thread(), slow_be_timeout); + } +    void slow_req_count_changed()   {    Pike.Backend monitor = slow_req_monitor;    int count = query ("slow_req_bt_count");       if (count && monitor) {    // Just a change of the count - nothing to do.    }       else if (count) { // Start.    monitor = slow_req_monitor = Pike.SmallBackend();    Thread.thread_create (slow_req_monitor_thread, monitor);    monitor->call_out (lambda () {}, 0); // Safeguard if there's a race. -  +  slow_be_timeout_changed();    }       else if (monitor) { // Stop.    slow_req_monitor = 0;    monitor->call_out (lambda () {}, 0); // To wake up the thread. -  +  slow_be_timeout_changed();    }   }      void slow_req_timeout_changed()   {   #ifdef DEBUG    if (query ("slow_req_bt_timeout") < 0) error ("Invalid timeout.\n");   #endif    slow_req_timeout = query ("slow_req_bt_timeout");   }    -  + void slow_be_timeout_changed() + { + #ifdef DEBUG +  if (query ("slow_be_bt_timeout") < 0) error ("Invalid timeout.\n"); + #endif +  slow_be_timeout = query ("slow_be_bt_timeout"); +  + #ifdef DEBUG +  if ((Pike.DefaultBackend->before_callback && +  Pike.DefaultBackend->before_callback != slow_be_before_cb) || +  (Pike.DefaultBackend->after_callback && +  Pike.DefaultBackend->after_callback != slow_be_after_cb)) +  werror ("Pike.DefaultBackend already hooked up with " +  "other before/after callbacks - they get overwritten: %O/%O\n", +  Pike.DefaultBackend->before_callback, +  Pike.DefaultBackend->after_callback); + #endif +  +  if (query ("slow_req_bt_count") && slow_be_timeout > 0.0 && +  // Don't trig if we're shutting down. +  !shutdown_recurse) { +  Pike.DefaultBackend->before_callback = slow_be_before_cb; +  Pike.DefaultBackend->after_callback = slow_be_after_cb; +  } +  else { +  Pike.DefaultBackend->before_callback = 0; +  Pike.DefaultBackend->after_callback = 0; +  if (Pike.Backend monitor = slow_be_call_out && slow_req_monitor) { +  monitor->remove_call_out (slow_be_call_out); +  slow_be_call_out = 0; +  } +  } + } +    protected void dump_slow_req (Thread.Thread thread, float timeout)   { -  +  object threads_disabled = _disable_threads(); +     int count = query ("slow_req_bt_count");    if (count > 0) set ("slow_req_bt_count", count - 1);    -  report_debug ("###### Thread 0x%x has been busy for more than %g seconds.\n", +  if (thread == backend_thread && !slow_be_call_out) { +  // Avoid false alarms for the backend thread if we got here due to +  // a race. Should perhaps have something like this for the handler +  // threads too, but otoh races are more rare there due to the +  // longer timeouts. +  } +  +  else { +  report_debug ("###### %s 0x%x has been busy for more than %g seconds.\n", +  thread == backend_thread ? "Backend thread" : "Thread",    thread->id_number(), timeout); -  describe_all_threads(); +  describe_all_threads (threads_disabled);    }    -  +  threads_disabled = 0; // Paranoia. + } +    #endif // !NO_SLOW_REQ_BT      // // This is easier than when there are no threads.   // // See the discussion below. :-)   //   // // But there is extra functionality below we really want, though,   // // so let's use that one instead...   // function async_sig_start( function f, int really )   // {   // return lambda( mixed ... args ) {
Roxen.git/server/base_server/roxen.pike:698: Inside #if defined(THREADS) and #if defined(TEST_EUID_CHANGE)
   if (f->open ("rootonly", "r") && f->read())    werror ("Handler thread %d can read rootonly\n", id);    else    werror ("Handler thread %d can't read rootonly\n", id);    }   #endif    while(1)    {    int thread_flagged_as_busy;   #ifndef NO_SLOW_REQ_BT -  mixed slow_req_call_out; +  Pike.Backend monitor; +  mixed call_out;   #endif    if(q=catch {    do {   // if (!busy_threads) werror ("GC: %d\n", gc());    cache_clear_deltas();    THREAD_WERR("Handle thread ["+id+"] waiting for next event");    if(arrayp(h=handle_queue->read()) && h[0]) {    THREAD_WERR(sprintf("Handle thread [%O] calling %O(%{%O, %})",    id, h[0], h[1] / 1));    set_locale();    busy_threads++;    thread_flagged_as_busy = 1;      #ifndef NO_SLOW_REQ_BT -  if (Pike.Backend monitor = +  if (h[0] != bg_process_queue &&    // Leave out bg_process_queue. It makes a timeout on    // every individual job instead. -  h[0] != bg_process_queue && -  slow_req_monitor) { -  slow_req_call_out = -  monitor->call_out (dump_slow_req, slow_req_timeout, +  (monitor = slow_req_monitor) && slow_req_timeout > 0.0) { +  call_out = monitor->call_out (dump_slow_req, slow_req_timeout,    this_thread(), slow_req_timeout);    h[0](@h[1]); -  monitor->remove_call_out (slow_req_call_out); +  monitor->remove_call_out (call_out);    }    else   #endif    h[0](@h[1]);       h=0;    busy_threads--;    thread_flagged_as_busy = 0;    } else if(!h) {    // Roxen is shutting down.
Roxen.git/server/base_server/roxen.pike:765: Inside #if defined(THREADS)
   cond->wait (m->lock());    }    threads_on_hold--;    THREAD_WERR("Handle thread [" + id + "] released");    }    } while(1);    }) {    if (thread_flagged_as_busy)    busy_threads--;   #ifndef NO_SLOW_REQ_BT -  if (Pike.Backend monitor = slow_req_call_out && slow_req_monitor) -  monitor->remove_call_out (slow_req_call_out); +  if (call_out) monitor->remove_call_out (call_out);   #endif    if (h = catch {    report_error(/*LOCALE("", "Uncaught error in handler thread: %s"    "Client will not get any response from Roxen.\n"),*/    describe_backtrace(q));    if (q = catch {h = 0;}) {    report_error(LOC_M(5, "Uncaught error in handler thread: %sClient "    "will not get any response from Roxen.")+"\n",    describe_backtrace(q));    catch (q = 0);
Roxen.git/server/base_server/roxen.pike:1062: Inside #if defined(THREADS)
  protected void bg_process_queue()   {    if (bg_process_running) return;    // Relying on the interpreter lock here.    bg_process_running = 1;       int maxbeats =    min (time() - bg_last_busy, bg_time_buffer_max) * (int) (1 / 0.04);      #ifndef NO_SLOW_REQ_BT -  mixed slow_req_call_out; +  Pike.Backend monitor; +  mixed call_out;   #endif       if (mixed err = catch {    while (bg_queue->size()) {    // Not a race here since only one thread is reading the queue.    array task = bg_queue->read();       // Wait a while if another thread is busy already.    if (busy_threads > 1) {    for (maxbeats = max (maxbeats, (int) (bg_time_buffer_min / 0.04));
Roxen.git/server/base_server/roxen.pike:1097: Inside #if defined(THREADS) and #if defined(DEBUG_BACKGROUND_RUN)
   master()->describe_program (task[0])) :    sprintf ("%O", task[0]),    map (task[1], lambda (mixed arg)    {return sprintf ("%O", arg);}) * ", ",    bg_queue->size());   #endif       float task_vtime, task_rtime;      #ifndef NO_SLOW_REQ_BT -  if (Pike.Backend monitor = slow_req_monitor) { -  slow_req_call_out = -  monitor->call_out (dump_slow_req, slow_req_timeout, +  if ((monitor = slow_req_monitor) && slow_req_timeout > 0.0) { +  call_out = monitor->call_out (dump_slow_req, slow_req_timeout,    this_thread(), slow_req_timeout);   #endif    int start_hrtime = gethrtime (1);    task_vtime = gauge {    if (task[0]) // Ignore things that have become destructed.    // Note: BackgroundProcess.repeat assumes that there are    // exactly two refs to task[0] during the call below.    task[0] (@task[1]);    };    task_rtime = (gethrtime (1) - start_hrtime) / 1e9;   #ifndef NO_SLOW_REQ_BT -  monitor->remove_call_out (slow_req_call_out); +  monitor->remove_call_out (call_out);    }   #endif       if (task_rtime > 60.0)    report_warning ("Warning: Background job took more than one minute "    "(%g s real time and %g s cpu time):\n"    " %s (%s)\n%s",    task_rtime, task_vtime,    functionp (task[0]) ?    sprintf ("%s: %s", Function.defined (task[0]),
Roxen.git/server/base_server/roxen.pike:1146: Inside #if defined(THREADS) and #if defined(DEBUG_BACKGROUND_RUN)
   else    report_debug ("background_run done, "    "took %g ms cpu time and %g ms real time\n",    task_vtime * 1000, task_rtime * 1000);   #endif       if (busy_threads > 1) bg_last_busy = time();    }    }) {   #ifndef NO_SLOW_REQ_BT -  if (Pike.Backend monitor = slow_req_call_out && slow_req_monitor) -  monitor->remove_call_out (slow_req_call_out); +  if (call_out) monitor->remove_call_out (call_out);   #endif    bg_process_running = 0;    handle (bg_process_queue);    throw (err);    }    bg_process_running = 0;   }   #endif      mixed background_run (int|float delay, function func, mixed... args)
Roxen.git/server/base_server/roxen.pike:4973:    p->output(to);    if(to2) p->output(to2);    p->input(from);    return p;   #if efun(spider.shuffle)    }   #endif   }      // Dump all threads to the debug log. - void describe_all_threads() + void describe_all_threads (void|object threads_disabled)   { - #if constant (thread_create) +  if (!threads_disabled)    // Disable all threads to avoid potential locking problems while we    // have the backtraces. It also gives an atomic view of the state. -  object threads_disabled = _disable_threads(); +  threads_disabled = _disable_threads();       report_debug("###### Describing all Pike threads:\n>>\n");       array(Thread.Thread) threads = all_threads();       threads = Array.sort_array (    threads,    lambda (Thread.Thread a, Thread.Thread b) {    // Backend thread first, otherwise in id order.    if (a == backend_thread)
Roxen.git/server/base_server/roxen.pike:5016:    report_debug(">> " +    replace (describe_backtrace (threads[i]->backtrace()),    "\n", "\n>> ") +    "\n");    }       report_debug ("###### Total %d Pike threads\n\n", sizeof (threads));       threads = 0;    threads_disabled = 0; - #else -  report_debug("Describing single thread:\n%s\n\n", -  describe_backtrace (backtrace())); - #endif +       #ifdef DEBUG    report_debug (RoxenDebug.report_leaks());   #endif   }         // Dump threads by file polling.      constant cdt_poll_interval = 5; // Seconds.
Roxen.git/server/base_server/roxen.pike:5347:    {    array(string) splitdir = roxen_path ("$LOGFILE") / "/";    cdt_filename = splitdir[-1];    cdt_directory = splitdir[..sizeof (splitdir) - 2] * "/";    if (has_suffix (cdt_filename, ".1"))    cdt_filename = cdt_filename[..sizeof (cdt_filename) - 3];    cdt_filename += ".dump_threads";    cdt_changed (getvar ("dump_threads_by_file"));    }    +  slow_req_count_changed(); +  slow_req_timeout_changed(); +  slow_be_timeout_changed(); +    #ifdef ROXEN_DEBUG_MEMORY_TRACE    restart_roxen_debug_memory_trace();   #endif      #ifndef __NT__    restart_if_stuck( 0 );   #endif   #ifdef __RUN_TRACE    trace(1);   #endif