levin@chromium.org | 5c52868 | 2011-03-28 10:54:15 +0900 | [diff] [blame] | 1 | // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
license.bot | f003cfe | 2008-08-24 09:55:55 +0900 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 4 | |
brettw@chromium.org | 6139182 | 2011-01-01 05:02:16 +0900 | [diff] [blame] | 5 | #include "base/threading/platform_thread.h" |
brettw@google.com | e3c034a | 2008-08-08 03:31:40 +0900 | [diff] [blame] | 6 | |
paulg@google.com | c8eeb75 | 2008-08-13 10:20:26 +0900 | [diff] [blame] | 7 | #include <errno.h> |
deanm@google.com | c1c5cf5 | 2008-08-06 19:06:59 +0900 | [diff] [blame] | 8 | #include <sched.h> |
deanm@google.com | c1c5cf5 | 2008-08-06 19:06:59 +0900 | [diff] [blame] | 9 | |
willchan@chromium.org | 25726ef | 2010-11-20 05:34:18 +0900 | [diff] [blame] | 10 | #include "base/logging.h" |
levin@chromium.org | 5c52868 | 2011-03-28 10:54:15 +0900 | [diff] [blame] | 11 | #include "base/memory/scoped_ptr.h" |
willchan@chromium.org | 25726ef | 2010-11-20 05:34:18 +0900 | [diff] [blame] | 12 | #include "base/safe_strerror_posix.h" |
brettw@chromium.org | 5b5f5e0 | 2011-01-01 10:01:06 +0900 | [diff] [blame] | 13 | #include "base/threading/thread_restrictions.h" |
willchan@chromium.org | 25726ef | 2010-11-20 05:34:18 +0900 | [diff] [blame] | 14 | |
pinkerton@google.com | 44159e4 | 2008-08-14 08:20:03 +0900 | [diff] [blame] | 15 | #if defined(OS_MACOSX) |
| 16 | #include <mach/mach.h> |
mark@chromium.org | 74c0644 | 2010-05-08 03:30:01 +0900 | [diff] [blame] | 17 | #include <sys/resource.h> |
| 18 | #include <algorithm> |
evan@chromium.org | b1fe13a | 2010-06-11 07:55:17 +0900 | [diff] [blame] | 19 | #endif |
| 20 | |
| 21 | #if defined(OS_LINUX) |
abarth@chromium.org | a6ffcf4 | 2010-11-13 02:59:41 +0900 | [diff] [blame] | 22 | #include <dlfcn.h> |
evan@chromium.org | b1fe13a | 2010-06-11 07:55:17 +0900 | [diff] [blame] | 23 | #include <sys/prctl.h> |
tc@google.com | 0e5744d | 2008-08-14 08:55:02 +0900 | [diff] [blame] | 24 | #include <sys/syscall.h> |
pinkerton@google.com | 9d2fd8a | 2008-08-14 08:34:26 +0900 | [diff] [blame] | 25 | #include <unistd.h> |
pinkerton@google.com | 44159e4 | 2008-08-14 08:20:03 +0900 | [diff] [blame] | 26 | #endif |
| 27 | |
abarth@chromium.org | a6ffcf4 | 2010-11-13 02:59:41 +0900 | [diff] [blame] | 28 | #if defined(OS_NACL) |
| 29 | #include <sys/nacl_syscalls.h> |
| 30 | #endif |
| 31 | |
mmentovai@google.com | 4304cf9 | 2008-08-28 10:17:02 +0900 | [diff] [blame] | 32 | namespace base { |
brettw@chromium.org | 6139182 | 2011-01-01 05:02:16 +0900 | [diff] [blame] | 33 | |
| 34 | #if defined(OS_MACOSX) |
mmentovai@google.com | 4304cf9 | 2008-08-28 10:17:02 +0900 | [diff] [blame] | 35 | void InitThreading(); |
mmentovai@google.com | 4304cf9 | 2008-08-28 10:17:02 +0900 | [diff] [blame] | 36 | #endif |
| 37 | |
willchan@chromium.org | 25726ef | 2010-11-20 05:34:18 +0900 | [diff] [blame] | 38 | namespace { |
| 39 | |
| 40 | struct ThreadParams { |
| 41 | PlatformThread::Delegate* delegate; |
| 42 | bool joinable; |
| 43 | }; |
| 44 | |
brettw@chromium.org | 6139182 | 2011-01-01 05:02:16 +0900 | [diff] [blame] | 45 | void* ThreadFunc(void* params) { |
willchan@chromium.org | 25726ef | 2010-11-20 05:34:18 +0900 | [diff] [blame] | 46 | ThreadParams* thread_params = static_cast<ThreadParams*>(params); |
| 47 | PlatformThread::Delegate* delegate = thread_params->delegate; |
| 48 | if (!thread_params->joinable) |
| 49 | base::ThreadRestrictions::SetSingletonAllowed(false); |
| 50 | delete thread_params; |
maruel@chromium.org | 8fe7adc | 2009-03-04 00:01:12 +0900 | [diff] [blame] | 51 | delegate->ThreadMain(); |
darin@google.com | c18d7ae | 2008-08-21 18:46:32 +0900 | [diff] [blame] | 52 | return NULL; |
paulg@google.com | c8eeb75 | 2008-08-13 10:20:26 +0900 | [diff] [blame] | 53 | } |
| 54 | |
brettw@chromium.org | 6139182 | 2011-01-01 05:02:16 +0900 | [diff] [blame] | 55 | bool CreateThread(size_t stack_size, bool joinable, |
| 56 | PlatformThread::Delegate* delegate, |
| 57 | PlatformThreadHandle* thread_handle) { |
| 58 | #if defined(OS_MACOSX) |
| 59 | base::InitThreading(); |
| 60 | #endif // OS_MACOSX |
| 61 | |
| 62 | bool success = false; |
| 63 | pthread_attr_t attributes; |
| 64 | pthread_attr_init(&attributes); |
| 65 | |
| 66 | // Pthreads are joinable by default, so only specify the detached attribute if |
| 67 | // the thread should be non-joinable. |
| 68 | if (!joinable) { |
| 69 | pthread_attr_setdetachstate(&attributes, PTHREAD_CREATE_DETACHED); |
| 70 | } |
| 71 | |
| 72 | #if defined(OS_MACOSX) |
| 73 | // The Mac OS X default for a pthread stack size is 512kB. |
| 74 | // Libc-594.1.4/pthreads/pthread.c's pthread_attr_init uses |
| 75 | // DEFAULT_STACK_SIZE for this purpose. |
| 76 | // |
| 77 | // 512kB isn't quite generous enough for some deeply recursive threads that |
| 78 | // otherwise request the default stack size by specifying 0. Here, adopt |
| 79 | // glibc's behavior as on Linux, which is to use the current stack size |
| 80 | // limit (ulimit -s) as the default stack size. See |
| 81 | // glibc-2.11.1/nptl/nptl-init.c's __pthread_initialize_minimal_internal. To |
| 82 | // avoid setting the limit below the Mac OS X default or the minimum usable |
| 83 | // stack size, these values are also considered. If any of these values |
| 84 | // can't be determined, or if stack size is unlimited (ulimit -s unlimited), |
| 85 | // stack_size is left at 0 to get the system default. |
| 86 | // |
| 87 | // Mac OS X normally only applies ulimit -s to the main thread stack. On |
| 88 | // contemporary OS X and Linux systems alike, this value is generally 8MB |
| 89 | // or in that neighborhood. |
| 90 | if (stack_size == 0) { |
| 91 | size_t default_stack_size; |
| 92 | struct rlimit stack_rlimit; |
| 93 | if (pthread_attr_getstacksize(&attributes, &default_stack_size) == 0 && |
| 94 | getrlimit(RLIMIT_STACK, &stack_rlimit) == 0 && |
| 95 | stack_rlimit.rlim_cur != RLIM_INFINITY) { |
| 96 | stack_size = std::max(std::max(default_stack_size, |
| 97 | static_cast<size_t>(PTHREAD_STACK_MIN)), |
| 98 | static_cast<size_t>(stack_rlimit.rlim_cur)); |
| 99 | } |
| 100 | } |
| 101 | #endif // OS_MACOSX |
| 102 | |
| 103 | if (stack_size > 0) |
| 104 | pthread_attr_setstacksize(&attributes, stack_size); |
| 105 | |
| 106 | ThreadParams* params = new ThreadParams; |
| 107 | params->delegate = delegate; |
| 108 | params->joinable = joinable; |
| 109 | success = !pthread_create(thread_handle, &attributes, ThreadFunc, params); |
| 110 | |
| 111 | pthread_attr_destroy(&attributes); |
| 112 | if (!success) |
| 113 | delete params; |
| 114 | return success; |
| 115 | } |
| 116 | |
| 117 | } // namespace |
| 118 | |
pinkerton@google.com | 44159e4 | 2008-08-14 08:20:03 +0900 | [diff] [blame] | 119 | // static |
agl@chromium.org | 118a98a | 2009-01-23 09:25:29 +0900 | [diff] [blame] | 120 | PlatformThreadId PlatformThread::CurrentId() { |
darin@google.com | c18d7ae | 2008-08-21 18:46:32 +0900 | [diff] [blame] | 121 | // Pthreads doesn't have the concept of a thread ID, so we have to reach down |
| 122 | // into the kernel. |
| 123 | #if defined(OS_MACOSX) |
pinkerton@google.com | 44159e4 | 2008-08-14 08:20:03 +0900 | [diff] [blame] | 124 | return mach_thread_self(); |
| 125 | #elif defined(OS_LINUX) |
tc@google.com | 0e5744d | 2008-08-14 08:55:02 +0900 | [diff] [blame] | 126 | return syscall(__NR_gettid); |
evan@chromium.org | 875bb6e | 2009-12-29 09:32:52 +0900 | [diff] [blame] | 127 | #elif defined(OS_FREEBSD) |
| 128 | // TODO(BSD): find a better thread ID |
| 129 | return reinterpret_cast<int64>(pthread_self()); |
chromium@hybridsource.org | 8f85a6a | 2011-06-25 13:54:41 +0900 | [diff] [blame^] | 130 | #elif defined(OS_NACL) || defined(OS_SOLARIS) |
abarth@chromium.org | a6ffcf4 | 2010-11-13 02:59:41 +0900 | [diff] [blame] | 131 | return pthread_self(); |
pinkerton@google.com | 44159e4 | 2008-08-14 08:20:03 +0900 | [diff] [blame] | 132 | #endif |
| 133 | } |
| 134 | |
darin@google.com | c18d7ae | 2008-08-21 18:46:32 +0900 | [diff] [blame] | 135 | // static |
| 136 | void PlatformThread::YieldCurrentThread() { |
| 137 | sched_yield(); |
| 138 | } |
| 139 | |
| 140 | // static |
| 141 | void PlatformThread::Sleep(int duration_ms) { |
| 142 | struct timespec sleep_time, remaining; |
| 143 | |
| 144 | // Contains the portion of duration_ms >= 1 sec. |
| 145 | sleep_time.tv_sec = duration_ms / 1000; |
| 146 | duration_ms -= sleep_time.tv_sec * 1000; |
| 147 | |
| 148 | // Contains the portion of duration_ms < 1 sec. |
| 149 | sleep_time.tv_nsec = duration_ms * 1000 * 1000; // nanoseconds. |
| 150 | |
| 151 | while (nanosleep(&sleep_time, &remaining) == -1 && errno == EINTR) |
| 152 | sleep_time = remaining; |
| 153 | } |
| 154 | |
evan@chromium.org | b1fe13a | 2010-06-11 07:55:17 +0900 | [diff] [blame] | 155 | // Linux SetName is currently disabled, as we need to distinguish between |
| 156 | // helper threads (where it's ok to make this call) and the main thread |
| 157 | // (where making this call renames our process, causing tools like killall |
| 158 | // to stop working). |
| 159 | #if 0 && defined(OS_LINUX) |
darin@google.com | c18d7ae | 2008-08-21 18:46:32 +0900 | [diff] [blame] | 160 | // static |
deanm@google.com | 8f4440a | 2008-08-25 22:54:18 +0900 | [diff] [blame] | 161 | void PlatformThread::SetName(const char* name) { |
evan@chromium.org | b1fe13a | 2010-06-11 07:55:17 +0900 | [diff] [blame] | 162 | // http://0pointer.de/blog/projects/name-your-threads.html |
| 163 | |
| 164 | // glibc recently added support for pthread_setname_np, but it's not |
| 165 | // commonly available yet. So test for it at runtime. |
| 166 | int (*dynamic_pthread_setname_np)(pthread_t, const char*); |
| 167 | *reinterpret_cast<void**>(&dynamic_pthread_setname_np) = |
| 168 | dlsym(RTLD_DEFAULT, "pthread_setname_np"); |
| 169 | |
| 170 | if (dynamic_pthread_setname_np) { |
| 171 | // This limit comes from glibc, which gets it from the kernel |
| 172 | // (TASK_COMM_LEN). |
| 173 | const int kMaxNameLength = 15; |
| 174 | std::string shortened_name = std::string(name).substr(0, kMaxNameLength); |
| 175 | int err = dynamic_pthread_setname_np(pthread_self(), |
| 176 | shortened_name.c_str()); |
| 177 | if (err < 0) |
| 178 | LOG(ERROR) << "pthread_setname_np: " << safe_strerror(err); |
| 179 | } else { |
| 180 | // Implementing this function without glibc is simple enough. (We |
| 181 | // don't do the name length clipping as above because it will be |
| 182 | // truncated by the callee (see TASK_COMM_LEN above).) |
| 183 | int err = prctl(PR_SET_NAME, name); |
| 184 | if (err < 0) |
| 185 | PLOG(ERROR) << "prctl(PR_SET_NAME)"; |
| 186 | } |
darin@google.com | c18d7ae | 2008-08-21 18:46:32 +0900 | [diff] [blame] | 187 | } |
evan@chromium.org | b1fe13a | 2010-06-11 07:55:17 +0900 | [diff] [blame] | 188 | #elif defined(OS_MACOSX) |
| 189 | // Mac is implemented in platform_thread_mac.mm. |
| 190 | #else |
| 191 | // static |
joi@chromium.org | 5444bec | 2011-02-25 11:18:13 +0900 | [diff] [blame] | 192 | void PlatformThread::SetName(const char* /*name*/) { |
evan@chromium.org | b1fe13a | 2010-06-11 07:55:17 +0900 | [diff] [blame] | 193 | // Leave it unimplemented. |
| 194 | |
| 195 | // (This should be relatively simple to implement for the BSDs; I |
| 196 | // just don't have one handy to test the code on.) |
| 197 | } |
| 198 | #endif // defined(OS_LINUX) |
darin@google.com | c18d7ae | 2008-08-21 18:46:32 +0900 | [diff] [blame] | 199 | |
willchan@chromium.org | af61474 | 2009-03-06 08:23:59 +0900 | [diff] [blame] | 200 | // static |
| 201 | bool PlatformThread::Create(size_t stack_size, Delegate* delegate, |
| 202 | PlatformThreadHandle* thread_handle) { |
| 203 | return CreateThread(stack_size, true /* joinable thread */, |
| 204 | delegate, thread_handle); |
| 205 | } |
| 206 | |
| 207 | // static |
| 208 | bool PlatformThread::CreateNonJoinable(size_t stack_size, Delegate* delegate) { |
| 209 | PlatformThreadHandle unused; |
| 210 | |
| 211 | bool result = CreateThread(stack_size, false /* non-joinable thread */, |
| 212 | delegate, &unused); |
| 213 | return result; |
| 214 | } |
| 215 | |
darin@google.com | c18d7ae | 2008-08-21 18:46:32 +0900 | [diff] [blame] | 216 | // static |
| 217 | void PlatformThread::Join(PlatformThreadHandle thread_handle) { |
willchan@chromium.org | 354a52a | 2010-12-14 09:40:00 +0900 | [diff] [blame] | 218 | // Joining another thread may block the current thread for a long time, since |
| 219 | // the thread referred to by |thread_handle| may still be running long-lived / |
| 220 | // blocking tasks. |
| 221 | base::ThreadRestrictions::AssertIOAllowed(); |
darin@google.com | c18d7ae | 2008-08-21 18:46:32 +0900 | [diff] [blame] | 222 | pthread_join(thread_handle, NULL); |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 223 | } |
brettw@chromium.org | 6139182 | 2011-01-01 05:02:16 +0900 | [diff] [blame] | 224 | |
crogers@google.com | a6ec4f8 | 2011-05-18 05:01:25 +0900 | [diff] [blame] | 225 | #if !defined(OS_MACOSX) |
| 226 | // Mac OS X uses lower-level mach APIs |
| 227 | |
| 228 | // static |
| 229 | void PlatformThread::SetThreadPriority(PlatformThreadHandle, ThreadPriority) { |
| 230 | // TODO(crogers): implement |
| 231 | NOTIMPLEMENTED(); |
| 232 | } |
| 233 | #endif |
| 234 | |
brettw@chromium.org | 6139182 | 2011-01-01 05:02:16 +0900 | [diff] [blame] | 235 | } // namespace base |