1 /************************** BEGIN thread.h **************************/
2 /************************************************************************
3  FAUST Architecture File
4  Copyright (C) 2020 GRAME, Centre National de Creation Musicale
5  ---------------------------------------------------------------------
6  This Architecture section is free software; you can redistribute it
7  and/or modify it under the terms of the GNU General Public License
8  as published by the Free Software Foundation; either version 3 of
9  the License, or (at your option) any later version.
10 
11  This program is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with this program; If not, see <http://www.gnu.org/licenses/>.
18 
19  EXCEPTION : As a special exception, you may create a larger work
20  that contains this FAUST architecture section and distribute
21  that work under terms of your choice, so long as this FAUST
22  architecture section is not modified.
23  ************************************************************************/
24 
25 #include <stdlib.h>
26 #include <assert.h>
27 #include <pthread.h>
28 #include <stdio.h>
29 #include <errno.h>
30 #include <string.h>
31 #include <semaphore.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <errno.h>
35 #include <fcntl.h>
36 #include <unistd.h>
37 #include <math.h>
38 
39 using namespace std;
40 
41 #ifdef __cplusplus
42 extern "C"
43 {
44 #endif
45 
46 #ifdef __ICC
47 #define INLINE __forceinline
48 #else
49 #define INLINE inline
50 #endif
51 
52 // Globals
53 
54 #define THREAD_POOL_SIZE 16
55 #define JACK_SCHED_POLICY SCHED_FIFO
56 
57 /* use 512KB stack per thread - the default is way too high to be feasible
58  * with mlockall() on many systems */
59 #define THREAD_STACK 524288
60 
61 typedef void* (ThreadHandler) (void* arg);
62 
GetPID()63 static int GetPID()
64 {
65 #ifdef WIN32
66     return  _getpid();
67 #else
68     return getpid();
69 #endif
70 }
71 
72 #ifdef __APPLE__
73 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacTypes.h>
74 #include <mach/thread_policy.h>
75 #include <mach/thread_act.h>
76 
77 #include <mach/thread_policy.h>
78 #include <mach/thread_act.h>
79 
80 #define THREAD_SET_PRIORITY         0
81 #define THREAD_SCHEDULED_PRIORITY   1
82 
83 static UInt32 GetThreadPriority(pthread_t thread, int inWhichPriority);
84 
85 // returns the thread's priority as it was last set by the API
GetThreadSetPriority(pthread_t thread)86 static UInt32 GetThreadSetPriority(pthread_t thread)
87 {
88     return GetThreadPriority(thread, THREAD_SET_PRIORITY);
89 }
90 
91 // returns the thread's priority as it was last scheduled by the Kernel
GetThreadScheduledPriority(pthread_t thread)92 static UInt32 GetThreadScheduledPriority(pthread_t thread)
93 {
94     return GetThreadPriority(thread, THREAD_SCHEDULED_PRIORITY);
95 }
96 
SetThreadToPriority(pthread_t thread,UInt32 inPriority,Boolean inIsFixed,UInt64 period,UInt64 computation,UInt64 constraint)97 static int SetThreadToPriority(pthread_t thread, UInt32 inPriority, Boolean inIsFixed, UInt64 period, UInt64 computation, UInt64 constraint)
98 {
99     if (inPriority == 96) {
100         // REAL-TIME / TIME-CONSTRAINT THREAD
101         thread_time_constraint_policy_data_t theTCPolicy;
102         theTCPolicy.period = period;
103         theTCPolicy.computation = computation;
104         theTCPolicy.constraint = constraint;
105         theTCPolicy.preemptible = true;
106         kern_return_t res = thread_policy_set(pthread_mach_thread_np(thread), THREAD_TIME_CONSTRAINT_POLICY, (thread_policy_t)&theTCPolicy, THREAD_TIME_CONSTRAINT_POLICY_COUNT);
107         return (res == KERN_SUCCESS) ? 0 : -1;
108     } else {
109         // OTHER THREADS
110         thread_extended_policy_data_t theFixedPolicy;
111         thread_precedence_policy_data_t thePrecedencePolicy;
112         SInt32 relativePriority;
113 
114         // [1] SET FIXED / NOT FIXED
115         theFixedPolicy.timeshare = !inIsFixed;
116         thread_policy_set(pthread_mach_thread_np(thread), THREAD_EXTENDED_POLICY, (thread_policy_t)&theFixedPolicy, THREAD_EXTENDED_POLICY_COUNT);
117 
118         // [2] SET PRECEDENCE
119         // N.B.: We expect that if thread A created thread B, and the program wishes to change
120         // the priority of thread B, then the call to change the priority of thread B must be
121         // made by thread A.
122         // This assumption allows us to use pthread_self() to correctly calculate the priority
123         // of the feeder thread (since precedency policy's importance is relative to the
124         // spawning thread's priority.)
125         relativePriority = inPriority - GetThreadSetPriority(pthread_self());
126 
127         thePrecedencePolicy.importance = relativePriority;
128         kern_return_t res = thread_policy_set(pthread_mach_thread_np(thread), THREAD_PRECEDENCE_POLICY, (thread_policy_t)&thePrecedencePolicy, THREAD_PRECEDENCE_POLICY_COUNT);
129         return (res == KERN_SUCCESS) ? 0 : -1;
130     }
131 }
132 
GetThreadPriority(pthread_t thread,int inWhichPriority)133 static UInt32 GetThreadPriority(pthread_t thread, int inWhichPriority)
134 {
135     thread_basic_info_data_t threadInfo;
136     policy_info_data_t thePolicyInfo;
137     unsigned int count;
138 
139     // get basic info
140     count = THREAD_BASIC_INFO_COUNT;
141     thread_info(pthread_mach_thread_np(thread), THREAD_BASIC_INFO, (thread_info_t)&threadInfo, &count);
142 
143     switch (threadInfo.policy) {
144         case POLICY_TIMESHARE:
145             count = POLICY_TIMESHARE_INFO_COUNT;
146             thread_info(pthread_mach_thread_np(thread), THREAD_SCHED_TIMESHARE_INFO, (thread_info_t)&(thePolicyInfo.ts), &count);
147             if (inWhichPriority == THREAD_SCHEDULED_PRIORITY) {
148                 return thePolicyInfo.ts.cur_priority;
149             } else {
150                 return thePolicyInfo.ts.base_priority;
151             }
152             break;
153 
154         case POLICY_FIFO:
155             count = POLICY_FIFO_INFO_COUNT;
156             thread_info(pthread_mach_thread_np(thread), THREAD_SCHED_FIFO_INFO, (thread_info_t)&(thePolicyInfo.fifo), &count);
157             if ((thePolicyInfo.fifo.depressed) && (inWhichPriority == THREAD_SCHEDULED_PRIORITY)) {
158                 return thePolicyInfo.fifo.depress_priority;
159             }
160             return thePolicyInfo.fifo.base_priority;
161             break;
162 
163         case POLICY_RR:
164             count = POLICY_RR_INFO_COUNT;
165             thread_info(pthread_mach_thread_np(thread), THREAD_SCHED_RR_INFO, (thread_info_t)&(thePolicyInfo.rr), &count);
166             if ((thePolicyInfo.rr.depressed) && (inWhichPriority == THREAD_SCHEDULED_PRIORITY)) {
167                 return thePolicyInfo.rr.depress_priority;
168             }
169             return thePolicyInfo.rr.base_priority;
170             break;
171     }
172 
173     return 0;
174 }
175 
GetParams(pthread_t thread,UInt64 * period,UInt64 * computation,UInt64 * constraint)176 static int GetParams(pthread_t thread, UInt64* period, UInt64* computation, UInt64* constraint)
177 {
178     thread_time_constraint_policy_data_t theTCPolicy;
179     mach_msg_type_number_t count = THREAD_TIME_CONSTRAINT_POLICY_COUNT;
180     boolean_t get_default = false;
181 
182     kern_return_t res = thread_policy_get(pthread_mach_thread_np(thread),
183                                           THREAD_TIME_CONSTRAINT_POLICY,
184                                           (thread_policy_t)&theTCPolicy,
185                                           &count,
186                                           &get_default);
187     if (res == KERN_SUCCESS) {
188         *period = theTCPolicy.period;
189         *computation = theTCPolicy.computation;
190         *constraint = theTCPolicy.constraint;
191         return 0;
192     } else {
193         return -1;
194     }
195 }
196 
197 static UInt64 period = 0;
198 static UInt64 computation = 0;
199 static UInt64 constraint = 0;
200 
GetRealTime()201 INLINE void GetRealTime()
202 {
203     if (period == 0) {
204         GetParams(pthread_self(), &period, &computation, &constraint);
205     }
206 }
207 
SetRealTime()208 INLINE void SetRealTime()
209 {
210     SetThreadToPriority(pthread_self(), 96, true, period, computation, constraint);
211 }
212 
213 #endif
214 
215 struct RunThread {
216 
217     pthread_t fThread;
218     sem_t* fSemaphore;
219     char fName[128];
220     bool fRealTime;
221 
222     #ifdef __APPLE__
223 
CancelRunThread224     void Cancel()
225     {
226         mach_port_t machThread = pthread_mach_thread_np(fThread);
227         thread_terminate(machThread);
228     }
229 
JoinRunThread230     void Join()
231     {
232         sem_post(fSemaphore);
233         pthread_join(fThread, NULL);
234     }
235 
236 
237     #endif
238 
239     #ifdef __linux__
240 
CancelRunThread241     void Cancel()
242     {
243         pthread_cancel(fThread);
244         pthread_join(fThread, NULL);
245     }
246 
JoinRunThread247     void Join()
248     {
249         sem_post(fSemaphore);
250         pthread_join(fThread, NULL);
251     }
252 
253     #endif
254 
RunThreadRunThread255     RunThread()
256     {
257         sprintf(fName, "faust_sem_%d_%p", GetPID(), this);
258         if ((fSemaphore = sem_open(fName, O_CREAT, 0777, 0)) == (sem_t*)SEM_FAILED) {
259             std::cerr << "Allocate: can't check in named semaphore name = " <<fName << " " << strerror(errno) << std::endl;
260             throw std::bad_alloc();
261         }
262     }
263 
~RunThreadRunThread264     ~RunThread()
265     {
266         sem_unlink(fName);
267         sem_close(fSemaphore);
268     }
269 
WaitRunThread270     void Wait()
271     {
272         while (sem_wait(fSemaphore) != 0) {}
273     }
274 
SignalRunThread275     void Signal()
276     {
277          sem_post(fSemaphore);
278     }
279 
StartRunThread280     int Start(bool realtime, ThreadHandler fun, void* arg)
281     {
282         pthread_attr_t attributes;
283         struct sched_param rt_param;
284         pthread_attr_init(&attributes);
285 
286         int priority = 70; // TODO
287         int res;
288 
289         if (realtime) {
290             fRealTime = true;
291         }else {
292             fRealTime = getenv("OMP_REALTIME") ? strtol(getenv("OMP_REALTIME"), NULL, 10) : true;
293         }
294 
295         if ((res = pthread_attr_setdetachstate(&attributes, PTHREAD_CREATE_JOINABLE))) {
296             printf("Cannot request joinable thread creation for real-time thread res = %d err = %s\n", res, strerror(errno));
297             return -1;
298         }
299 
300         if ((res = pthread_attr_setscope(&attributes, PTHREAD_SCOPE_SYSTEM))) {
301             printf("Cannot set scheduling scope for real-time thread res = %d err = %s\n", res, strerror(errno));
302             return -1;
303         }
304 
305         if (realtime) {
306 
307             if ((res = pthread_attr_setinheritsched(&attributes, PTHREAD_EXPLICIT_SCHED))) {
308                 printf("Cannot request explicit scheduling for RT thread res = %d err = %s\n", res, strerror(errno));
309                 return -1;
310             }
311 
312             if ((res = pthread_attr_setschedpolicy(&attributes, JACK_SCHED_POLICY))) {
313                 printf("Cannot set RR scheduling class for RT thread res = %d err = %s\n", res, strerror(errno));
314                 return -1;
315             }
316 
317             memset(&rt_param, 0, sizeof(rt_param));
318             rt_param.sched_priority = priority;
319 
320             if ((res = pthread_attr_setschedparam(&attributes, &rt_param))) {
321                 printf("Cannot set scheduling priority for RT thread res = %d err = %s\n", res, strerror(errno));
322                 return -1;
323             }
324 
325         } else {
326 
327             if ((res = pthread_attr_setinheritsched(&attributes, PTHREAD_INHERIT_SCHED))) {
328                 printf("Cannot request explicit scheduling for RT thread res = %d err = %s\n", res, strerror(errno));
329                 return -1;
330             }
331         }
332 
333         if ((res = pthread_attr_setstacksize(&attributes, THREAD_STACK))) {
334             printf("Cannot set thread stack size res = %d err = %s\n", res, strerror(errno));
335             return -1;
336         }
337 
338         if ((res = pthread_create(&fThread, &attributes, fun, arg))) {
339             printf("Cannot create thread res = %d err = %s\n", res, strerror(errno));
340             return -1;
341         }
342 
343         pthread_attr_destroy(&attributes);
344         return 0;
345     }
346 
StopRunThread347     void Stop()
348     {
349         Cancel();
350     }
351 
352 };
353 
load_program_source(const char * filename)354 char* load_program_source(const char* filename)
355 {
356     struct stat statbuf;
357     FILE* fh;
358     char* source;
359 
360     fh = fopen(filename, "r");
361     if (fh == 0) return 0;
362 
363     stat(filename, &statbuf);
364     source = (char*)malloc(statbuf.st_size + 1);
365     fread(source, statbuf.st_size, 1, fh);
366     source[statbuf.st_size] = '\0';
367 
368     return source;
369 }
370 
371 #ifdef __cplusplus
372 }
373 #endif
374 /************************** END thread.h **************************/
375