Permalink
Browse files

Fix thread creation on Intel Itanium. Fixes issue #427.

  • Loading branch information...
1 parent adad4c6 commit 78beaf71018a018eed412e4cf7d912899dd11cb6 @FooBarWidget FooBarWidget committed Dec 12, 2009
Showing with 104 additions and 4 deletions.
  1. +2 −0 NEWS
  2. +4 −2 ext/common/StandardApplicationPool.h
  3. +97 −1 ext/oxt/thread.hpp
  4. +1 −1 test/ApplicationPoolTest.cpp
View
2 NEWS
@@ -10,6 +10,8 @@ Release 2.2.8
by passenger-install-nginx-module. As a result,
passenger-install-nginx-module fails on e.g. out-of-the-box Ubuntu
installations until the user manually installs OpenSSL. Issue #422.
+ * Fixed thread creation issue on Intel Itanium platforms.
+ This fixes issue #427.
Release 2.2.7
@@ -34,6 +34,7 @@
#include <oxt/system_calls.hpp>
#include <oxt/backtrace.hpp>
+#include <oxt/thread.hpp>
#include <string>
#include <sstream>
@@ -242,7 +243,7 @@ class StandardApplicationPool: public ApplicationPool {
SpawnManager spawnManager;
#endif
SharedDataPtr data;
- boost::thread *cleanerThread;
+ oxt::thread *cleanerThread;
bool detached;
bool done;
unsigned int maxIdleTime;
@@ -632,8 +633,9 @@ class StandardApplicationPool: public ApplicationPool {
waitingOnGlobalQueue = 0;
maxPerApp = DEFAULT_MAX_INSTANCES_PER_APP;
maxIdleTime = DEFAULT_MAX_IDLE_TIME;
- cleanerThread = new boost::thread(
+ cleanerThread = new oxt::thread(
bind(&StandardApplicationPool::cleanerThreadMainLoop, this),
+ "ApplicationPool cleaner",
CLEANER_THREAD_STACK_SIZE
);
}
View
@@ -34,6 +34,9 @@
#ifdef OXT_BACKTRACE_IS_ENABLED
#include <sstream>
#endif
+#include <string>
+#include <list>
+#include <unistd.h>
#include <limits.h> // for PTHREAD_STACK_MIN
namespace oxt {
@@ -132,17 +135,45 @@ class thread: public boost::thread {
set_thread_main_function(boost::bind(thread_main, func, data));
unsigned long min_stack_size;
+ bool stack_min_size_defined;
+ bool round_stack_size;
+
#ifdef PTHREAD_STACK_MIN
// PTHREAD_STACK_MIN may not be a constant macro so we need
// to evaluate it dynamically.
min_stack_size = PTHREAD_STACK_MIN;
+ stack_min_size_defined = true;
#else
// Assume minimum stack size is 128 KB.
min_stack_size = 128 * 1024;
+ stack_min_size_defined = false;
#endif
- if (stack_size < min_stack_size) {
+ if (stack_size != 0 && stack_size < min_stack_size) {
stack_size = min_stack_size;
+ round_stack_size = !stack_min_size_defined;
+ } else {
+ round_stack_size = true;
+ }
+
+ if (round_stack_size) {
+ // Round stack size up to page boundary.
+ long page_size;
+ #if defined(_SC_PAGESIZE)
+ page_size = sysconf(_SC_PAGESIZE);
+ #elif defined(_SC_PAGE_SIZE)
+ page_size = sysconf(_SC_PAGE_SIZE);
+ #elif defined(PAGESIZE)
+ page_size = sysconf(PAGESIZE);
+ #elif defined(PAGE_SIZE)
+ page_size = sysconf(PAGE_SIZE);
+ #else
+ page_size = getpagesize();
+ #endif
+ if (stack_size % page_size != 0) {
+ stack_size = stack_size - (stack_size % page_size) + page_size;
+ }
}
+
start_thread(stack_size);
}
@@ -231,6 +262,71 @@ class thread: public boost::thread {
done = timed_join(boost::posix_time::millisec(10));
}
}
+
+ /**
+ * Keep interrupting the thread until it's done, then join it.
+ * This method will keep trying for at most <em>timeout</em> milliseconds.
+ *
+ * @param timeout The maximum number of milliseconds that this method
+ * should keep trying.
+ * @return True if the thread was successfully joined, false if the
+ * timeout has been reached.
+ * @throws boost::thread_interrupted The calling thread has been
+ * interrupted before we could join this thread.
+ */
+ bool interrupt_and_join(unsigned int timeout) {
+ bool joined = false, timed_out = false;
+ boost::posix_time::ptime deadline =
+ boost::posix_time::microsec_clock::local_time() +
+ boost::posix_time::millisec(timeout);
+ while (!joined && !timed_out) {
+ interrupt();
+ joined = timed_join(boost::posix_time::millisec(10));
+ timed_out = !joined && boost::posix_time::microsec_clock::local_time() > deadline;
+ }
+ return joined;
+ }
+
+ /**
+ * Interrupt and join multiple threads in a way that's more efficient than calling
+ * interrupt_and_join() on each thread individually. It iterates over all threads,
+ * interrupts each one without joining it, then waits until at least one thread
+ * is joinable. This is repeated until all threads are joined.
+ *
+ * @param threads An array of threads to join.
+ * @param size The number of elements in <em>threads</em>.
+ * @throws boost::thread_interrupted The calling thread has been
+ * interrupted before all threads have been joined. Some threads
+ * may have been successfully joined while others haven't.
+ */
+ static void interrupt_and_join_multiple(oxt::thread **threads, unsigned int size) {
+ std::list<oxt::thread *> remaining_threads;
+ std::list<oxt::thread *>::iterator it, current;
+ oxt::thread *thread;
+ unsigned int i;
+
+ for (i = 0; i < size; i++) {
+ remaining_threads.push_back(threads[i]);
+ }
+
+ while (!remaining_threads.empty()) {
+ for (it = remaining_threads.begin(); it != remaining_threads.end(); it++) {
+ thread = *it;
+ thread->interrupt();
+ }
+ for (it = remaining_threads.begin(); it != remaining_threads.end(); it++) {
+ thread = *it;
+ if (thread->timed_join(boost::posix_time::millisec(0))) {
+ current = it;
+ it--;
+ remaining_threads.erase(current);
+ }
+ }
+ if (!remaining_threads.empty()) {
+ syscalls::usleep(10000);
+ }
+ }
+ }
};
} // namespace oxt
@@ -182,7 +182,7 @@
Application::SessionPtr session3;
bool done;
- shared_ptr<thread> thr = ptr(new thread(PoolWaitTestThread(pool2, session3, done)));
+ shared_ptr<boost::thread> thr = ptr(new boost::thread(PoolWaitTestThread(pool2, session3, done)));
usleep(500000);
ensure("ApplicationPool is still waiting", !done);
ensure_equals(pool->getActive(), 2u);

0 comments on commit 78beaf7

Please sign in to comment.