2001-02-23
2001-02-23 04:04:09 by Martin Stjernholm <mast@lysator.liu.se>
-
9567c189c4bab9ac894a82d2adf3b668c88ffd58
(154 lines)
(+139/-15)
[
Show
| Annotate
]
Branch: 5.2
Fixed shutdown code to wait in the handler threads before shutting down
the modules. Don't let the handler threads bail out on die_die_die, since
we might in some circumstances need to start new ones to shut down the
modules without risking blocking.
Rev: server/base_server/roxen.pike:1.634
4:
// 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.633 2001/02/23 02:25:44 per Exp $";
+ constant cvs_version="$Id: roxen.pike,v 1.634 2001/02/23 04:04:09 mast Exp $";
// Used when running threaded to find out which thread is the backend thread.
Thread.Thread backend_thread;
355:
array(Configuration) configurations = ({});
+ private void stop_all_configurations()
+ {
+ configurations->unregister_urls();
+ #ifdef THREADS
+ // Spend some time waiting out the handler threads before starting
+ // to shut down the modules.
+ hold_handler_threads();
+ release_handler_threads(3);
+ #endif
+ configurations->stop(1);
+ }
+
// When true, roxen will shut down as soon as possible.
local static int die_die_die;
368:
exit( exit_code ); // Now we die...
}
-
+
// Shutdown Roxen
// exit_code = 0 True shutdown
// exit_code = -1 Restart
376:
{
catch
{
- configurations->stop();
+ stop_all_configurations();
int pid;
if (exit_code) {
report_debug("Restarting Roxen.\n");
414: Inside #if defined(THREADS)
{
Thread.Thread t = thread_create(f, @args);
catch(t->set_name( id ));
- THREAD_WERR(id+" started");
+
return t;
}
442: Inside #if defined(THREADS)
return tmp;
}
+ mixed tryread()
+ {
+ if (!(w_ptr - r_ptr)) return ([])[0];
+ mixed tmp = buffer[r_ptr];
+ buffer[r_ptr++] = 0; // Throw away any references.
+ return tmp;
+ }
+
void write(mixed v)
{
if(w_ptr >= sizeof(buffer))
466: Inside #if defined(THREADS)
//! An entry consists of an array(function fp, array args)
local static int thread_reap_cnt;
- //! Number of handler threads that are alive.
+ //! Number of handler threads in the process of being stopped.
-
+ static int threads_on_hold;
+ //! Number of handler threads on hold.
+
local static void handler_thread(int id)
//! The actual handling function. This functions read function and
//! parameters from the queue, calls it, then reads another one. There
//! is a lot of error handling to ensure that nothing serious happens if
//! the handler function throws an error.
{
- array (mixed) h, q;
- while(!die_die_die)
+ THREAD_WERR("Handle thread ["+id+"] started");
+ mixed h, q;
+ while(1)
{
if(q=catch {
do {
THREAD_WERR("Handle thread ["+id+"] waiting for next event");
- if((h=handle_queue->read()) && h[0]) {
- THREAD_WERR(sprintf("Handle thread [%O] calling %O(@%O)...",
- id, h[0], h[1..]));
+ 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();
h[0](@h[1]);
h=0;
495: Inside #if defined(THREADS)
#endif
return;
}
+ #ifdef DEBUG
+ else if (h != 1)
+ error ("Unknown message in handle_queue: %O\n", h);
+ #endif
+ else {
+ num_hold_messages--;
+ THREAD_WERR("Handle thread [" + id + "] put on hold");
+ threads_on_hold++;
+ if (Thread.Condition cond = hold_wakeup_cond) cond->wait();
+ threads_on_hold--;
+ THREAD_WERR("Handle thread [" + id + "] released");
+ }
} while(1);
}) {
if (h = catch {
544: Inside #if defined(THREADS)
handler_threads += new_threads;
}
+ static int num_hold_messages;
+ static Thread.Condition hold_wakeup_cond = Thread.Condition();
+
+ void hold_handler_threads()
+ //! Tries to put all handler threads on hold, but gives up if it takes
+ //! too long.
+ {
+ if (!hold_wakeup_cond) {
+ THREAD_WERR("Ignoring request to hold handler threads during stop");
+ return;
+ }
+
+ int timeout=10;
+ #if constant(_reset_dmalloc)
+ // DMALLOC slows stuff down a bit...
+ timeout *= 10;
+ #endif /* constant(_reset_dmalloc) */
+
+ THREAD_WERR("Putting " + (number_of_threads - threads_on_hold) +
+ " handler threads on hold, " +
+ threads_on_hold + " on hold already");
+
+ for (int i = number_of_threads - threads_on_hold - num_hold_messages; i-- > 0;) {
+ handle_queue->write (1);
+ num_hold_messages++;
+ }
+ while (threads_on_hold < number_of_threads && timeout--)
+ sleep (0.1);
+
+ THREAD_WERR(threads_on_hold + " handler threads on hold, " +
+ (number_of_threads - threads_on_hold) + " not responding");
+ }
+
+ void release_handler_threads (int numthreads)
+ //! Releases any handler threads put on hold. If necessary new threads
+ //! are started to ensure that at least @[numthreads] threads are
+ //! responding. Any thread that haven't arrived to the hold state
+ //! since @[hold_handler_threads] is considered nonresponding.
+ {
+ if (Thread.Condition cond = hold_wakeup_cond) {
+ // Flush out any remaining hold messages from the queue.
+ for (int i = handle_queue->size(); i && num_hold_messages; i--) {
+ mixed task = handle_queue->tryread();
+ if (task == 1) num_hold_messages--;
+ else handle_queue->write (task);
+ }
+ #ifdef DEBUG
+ if (num_hold_messages)
+ error ("num_hold_messages is bogus (%d).\n", num_hold_messages);
+ #endif
+ num_hold_messages = 0;
+
+ int blocked_threads = number_of_threads - threads_on_hold;
+ int threads_to_create = numthreads - threads_on_hold;
+
+ THREAD_WERR("Releasing " + was_on_hold + " threads on hold");
+ cond->broadcast();
+
+ if (threads_to_create > 0) {
+ array(object) new_threads = ({});
+ for (threads_to_create = 0;
+ threads_on_hold < numthreads;
+ number_of_threads++, threads_to_create++)
+ new_threads += ({ do_thread_create( "Handle thread [" +
+ number_of_threads + "]",
+ handler_thread, number_of_threads ) });
+ handler_threads += new_threads;
+ report_notice ("Created %d new handler threads to compensate "
+ "for %d blocked ones.\n", threads_to_create, blocked_threads);
+ }
+ }
+ else {
+ THREAD_WERR("Ignoring request to release handler threads during stop");
+ return;
+ }
+ }
+
static Thread.MutexKey backend_block_lock;
void stop_handler_threads()
556: Inside #if defined(THREADS)
timeout *= 10;
#endif /* constant(_reset_dmalloc) */
report_debug("Stopping all request handler threads.\n");
+
+ // Wake up any handler threads on hold, and ensure none gets on hold
+ // after this.
+ if (Thread.Condition cond = hold_wakeup_cond) {
+ hold_wakeup_cond = 0;
+ cond->broadcast();
+ }
+
while(number_of_threads>0) {
number_of_threads--;
handle_queue->write(0);
563: Inside #if defined(THREADS)
}
handler_threads = ({});
- if (this_thread() != backend_thread) {
+ if (this_thread() != backend_thread && !backend_block_lock) {
thread_reap_cnt++;
Thread.Mutex mutex = Thread.Mutex();
backend_block_lock = mutex->lock();
578: Inside #if defined(THREADS)
while(thread_reap_cnt) {
sleep(0.1);
if(--timeout<=0) {
- report_debug("Giving up waiting on threads!\n");
+ report_debug("Giving up waiting on threads; "
+ "%d threads blocked.\n", thread_reap_cnt);
+ #ifdef DEBUG
+ describe_all_threads();
+ #endif
return;
}
}
3128:
if(++_recurse > 4)
{
report_debug("Exiting roxen (spurious signals received).\n");
- configurations->stop();
+ stop_all_configurations();
#ifdef THREADS
stop_handler_threads();
#endif /* THREADS */
3136:
}
report_debug("Exiting roxen.\n");
- configurations->stop();
+ stop_all_configurations();
#ifdef THREADS
stop_handler_threads();
#endif /* THREADS */