Skip to content

Commit

Permalink
base: Support setting thread priorities generically.
Browse files Browse the repository at this point in the history
This patch supports setting priorities across platforms
at the PlatformThread level, by stashing thread id into the
thread handle on linux/android.

Since this adds more platform specific code, and #ifdefs
were starting to get unwieldy, all platform specific code
is moved into _platform.cc files, with the exception of the
'default' implementation, which stay in _posix.

BUG=170549

Review URL: https://chromiumcodereview.appspot.com/12741012

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@201202 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
epenner@chromium.org committed May 21, 2013
1 parent f398851 commit a688c89
Show file tree
Hide file tree
Showing 18 changed files with 434 additions and 186 deletions.
15 changes: 13 additions & 2 deletions base/base.gypi
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,8 @@
'threading/non_thread_safe_impl.cc',
'threading/non_thread_safe_impl.h',
'threading/platform_thread.h',
'threading/platform_thread_android.cc',
'threading/platform_thread_linux.cc',
'threading/platform_thread_mac.mm',
'threading/platform_thread_posix.cc',
'threading/platform_thread_win.cc',
Expand Down Expand Up @@ -653,8 +655,11 @@
'threading/sequenced_worker_pool.cc',
'third_party/dynamic_annotations/dynamic_annotations.c',
],
# Metrics won't work in the NaCl sandbox.
'sources/': [ ['exclude', '^metrics/'] ],
'sources/': [
# Metrics won't work in the NaCl sandbox.
['exclude', '^metrics/'],
['include', '^threading/platform_thread_linux\\.cc$'],
],
}],
['OS == "android" and >(nacl_untrusted_build)==0', {
'sources!': [
Expand All @@ -672,6 +677,12 @@
['include', '^worker_pool_linux\\.cc$'],
],
}],
['OS == "android" and _toolset == "host" and host_os == "linux"', {
'sources/': [
# Pull in specific files for host builds.
['include', '^threading/platform_thread_linux\\.cc$'],
],
}],
['OS == "ios" and _toolset != "host"', {
'sources/': [
# Pull in specific Mac files for iOS (which have been filtered out
Expand Down
2 changes: 1 addition & 1 deletion base/debug/trace_event_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -977,7 +977,7 @@ void TraceLog::SetDisabled() {
lock_.Release();
PlatformThread::Join(sampling_thread_handle_);
lock_.Acquire();
sampling_thread_handle_ = 0;
sampling_thread_handle_ = kNullThreadHandle;
sampling_thread_.reset();
}

Expand Down
66 changes: 54 additions & 12 deletions base/threading/platform_thread.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,27 +23,68 @@

namespace base {

// PlatformThreadHandle should not be assumed to be a numeric type, since the
// standard intends to allow pthread_t to be a structure. This means you
// should not initialize it to a value, like 0. If it's a member variable, the
// constructor can safely "value initialize" using () in the initializer list.
#if defined(OS_WIN)
typedef DWORD PlatformThreadId;
typedef void* PlatformThreadHandle; // HANDLE
const PlatformThreadHandle kNullThreadHandle = NULL;
#elif defined(OS_POSIX)
typedef pthread_t PlatformThreadHandle;
const PlatformThreadHandle kNullThreadHandle = 0;
typedef pid_t PlatformThreadId;
#endif

const PlatformThreadId kInvalidThreadId = 0;
class PlatformThreadHandle {
public:
#if defined(OS_WIN)
typedef void* Handle;
#elif defined(OS_POSIX)
typedef pthread_t Handle;
#endif

PlatformThreadHandle()
: handle_(0),
id_(0) {
}

explicit PlatformThreadHandle(Handle handle)
: handle_(handle),
id_(0) {
}

PlatformThreadHandle(Handle handle,
PlatformThreadId id)
: handle_(handle),
id_(id) {
}

bool is_equal(const PlatformThreadHandle& other) {
return handle_ == other.handle_;
}

bool is_null() {
return !handle_;
}

Handle platform_handle() {
return handle_;
}

private:
friend class PlatformThread;

Handle handle_;
PlatformThreadId id_;
};

const PlatformThreadHandle kNullThreadHandle(0);
const PlatformThreadId kInvalidThreadId(0);


// Valid values for SetThreadPriority()
enum ThreadPriority{
kThreadPriority_Normal,
// Suitable for low-latency, glitch-resistant audio.
kThreadPriority_RealtimeAudio
kThreadPriority_RealtimeAudio,
// Suitable for threads which generate data for the display (at ~60Hz).
kThreadPriority_Display,
// Suitable for threads that shouldn't disrupt high priority work.
kThreadPriority_Background
};

// A namespace for low-level thread functions.
Expand All @@ -62,6 +103,9 @@ class BASE_EXPORT PlatformThread {
// Gets the current thread id, which may be useful for logging purposes.
static PlatformThreadId CurrentId();

// Get the current handle.
static PlatformThreadHandle CurrentHandle();

// Yield the current thread so another thread can be scheduled.
static void YieldCurrentThread();

Expand Down Expand Up @@ -106,8 +150,6 @@ class BASE_EXPORT PlatformThread {
// |thread_handle|.
static void Join(PlatformThreadHandle thread_handle);

// Sets the priority of the thread specified in |handle| to |priority|.
// This does not work on Linux, use CreateWithPriority() instead.
static void SetThreadPriority(PlatformThreadHandle handle,
ThreadPriority priority);

Expand Down
105 changes: 105 additions & 0 deletions base/threading/platform_thread_android.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/threading/platform_thread.h"

#include <errno.h>
#include <sys/resource.h>

#include "base/android/jni_android.h"
#include "base/android/thread_utils.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/threading/thread_id_name_manager.h"
#include "base/tracked_objects.h"
#include "jni/ThreadUtils_jni.h"

namespace base {

namespace {
int ThreadNiceValue(ThreadPriority priority) {
// These nice values are taken from Android, which uses nice
// values like linux, but defines some preset nice values.
// Process.THREAD_PRIORITY_AUDIO = -16
// Process.THREAD_PRIORITY_BACKGROUND = 10
// Process.THREAD_PRIORITY_DEFAULT = 0;
// Process.THREAD_PRIORITY_DISPLAY = -4;
// Process.THREAD_PRIORITY_FOREGROUND = -2;
// Process.THREAD_PRIORITY_LESS_FAVORABLE = 1;
// Process.THREAD_PRIORITY_LOWEST = 19;
// Process.THREAD_PRIORITY_MORE_FAVORABLE = -1;
// Process.THREAD_PRIORITY_URGENT_AUDIO = -19;
// Process.THREAD_PRIORITY_URGENT_DISPLAY = -8;
// We use -6 for display, but we may want to split this
// into urgent (-8) and non-urgent (-4).
static const int threadPriorityAudio = -16;
static const int threadPriorityBackground = 10;
static const int threadPriorityDefault = 0;
static const int threadPriorityDisplay = -6;
switch (priority) {
case kThreadPriority_RealtimeAudio:
return threadPriorityAudio;
case kThreadPriority_Background:
return threadPriorityBackground;
case kThreadPriority_Normal:
return threadPriorityDefault;
case kThreadPriority_Display:
return threadPriorityDisplay;
default:
NOTREACHED() << "Unknown priority.";
return 0;
}
}
} // namespace

//static
void PlatformThread::SetThreadPriority(PlatformThreadHandle handle,
ThreadPriority priority) {
// On Android, we set the Audio priority through JNI as Audio priority
// will also allow the process to run while it is backgrounded.
if (priority == kThreadPriority_RealtimeAudio) {
JNIEnv* env = base::android::AttachCurrentThread();
Java_ThreadUtils_setThreadPriorityAudio(env, PlatformThread::CurrentId());
return;
}

// setpriority(2) will set a thread's priority if it is passed a tid as
// the 'process identifier', not affecting the rest of the threads in the
// process. Setting this priority will only succeed if the user has been
// granted permission to adjust nice values on the system.
DCHECK_NE(handle.id_, kInvalidThreadId);
int kNiceSetting = ThreadNiceValue(priority);
if (setpriority(PRIO_PROCESS, handle.id_, kNiceSetting))
LOG(ERROR) << "Failed to set nice value of thread to " << kNiceSetting;
}

void PlatformThread::SetName(const char* name) {
ThreadIdNameManager::GetInstance()->SetName(CurrentId(), name);
tracked_objects::ThreadData::InitializeThreadContext(name);
}


void InitThreading() {
}

void InitOnThread() {
// Threads on linux/android may inherit their priority from the thread
// where they were created. This sets all new threads to the default.
PlatformThread::SetThreadPriority(PlatformThread::CurrentHandle(),
kThreadPriority_Normal);
}

void TerminateOnThread() {
base::android::DetachFromVM();
}

size_t GetDefaultThreadStackSize(const pthread_attr_t& attributes) {
return 0;
}

bool RegisterThreadUtils(JNIEnv* env) {
return RegisterNativesImpl(env);
}

} // namespace base
103 changes: 103 additions & 0 deletions base/threading/platform_thread_linux.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/threading/platform_thread.h"

#include <errno.h>
#include <sched.h>

#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/safe_strerror_posix.h"
#include "base/threading/thread_id_name_manager.h"
#include "base/threading/thread_restrictions.h"
#include "base/tracked_objects.h"

#if defined(OS_LINUX)
#include <sys/prctl.h>
#include <sys/resource.h>
#include <sys/syscall.h>
#include <sys/time.h>
#include <unistd.h>
#endif

namespace base {

namespace {
int ThreadNiceValue(ThreadPriority priority) {
static const int threadPriorityAudio = -10;
static const int threadPriorityBackground = 10;
static const int threadPriorityDefault = 0;
static const int threadPriorityDisplay = -6;
switch (priority) {
case kThreadPriority_RealtimeAudio:
return threadPriorityAudio;
case kThreadPriority_Background:
return threadPriorityBackground;
case kThreadPriority_Normal:
return threadPriorityDefault;
case kThreadPriority_Display:
return threadPriorityDisplay;
default:
NOTREACHED() << "Unknown priority.";
return 0;
}
}
} // namespace

// static
void PlatformThread::SetName(const char* name) {
ThreadIdNameManager::GetInstance()->SetName(CurrentId(), name);
tracked_objects::ThreadData::InitializeThreadContext(name);

#ifndef OS_NACL
// On linux we can get the thread names to show up in the debugger by setting
// the process name for the LWP. We don't want to do this for the main
// thread because that would rename the process, causing tools like killall
// to stop working.
if (PlatformThread::CurrentId() == getpid())
return;

// http://0pointer.de/blog/projects/name-your-threads.html
// Set the name for the LWP (which gets truncated to 15 characters).
// Note that glibc also has a 'pthread_setname_np' api, but it may not be
// available everywhere and it's only benefit over using prctl directly is
// that it can set the name of threads other than the current thread.
int err = prctl(PR_SET_NAME, name);
// We expect EPERM failures in sandboxed processes, just ignore those.
if (err < 0 && errno != EPERM)
DPLOG(ERROR) << "prctl(PR_SET_NAME)";
#endif
}

// static
void PlatformThread::SetThreadPriority(PlatformThreadHandle handle,
ThreadPriority priority) {
#ifndef OS_NACL
// setpriority(2) will set a thread's priority if it is passed a tid as
// the 'process identifier', not affecting the rest of the threads in the
// process. Setting this priority will only succeed if the user has been
// granted permission to adjust nice values on the system.
DCHECK_NE(handle.id_, kInvalidThreadId);
int kNiceSetting = ThreadNiceValue(priority);
if (setpriority(PRIO_PROCESS, handle.id_, kNiceSetting))
LOG(ERROR) << "Failed to set nice value of thread to " << kNiceSetting;
#endif
}

void InitThreading() {
}

void InitOnThread() {
}

void TerminateOnThread() {
}

size_t GetDefaultThreadStackSize(const pthread_attr_t& attributes) {
return 0;
}

} // namespace base
Loading

0 comments on commit a688c89

Please sign in to comment.