Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a46bf7eb authored by Christopher Ferris's avatar Christopher Ferris Committed by Gerrit Code Review
Browse files

Merge "Convert futex to cond wait."

parents 6ef68b55 3cdbfdce
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -64,6 +64,10 @@ libbacktrace_shared_libraries_host := \
libbacktrace_static_libraries_host := \
	libcutils \

libbacktrace_ldlibs_host := \
	-lpthread \
	-lrt \

module := libbacktrace
module_tag := optional
build_type := target
+33 −16
Original line number Diff line number Diff line
@@ -17,14 +17,15 @@
#include <errno.h>
#include <inttypes.h>
#include <limits.h>
#include <linux/futex.h>
#include <pthread.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <sys/syscall.h>
#include <sys/time.h>
#include <sys/types.h>
#include <ucontext.h>
#include <unistd.h>

#include <cutils/atomic.h>

@@ -32,10 +33,6 @@
#include "BacktraceThread.h"
#include "thread_utils.h"

static inline int futex(volatile int* uaddr, int op, int val, const struct timespec* ts, volatile int* uaddr2, int val3) {
  return syscall(__NR_futex, uaddr, op, val, ts, uaddr2, val3);
}

//-------------------------------------------------------------------------
// ThreadEntry implementation.
//-------------------------------------------------------------------------
@@ -45,7 +42,14 @@ pthread_mutex_t ThreadEntry::list_mutex_ = PTHREAD_MUTEX_INITIALIZER;
// Assumes that ThreadEntry::list_mutex_ has already been locked before
// creating a ThreadEntry object.
ThreadEntry::ThreadEntry(pid_t pid, pid_t tid)
    : pid_(pid), tid_(tid), futex_(0), ref_count_(1), mutex_(PTHREAD_MUTEX_INITIALIZER), next_(ThreadEntry::list_), prev_(NULL) {
    : pid_(pid), tid_(tid), ref_count_(1), mutex_(PTHREAD_MUTEX_INITIALIZER),
      wait_mutex_(PTHREAD_MUTEX_INITIALIZER), wait_value_(0),
      next_(ThreadEntry::list_), prev_(NULL) {
  pthread_condattr_t attr;
  pthread_condattr_init(&attr);
  pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
  pthread_cond_init(&wait_cond_, &attr);

  // Add ourselves to the list.
  if (ThreadEntry::list_) {
    ThreadEntry::list_->prev_ = this;
@@ -99,22 +103,35 @@ ThreadEntry::~ThreadEntry() {

  next_ = NULL;
  prev_ = NULL;

  pthread_cond_destroy(&wait_cond_);
}

void ThreadEntry::Wait(int value) {
  timespec ts;
  ts.tv_sec = 10;
  ts.tv_nsec = 0;
  errno = 0;
  futex(&futex_, FUTEX_WAIT, value, &ts, NULL, 0);
  if (errno != 0 && errno != EWOULDBLOCK) {
    BACK_LOGW("futex wait failed, futex = %d: %s", futex_, strerror(errno));
  if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) {
    BACK_LOGW("clock_gettime failed: %s", strerror(errno));
    abort();
  }
  ts.tv_sec += 10;

  pthread_mutex_lock(&wait_mutex_);
  while (wait_value_ != value) {
    int ret = pthread_cond_timedwait(&wait_cond_, &wait_mutex_, &ts);
    if (ret != 0) {
      BACK_LOGW("pthread_cond_timedwait failed: %s", strerror(ret));
      break;
    }
  }
  pthread_mutex_unlock(&wait_mutex_);
}

void ThreadEntry::Wake() {
  futex_++;
  futex(&futex_, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
  pthread_mutex_lock(&wait_mutex_);
  wait_value_++;
  pthread_mutex_unlock(&wait_mutex_);

  pthread_cond_signal(&wait_cond_);
}

void ThreadEntry::CopyUcontextFromSigcontext(void* sigcontext) {
@@ -142,7 +159,7 @@ static void SignalHandler(int, siginfo_t*, void* sigcontext) {

  // Pause the thread until the unwind is complete. This avoids having
  // the thread run ahead causing problems.
  entry->Wait(1);
  entry->Wait(2);

  ThreadEntry::Remove(entry);
}
@@ -194,7 +211,7 @@ bool BacktraceThread::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) {
  }

  // Wait for the thread to get the ucontext.
  entry->Wait(0);
  entry->Wait(1);

  // After the thread has received the signal, allow other unwinders to
  // continue.
+7 −3
Original line number Diff line number Diff line
@@ -48,8 +48,10 @@ public:

  inline void Lock() {
    pthread_mutex_lock(&mutex_);
    // Reset the futex value in case of multiple unwinds of the same thread.
    futex_ = 0;

    // Always reset the wait value since this could be the first or nth
    // time this entry is locked.
    wait_value_ = 0;
  }

  inline void Unlock() {
@@ -66,9 +68,11 @@ private:

  pid_t pid_;
  pid_t tid_;
  int futex_;
  int ref_count_;
  pthread_mutex_t mutex_;
  pthread_mutex_t wait_mutex_;
  pthread_cond_t wait_cond_;
  int wait_value_;
  ThreadEntry* next_;
  ThreadEntry* prev_;
  ucontext_t ucontext_;
+12 −5
Original line number Diff line number Diff line
@@ -51,7 +51,7 @@
#define NS_PER_SEC              1000000000ULL

// Number of simultaneous dumping operations to perform.
#define NUM_THREADS  20
#define NUM_THREADS  40

// Number of simultaneous threads running in our forked process.
#define NUM_PTRACE_THREADS 5
@@ -486,6 +486,13 @@ TEST(libbacktrace, thread_level_trace) {
  struct sigaction new_action;
  ASSERT_TRUE(sigaction(THREAD_SIGNAL, NULL, &new_action) == 0);
  EXPECT_EQ(cur_action.sa_sigaction, new_action.sa_sigaction);
  // The SA_RESTORER flag gets set behind our back, so a direct comparison
  // doesn't work unless we mask the value off. Mips doesn't have this
  // flag, so skip this on that platform.
#ifdef SA_RESTORER
  cur_action.sa_flags &= ~SA_RESTORER;
  new_action.sa_flags &= ~SA_RESTORER;
#endif
  EXPECT_EQ(cur_action.sa_flags, new_action.sa_flags);
}

@@ -583,7 +590,7 @@ TEST(libbacktrace, thread_multiple_dump) {

  // Wait for tids to be set.
  for (std::vector<thread_t>::iterator it = runners.begin(); it != runners.end(); ++it) {
    ASSERT_TRUE(WaitForNonZero(&it->state, 10));
    ASSERT_TRUE(WaitForNonZero(&it->state, 30));
  }

  // Start all of the dumpers at once, they will spin until they are signalled
@@ -602,7 +609,7 @@ TEST(libbacktrace, thread_multiple_dump) {
  android_atomic_acquire_store(1, &dump_now);

  for (size_t i = 0; i < NUM_THREADS; i++) {
    ASSERT_TRUE(WaitForNonZero(&dumpers[i].done, 10));
    ASSERT_TRUE(WaitForNonZero(&dumpers[i].done, 30));

    // Tell the runner thread to exit its infinite loop.
    android_atomic_acquire_store(0, &runners[i].state);
@@ -625,7 +632,7 @@ TEST(libbacktrace, thread_multiple_dump_same_thread) {
  ASSERT_TRUE(pthread_create(&runner.threadId, &attr, ThreadMaxRun, &runner) == 0);

  // Wait for tids to be set.
  ASSERT_TRUE(WaitForNonZero(&runner.state, 10));
  ASSERT_TRUE(WaitForNonZero(&runner.state, 30));

  // Start all of the dumpers at once, they will spin until they are signalled
  // to begin their dump run.
@@ -645,7 +652,7 @@ TEST(libbacktrace, thread_multiple_dump_same_thread) {
  android_atomic_acquire_store(1, &dump_now);

  for (size_t i = 0; i < NUM_THREADS; i++) {
    ASSERT_TRUE(WaitForNonZero(&dumpers[i].done, 100));
    ASSERT_TRUE(WaitForNonZero(&dumpers[i].done, 30));

    ASSERT_TRUE(dumpers[i].backtrace != NULL);
    VerifyMaxDump(dumpers[i].backtrace);