diff --git a/src/util/subprocess-test.cc b/src/util/subprocess-test.cc index c3a7e00..a42200d 100644 --- a/src/util/subprocess-test.cc +++ b/src/util/subprocess-test.cc @@ -4,8 +4,10 @@ #include #include +#include "util/logging.h" #include "util/subprocess.h" #include "util/test_util.h" +#include "util/thread.h" using std::string; using std::vector; @@ -83,4 +85,116 @@ TEST_F(SubprocessTest, TestKill) { ASSERT_EQ(SIGKILL, WTERMSIG(wait_status)); } +static void LogAndSleepLoop10x(sigval_t sigval) { + for (int i = 0; i < 10; i++) { + LOG(INFO) << "Thing " << i; + sleep(1); + } +} + +static void CreateAndStartTimer(timer_t* timerid, struct sigevent* sevp, struct itimerspec* its) { + // Create the test-timeout timer. + sevp->sigev_notify = SIGEV_THREAD; + sevp->sigev_value.sival_ptr = timerid; + sevp->sigev_notify_function = LogAndSleepLoop10x; + sevp->sigev_notify_attributes = NULL; + CHECK_ERR(::timer_create(CLOCK_MONOTONIC, sevp, timerid)) << "Unable to set timeout timer"; + + // Start the timer. + its->it_interval.tv_sec = 0; // No repeat. + its->it_interval.tv_nsec = 0; + its->it_value.tv_sec = 5; // Fire in timeout seconds. + its->it_value.tv_nsec = 0; + CHECK_ERR(::timer_settime(*timerid, 0, its, NULL)); +} + +// This test clearly shows that the thread started by SIGEV_THREAD runs +// concurrently with main(), so AFAICT is not required to be async-signal-safe. +TEST_F(SubprocessTest, DISABLED_TestThreadSigHandler) { + timer_t timerid; + struct sigevent sevp; + struct itimerspec its; + CreateAndStartTimer(&timerid, &sevp, &its); + + for (int i = 0; i < 10; i++) { + LOG(INFO) << "Main " << i; + sleep(1); + } +} + +static const int kNumThreads = 2; +static AtomicWord addrs[kNumThreads]; +static AtomicWord keep_going_nuts = true; + +static void GoNuts() { + // Spin on an atomic flag. + while (base::subtle::NoBarrier_Load(&keep_going_nuts)) { + // We use randomness to defeat tcmalloc's thread locality. + int slot_to_use = rand() % kNumThreads; + char* s = (char*) malloc(64); + AtomicWord prev = base::subtle::NoBarrier_AtomicExchange(&addrs[slot_to_use], + reinterpret_cast(s)); + if (prev) { + free(reinterpret_cast(prev)); + } + } +} + +// Even with only 2 threads and 2 forks, this test deadlocks 80% of the time. +// See https://code.google.com/p/gperftools/issues/detail?id=175 +TEST_F(SubprocessTest, TestForkMalloc) { + const int kNumForks = kNumThreads * 1; + + scoped_refptr threads[kNumThreads]; + pid_t pids[kNumForks]; + + for (int i = 0; i < kNumThreads; i++) { + CHECK_OK(Thread::Create("a", "foo", GoNuts, &threads[i])); + } + for (int i = 0; i < kNumForks; i++) { + printf("Forking child %d\n", i); + switch (pids[i] = fork()) { + case -1: { + perror("Fork failure"); + abort(); + break; + } + case 0: { + // Child. + void* a = malloc(64); + if (!a) { + perror("malloc failure"); + abort(); + } + free(a); + exit(0); + break; + } + default: { + // Parent. + break; + } + } + } + + // Reap the kids. + for (int i = 0; i < kNumForks; i++) { + waitpid(pids[i], 0, 0); + } + + // Stop and join the threads. + base::subtle::NoBarrier_Store(&keep_going_nuts, false); + for (int i = 0; i < kNumThreads; i++) { + CHECK_OK(ThreadJoiner(threads[i].get()).Join()); + } + + // Final free() calls to avoid leak check errors. + for (int i = 0; i < kNumThreads; i++) { + AtomicWord addr = base::subtle::NoBarrier_Load(&addrs[i]); + if (addr) { + free(reinterpret_cast(addr)); + } + } +} + } // namespace kudu