1 /* 2 * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include "thread_posix.h" 12 13 #include <errno.h> 14 #include <string.h> // strncpy 15 #include <time.h> // nanosleep 16 #include <unistd.h> 17 #ifdef WEBRTC_LINUX 18 #include <sys/types.h> 19 #include <sched.h> 20 #include <sys/syscall.h> 21 #include <linux/unistd.h> 22 #include <sys/prctl.h> 23 #endif 24 25 #include "event_wrapper.h" 26 #include "trace.h" 27 28 namespace webrtc { 29 extern "C" 30 { 31 static void* StartThread(void* lpParameter) 32 { 33 static_cast<ThreadPosix*>(lpParameter)->Run(); 34 return 0; 35 } 36 } 37 38 #if (defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID)) 39 static pid_t gettid() 40 { 41 #if defined(__NR_gettid) 42 return syscall(__NR_gettid); 43 #else 44 return -1; 45 #endif 46 } 47 #endif 48 49 ThreadWrapper* ThreadPosix::Create(ThreadRunFunction func, ThreadObj obj, 50 ThreadPriority prio, const char* threadName) 51 { 52 ThreadPosix* ptr = new ThreadPosix(func, obj, prio, threadName); 53 if (!ptr) 54 { 55 return NULL; 56 } 57 const int error = ptr->Construct(); 58 if (error) 59 { 60 delete ptr; 61 return NULL; 62 } 63 return ptr; 64 } 65 66 ThreadPosix::ThreadPosix(ThreadRunFunction func, ThreadObj obj, 67 ThreadPriority prio, const char* threadName) 68 : _runFunction(func), 69 _obj(obj), 70 _alive(false), 71 _dead(true), 72 _prio(prio), 73 _event(EventWrapper::Create()), 74 _setThreadName(false) 75 { 76 #ifdef WEBRTC_LINUX 77 _linuxPid = -1; 78 #endif 79 if (threadName != NULL) 80 { 81 _setThreadName = true; 82 strncpy(_name, threadName, kThreadMaxNameLength); 83 } 84 } 85 86 int ThreadPosix::Construct() 87 { 88 int result = 0; 89 #if !defined(WEBRTC_ANDROID) 90 // Enable immediate cancellation if requested, see Shutdown() 91 result = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); 92 if (result != 0) 93 { 94 return -1; 95 } 96 result = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); 97 if (result != 0) 98 { 99 return -1; 100 } 101 #endif 102 result = pthread_attr_init(&_attr); 103 if (result != 0) 104 { 105 return -1; 106 } 107 108 return 0; 109 } 110 111 ThreadPosix::~ThreadPosix() 112 { 113 pthread_attr_destroy(&_attr); 114 delete _event; 115 } 116 117 #define HAS_THREAD_ID !defined(MAC_IPHONE) && !defined(MAC_IPHONE_SIM) && \ 118 !defined(WEBRTC_MAC) && !defined(WEBRTC_MAC_INTEL) && \ 119 !defined(MAC_DYLIB) && !defined(MAC_INTEL_DYLIB) 120 #if HAS_THREAD_ID 121 bool ThreadPosix::Start(unsigned int& threadID) 122 #else 123 bool ThreadPosix::Start(unsigned int& /*threadID*/) 124 #endif 125 { 126 if (!_runFunction) 127 { 128 return false; 129 } 130 int result = pthread_attr_setdetachstate(&_attr, PTHREAD_CREATE_DETACHED); 131 // Set the stack stack size to 1M. 132 result |= pthread_attr_setstacksize(&_attr, 1024*1024); 133 #ifdef WEBRTC_THREAD_RR 134 const int policy = SCHED_RR; 135 #else 136 const int policy = SCHED_FIFO; 137 #endif 138 _event->Reset(); 139 result |= pthread_create(&_thread, &_attr, &StartThread, this); 140 if (result != 0) 141 { 142 return false; 143 } 144 145 // Wait up to 10 seconds for the OS to call the callback function. Prevents 146 // race condition if Stop() is called too quickly after start. 147 if (kEventSignaled != _event->Wait(WEBRTC_EVENT_10_SEC)) 148 { 149 // Timed out. Something went wrong. 150 _runFunction = NULL; 151 return false; 152 } 153 154 #if HAS_THREAD_ID 155 threadID = static_cast<unsigned int>(_thread); 156 #endif 157 sched_param param; 158 159 const int minPrio = sched_get_priority_min(policy); 160 const int maxPrio = sched_get_priority_max(policy); 161 if ((minPrio == EINVAL) || (maxPrio == EINVAL)) 162 { 163 return false; 164 } 165 166 switch (_prio) 167 { 168 case kLowPriority: 169 param.sched_priority = minPrio + 1; 170 break; 171 case kNormalPriority: 172 param.sched_priority = (minPrio + maxPrio) / 2; 173 break; 174 case kHighPriority: 175 param.sched_priority = maxPrio - 3; 176 break; 177 case kHighestPriority: 178 param.sched_priority = maxPrio - 2; 179 break; 180 case kRealtimePriority: 181 param.sched_priority = maxPrio - 1; 182 break; 183 default: 184 return false; 185 } 186 result = pthread_setschedparam(_thread, policy, ¶m); 187 if (result == EINVAL) 188 { 189 return false; 190 } 191 return true; 192 } 193 194 #if (defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID)) 195 bool ThreadPosix::SetAffinity(const int* processorNumbers, 196 const unsigned int amountOfProcessors) 197 { 198 if (!processorNumbers || (amountOfProcessors == 0)) 199 { 200 return false; 201 } 202 203 cpu_set_t mask; 204 CPU_ZERO(&mask); 205 206 for(unsigned int processor = 0; 207 processor < amountOfProcessors; 208 processor++) 209 { 210 CPU_SET(processorNumbers[processor], &mask); 211 } 212 const int result = sched_setaffinity(_linuxPid, (unsigned int)sizeof(mask), 213 &mask); 214 if (result != 0) 215 { 216 return false; 217 218 } 219 return true; 220 } 221 #else 222 // NOTE: On Mac OS X, use the Thread affinity API in 223 // /usr/include/mach/thread_policy.h: thread_policy_set and mach_thread_self() 224 // instead of Linux gettid() syscall. 225 bool ThreadPosix::SetAffinity(const int* , const unsigned int) 226 { 227 return false; 228 } 229 #endif 230 231 void ThreadPosix::SetNotAlive() 232 { 233 _alive = false; 234 } 235 236 bool ThreadPosix::Shutdown() 237 { 238 #if !defined(WEBRTC_ANDROID) 239 if (_thread && (0 != pthread_cancel(_thread))) 240 { 241 return false; 242 } 243 244 return true; 245 #else 246 return false; 247 #endif 248 } 249 250 bool ThreadPosix::Stop() 251 { 252 _alive = false; 253 254 // TODO (hellner) why not use an event here? 255 // Wait up to 10 seconds for the thread to terminate 256 for (int i = 0; i < 1000 && !_dead; i++) 257 { 258 timespec t; 259 t.tv_sec = 0; 260 t.tv_nsec = 10*1000*1000; 261 nanosleep(&t, NULL); 262 } 263 if (_dead) 264 { 265 return true; 266 } 267 else 268 { 269 return false; 270 } 271 } 272 273 void ThreadPosix::Run() 274 { 275 _alive = true; 276 _dead = false; 277 #ifdef WEBRTC_LINUX 278 if(_linuxPid == -1) 279 { 280 _linuxPid = gettid(); 281 } 282 #endif 283 // The event the Start() is waiting for. 284 _event->Set(); 285 286 if (_setThreadName) 287 { 288 #ifdef WEBRTC_LINUX 289 WEBRTC_TRACE(kTraceStateInfo, kTraceUtility,-1, 290 "Thread with id:%d name:%s started ", _linuxPid, _name); 291 prctl(PR_SET_NAME, (unsigned long)_name, 0, 0, 0); 292 #else 293 WEBRTC_TRACE(kTraceStateInfo, kTraceUtility,-1, 294 "Thread with name:%s started ", _name); 295 #endif 296 }else 297 { 298 #ifdef WEBRTC_LINUX 299 WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, -1, 300 "Thread with id:%d without name started", _linuxPid); 301 #else 302 WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, -1, 303 "Thread without name started"); 304 #endif 305 } 306 do 307 { 308 if (_runFunction) 309 { 310 if (!_runFunction(_obj)) 311 { 312 _alive = false; 313 } 314 } 315 else 316 { 317 _alive = false; 318 } 319 } 320 while (_alive); 321 322 if (_setThreadName) 323 { 324 // Don't set the name for the trace thread because it may cause a 325 // deadlock. TODO (hellner) there should be a better solution than 326 // coupling the thread and the trace class like this. 327 if (strcmp(_name, "Trace")) 328 { 329 WEBRTC_TRACE(kTraceStateInfo, kTraceUtility,-1, 330 "Thread with name:%s stopped", _name); 331 } 332 } 333 else 334 { 335 WEBRTC_TRACE(kTraceStateInfo, kTraceUtility,-1, 336 "Thread without name stopped"); 337 } 338 _dead = true; 339 } 340 } // namespace webrtc 341