The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

483 lines
13KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-7 by Raw Material Software ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the
  7. GNU General Public License, as published by the Free Software Foundation;
  8. either version 2 of the License, or (at your option) any later version.
  9. JUCE is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with JUCE; if not, visit www.gnu.org/licenses or write to the
  15. Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  16. Boston, MA 02111-1307 USA
  17. ------------------------------------------------------------------------------
  18. If you'd like to release a closed-source product which uses JUCE, commercial
  19. licenses are also available: visit www.rawmaterialsoftware.com/juce for
  20. more information.
  21. ==============================================================================
  22. */
  23. #include "linuxincludes.h"
  24. #include <dlfcn.h>
  25. #include <sys/file.h>
  26. #include <sys/types.h>
  27. #include <sys/ptrace.h>
  28. #include "../../../src/juce_core/basics/juce_StandardHeader.h"
  29. BEGIN_JUCE_NAMESPACE
  30. #include "../../../src/juce_core/threads/juce_CriticalSection.h"
  31. #include "../../../src/juce_core/threads/juce_WaitableEvent.h"
  32. #include "../../../src/juce_core/threads/juce_InterProcessLock.h"
  33. #include "../../../src/juce_core/threads/juce_Thread.h"
  34. #include "../../../src/juce_core/threads/juce_Process.h"
  35. #include "../../../src/juce_core/io/files/juce_File.h"
  36. #include "../../../src/juce_core/basics/juce_SystemStats.h"
  37. #ifndef CPU_ISSET
  38. #undef SUPPORT_AFFINITIES
  39. #endif
  40. //==============================================================================
  41. void JUCE_API juce_threadEntryPoint (void*);
  42. void* threadEntryProc (void* value) throw()
  43. {
  44. // New threads start off as root when running suid
  45. Process::lowerPrivilege();
  46. juce_threadEntryPoint (value);
  47. return 0;
  48. }
  49. void* juce_createThread (void* userData) throw()
  50. {
  51. pthread_t handle = 0;
  52. if (pthread_create (&handle, 0, threadEntryProc, userData) == 0)
  53. {
  54. pthread_detach (handle);
  55. return (void*)handle;
  56. }
  57. return 0;
  58. }
  59. void juce_killThread (void* handle) throw()
  60. {
  61. if (handle != 0)
  62. pthread_cancel ((pthread_t)handle);
  63. }
  64. void juce_setCurrentThreadName (const String& /*name*/) throw()
  65. {
  66. }
  67. int Thread::getCurrentThreadId() throw()
  68. {
  69. return (int) pthread_self();
  70. }
  71. /*
  72. * This is all a bit non-ideal... the trouble is that on Linux you
  73. * need to call setpriority to affect the dynamic priority for
  74. * non-realtime processes, but this requires the pid, which is not
  75. * accessible from the pthread_t. We could get it by calling getpid
  76. * once each thread has started, but then we would need a list of
  77. * running threads etc etc.
  78. * Also there is no such thing as IDLE priority on Linux.
  79. * For the moment, map idle, low and normal process priorities to
  80. * SCHED_OTHER, with the thread priority ignored for these classes.
  81. * Map high priority processes to the lower half of the SCHED_RR
  82. * range, and realtime to the upper half
  83. */
  84. // priority 1 to 10 where 5=normal, 1=low. If the handle is 0, sets the
  85. // priority of the current thread
  86. void juce_setThreadPriority (void* handle, int priority) throw()
  87. {
  88. struct sched_param param;
  89. int policy, maxp, minp, pri;
  90. if (handle == 0)
  91. handle = (void*) pthread_self();
  92. if (pthread_getschedparam ((pthread_t) handle, &policy, &param) == 0
  93. && policy != SCHED_OTHER)
  94. {
  95. minp = sched_get_priority_min(policy);
  96. maxp = sched_get_priority_max(policy);
  97. pri = ((maxp - minp) / 2) * (priority - 1) / 9;
  98. if (param.__sched_priority >= (minp + (maxp - minp) / 2))
  99. // Realtime process priority
  100. param.__sched_priority = minp + ((maxp - minp) / 2) + pri;
  101. else
  102. // High process priority
  103. param.__sched_priority = minp + pri;
  104. param.sched_priority = jlimit (1, 127, 1 + (priority * 126) / 11);
  105. pthread_setschedparam ((pthread_t) handle, policy, &param);
  106. }
  107. }
  108. void Thread::setCurrentThreadAffinityMask (const uint32 affinityMask) throw()
  109. {
  110. #if SUPPORT_AFFINITIES
  111. cpu_set_t affinity;
  112. CPU_ZERO (&affinity);
  113. for (int i = 0; i < 32; ++i)
  114. if ((affinityMask & (1 << i)) != 0)
  115. CPU_SET (i, &affinity);
  116. /*
  117. N.B. If this line causes a compile error, then you've probably not got the latest
  118. version of glibc installed.
  119. If you don't want to update your copy of glibc and don't care about cpu affinities,
  120. then you can just disable all this stuff by removing the SUPPORT_AFFINITIES macro
  121. from the linuxincludes.h file.
  122. */
  123. sched_setaffinity (getpid(), sizeof (cpu_set_t), &affinity);
  124. sched_yield();
  125. #else
  126. /* affinities aren't supported because either the appropriate header files weren't found,
  127. or the SUPPORT_AFFINITIES macro was turned off in linuxheaders.h
  128. */
  129. jassertfalse
  130. #endif
  131. }
  132. void Thread::yield() throw()
  133. {
  134. sched_yield();
  135. }
  136. void JUCE_CALLTYPE Thread::sleep (int millisecs) throw()
  137. {
  138. struct timespec time;
  139. time.tv_sec = millisecs / 1000;
  140. time.tv_nsec = (millisecs % 1000) * 1000000;
  141. nanosleep (&time, 0);
  142. }
  143. //==============================================================================
  144. CriticalSection::CriticalSection() throw()
  145. {
  146. pthread_mutexattr_t atts;
  147. pthread_mutexattr_init (&atts);
  148. pthread_mutexattr_settype (&atts, PTHREAD_MUTEX_RECURSIVE);
  149. pthread_mutex_init (&internal, &atts);
  150. }
  151. CriticalSection::~CriticalSection() throw()
  152. {
  153. pthread_mutex_destroy (&internal);
  154. }
  155. void CriticalSection::enter() const throw()
  156. {
  157. pthread_mutex_lock (&internal);
  158. }
  159. bool CriticalSection::tryEnter() const throw()
  160. {
  161. return pthread_mutex_trylock (&internal) == 0;
  162. }
  163. void CriticalSection::exit() const throw()
  164. {
  165. pthread_mutex_unlock (&internal);
  166. }
  167. //==============================================================================
  168. struct EventStruct
  169. {
  170. pthread_cond_t condition;
  171. pthread_mutex_t mutex;
  172. bool triggered;
  173. };
  174. WaitableEvent::WaitableEvent() throw()
  175. {
  176. EventStruct* const es = new EventStruct();
  177. es->triggered = false;
  178. pthread_cond_init (&es->condition, 0);
  179. pthread_mutex_init (&es->mutex, 0);
  180. internal = es;
  181. }
  182. WaitableEvent::~WaitableEvent() throw()
  183. {
  184. EventStruct* const es = (EventStruct*)internal;
  185. pthread_cond_destroy (&es->condition);
  186. pthread_mutex_destroy (&es->mutex);
  187. delete es;
  188. }
  189. bool WaitableEvent::wait (const int timeOutMillisecs) const throw()
  190. {
  191. EventStruct* const es = (EventStruct*)internal;
  192. bool ok = true;
  193. pthread_mutex_lock (&es->mutex);
  194. if (! es->triggered)
  195. {
  196. if (timeOutMillisecs < 0)
  197. {
  198. pthread_cond_wait (&es->condition, &es->mutex);
  199. }
  200. else
  201. {
  202. struct timespec time;
  203. struct timeval t;
  204. int timeout = 0;
  205. gettimeofday (&t, 0);
  206. time.tv_sec = t.tv_sec + (timeOutMillisecs / 1000);
  207. time.tv_nsec = (t.tv_usec + ((timeOutMillisecs % 1000) * 1000)) * 1000;
  208. while (time.tv_nsec >= 1000000000)
  209. {
  210. time.tv_nsec -= 1000000000;
  211. time.tv_sec++;
  212. }
  213. while (! timeout)
  214. {
  215. timeout = pthread_cond_timedwait (&es->condition, &es->mutex, &time);
  216. if (! timeout)
  217. // Success
  218. break;
  219. if (timeout == EINTR)
  220. // Go round again
  221. timeout = 0;
  222. }
  223. }
  224. ok = es->triggered;
  225. }
  226. es->triggered = false;
  227. pthread_mutex_unlock (&es->mutex);
  228. return ok;
  229. }
  230. void WaitableEvent::signal() const throw()
  231. {
  232. EventStruct* const es = (EventStruct*)internal;
  233. pthread_mutex_lock (&es->mutex);
  234. es->triggered = true;
  235. pthread_cond_signal (&es->condition);
  236. pthread_mutex_unlock (&es->mutex);
  237. }
  238. void WaitableEvent::reset() const throw()
  239. {
  240. EventStruct* const es = (EventStruct*)internal;
  241. pthread_mutex_lock (&es->mutex);
  242. es->triggered = false;
  243. pthread_mutex_unlock (&es->mutex);
  244. }
  245. //==============================================================================
  246. // sets the process to 0=low priority, 1=normal, 2=high, 3=realtime
  247. void Process::setPriority (ProcessPriority prior)
  248. {
  249. struct sched_param param;
  250. int policy, maxp, minp;
  251. const int p = (int) prior;
  252. if (p <= 1)
  253. policy = SCHED_OTHER;
  254. else
  255. policy = SCHED_RR;
  256. minp = sched_get_priority_min (policy);
  257. maxp = sched_get_priority_max (policy);
  258. if (p < 2)
  259. param.__sched_priority = 0;
  260. else if (p == 2 )
  261. // Set to middle of lower realtime priority range
  262. param.__sched_priority = minp + (maxp - minp) / 4;
  263. else
  264. // Set to middle of higher realtime priority range
  265. param.__sched_priority = minp + (3 * (maxp - minp) / 4);
  266. pthread_setschedparam (pthread_self(), policy, &param);
  267. }
  268. void Process::terminate()
  269. {
  270. exit (0);
  271. }
  272. bool JUCE_CALLTYPE juce_isRunningUnderDebugger() throw()
  273. {
  274. static char testResult = 0;
  275. if (testResult == 0)
  276. {
  277. testResult = (ptrace (PTRACE_TRACEME, 0, 0, 0) < 0) ? 1 : -1;
  278. if (testResult < 0)
  279. ptrace (PTRACE_DETACH, 0, (caddr_t) 1, 0);
  280. }
  281. return testResult > 0;
  282. }
  283. bool JUCE_CALLTYPE Process::isRunningUnderDebugger() throw()
  284. {
  285. return juce_isRunningUnderDebugger();
  286. }
  287. void Process::raisePrivilege()
  288. {
  289. // If running suid root, change effective user
  290. // to root
  291. if (geteuid() != 0 && getuid() == 0)
  292. {
  293. setreuid (geteuid(), getuid());
  294. setregid (getegid(), getgid());
  295. }
  296. }
  297. void Process::lowerPrivilege()
  298. {
  299. // If runing suid root, change effective user
  300. // back to real user
  301. if (geteuid() == 0 && getuid() != 0)
  302. {
  303. setreuid (geteuid(), getuid());
  304. setregid (getegid(), getgid());
  305. }
  306. }
  307. #if JUCE_BUILD_GUI_CLASSES
  308. void* Process::loadDynamicLibrary (const String& name)
  309. {
  310. return dlopen ((const char*) name.toUTF8(), RTLD_LOCAL | RTLD_NOW);
  311. }
  312. void Process::freeDynamicLibrary (void* handle)
  313. {
  314. dlclose(handle);
  315. }
  316. void* Process::getProcedureEntryPoint (void* libraryHandle, const String& procedureName)
  317. {
  318. return dlsym (libraryHandle, (const char*) procedureName);
  319. }
  320. #endif
  321. //==============================================================================
  322. InterProcessLock::InterProcessLock (const String& name_) throw()
  323. : internal (0),
  324. name (name_),
  325. reentrancyLevel (0)
  326. {
  327. const File tempDir (File::getSpecialLocation (File::tempDirectory));
  328. const File temp (tempDir.getChildFile (name));
  329. temp.create();
  330. internal = (void*) open (temp.getFullPathName().toUTF8(), 'a');
  331. }
  332. InterProcessLock::~InterProcessLock() throw()
  333. {
  334. while (reentrancyLevel > 0)
  335. this->exit();
  336. #if JUCE_64BIT
  337. close ((long long) internal);
  338. #else
  339. close ((int) internal);
  340. #endif
  341. }
  342. bool InterProcessLock::enter (const int timeOutMillisecs) throw()
  343. {
  344. if (internal == 0)
  345. return false;
  346. if (reentrancyLevel != 0)
  347. return true;
  348. if (timeOutMillisecs <= 0)
  349. {
  350. if (flock ((long) internal,
  351. timeOutMillisecs < 0 ? LOCK_EX
  352. : (LOCK_EX | LOCK_NB)) == 0)
  353. {
  354. ++reentrancyLevel;
  355. return true;
  356. }
  357. }
  358. else
  359. {
  360. const int64 endTime = Time::currentTimeMillis() + timeOutMillisecs;
  361. for (;;)
  362. {
  363. if (flock ((long) internal, LOCK_EX | LOCK_NB) == 0)
  364. {
  365. ++reentrancyLevel;
  366. return true;
  367. }
  368. if (Time::currentTimeMillis() >= endTime)
  369. break;
  370. Thread::sleep (10);
  371. }
  372. }
  373. return false;
  374. }
  375. void InterProcessLock::exit() throw()
  376. {
  377. if (reentrancyLevel > 0 && internal != 0)
  378. {
  379. --reentrancyLevel;
  380. const int result = flock ((long) internal, LOCK_UN);
  381. (void) result;
  382. jassert (result == 0);
  383. }
  384. }
  385. END_JUCE_NAMESPACE