1/*
2Copyright (C) 2001 Paul Davis
3Copyright (C) 2004-2008 Grame
4
5This program is free software; you can redistribute it and/or modify
6it under the terms of the GNU Lesser General Public License as published by
7the Free Software Foundation; either version 2.1 of the License, or
8(at your option) any later version.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13GNU Lesser General Public License for more details.
14
15You should have received a copy of the GNU Lesser General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19*/
20
21
22#include "JackMachThread.h"
23#include "JackError.h"
24
25#ifdef MY_TARGET_OS_IPHONE
26#include "/Developer/Extras/CoreAudio/PublicUtility/CAHostTimeBase.h"
27#endif
28
29namespace Jack
30{
31
32int JackMachThread::SetThreadToPriority(jack_native_thread_t thread, UInt32 inPriority, Boolean inIsFixed, UInt64 period, UInt64 computation, UInt64 constraint)
33{
34    if (inPriority == 96) {
35        // REAL-TIME / TIME-CONSTRAINT THREAD
36        thread_time_constraint_policy_data_t	theTCPolicy;
37
38#ifdef MY_TARGET_OS_IPHONE
39        theTCPolicy.period = CAHostTimeBase::ConvertFromNanos(period);
40        theTCPolicy.computation = CAHostTimeBase::ConvertFromNanos(computation);
41        theTCPolicy.constraint = CAHostTimeBase::ConvertFromNanos(constraint);
42#else
43        theTCPolicy.period = AudioConvertNanosToHostTime(period);
44        theTCPolicy.computation = AudioConvertNanosToHostTime(computation);
45        theTCPolicy.constraint = AudioConvertNanosToHostTime(constraint);
46#endif
47        theTCPolicy.preemptible = true;
48        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);
49        jack_log("JackMachThread::thread_policy_set res = %ld", res);
50        return (res == KERN_SUCCESS) ? 0 : -1;
51    } else {
52        // OTHER THREADS
53        thread_extended_policy_data_t theFixedPolicy;
54        thread_precedence_policy_data_t thePrecedencePolicy;
55        SInt32 relativePriority;
56
57        // [1] SET FIXED / NOT FIXED
58        theFixedPolicy.timeshare = !inIsFixed;
59        thread_policy_set(pthread_mach_thread_np(thread), THREAD_EXTENDED_POLICY, (thread_policy_t)&theFixedPolicy, THREAD_EXTENDED_POLICY_COUNT);
60
61        // [2] SET PRECEDENCE
62        // N.B.: We expect that if thread A created thread B, and the program wishes to change
63        // the priority of thread B, then the call to change the priority of thread B must be
64        // made by thread A.
65        // This assumption allows us to use pthread_self() to correctly calculate the priority
66        // of the feeder thread (since precedency policy's importance is relative to the
67        // spawning thread's priority.)
68        relativePriority = inPriority - GetThreadSetPriority(pthread_self());
69
70        thePrecedencePolicy.importance = relativePriority;
71        kern_return_t res = thread_policy_set(pthread_mach_thread_np(thread), THREAD_PRECEDENCE_POLICY, (thread_policy_t) &thePrecedencePolicy, THREAD_PRECEDENCE_POLICY_COUNT);
72        jack_log("JackMachThread::thread_policy_set res = %ld", res);
73        return (res == KERN_SUCCESS) ? 0 : -1;
74    }
75}
76
77// returns the thread's priority as it was last set by the API
78UInt32 JackMachThread::GetThreadSetPriority(jack_native_thread_t thread)
79{
80    return GetThreadPriority(thread, THREAD_SET_PRIORITY);
81}
82
83// returns the thread's priority as it was last scheduled by the Kernel
84UInt32 JackMachThread::GetThreadScheduledPriority(jack_native_thread_t thread)
85{
86    return GetThreadPriority(thread, THREAD_SCHEDULED_PRIORITY);
87}
88
89UInt32 JackMachThread::GetThreadPriority(jack_native_thread_t thread, int inWhichPriority)
90{
91    thread_basic_info_data_t threadInfo;
92    policy_info_data_t thePolicyInfo;
93    unsigned int count;
94
95    // get basic info
96    count = THREAD_BASIC_INFO_COUNT;
97    thread_info(pthread_mach_thread_np(thread), THREAD_BASIC_INFO, (thread_info_t)&threadInfo, &count);
98
99    switch (threadInfo.policy) {
100        case POLICY_TIMESHARE:
101            count = POLICY_TIMESHARE_INFO_COUNT;
102            thread_info(pthread_mach_thread_np(thread), THREAD_SCHED_TIMESHARE_INFO, (thread_info_t)&(thePolicyInfo.ts), &count);
103            if (inWhichPriority == THREAD_SCHEDULED_PRIORITY) {
104                return thePolicyInfo.ts.cur_priority;
105            } else {
106                return thePolicyInfo.ts.base_priority;
107            }
108            break;
109
110        case POLICY_FIFO:
111            count = POLICY_FIFO_INFO_COUNT;
112            thread_info(pthread_mach_thread_np(thread), THREAD_SCHED_FIFO_INFO, (thread_info_t)&(thePolicyInfo.fifo), &count);
113            if ( (thePolicyInfo.fifo.depressed) && (inWhichPriority == THREAD_SCHEDULED_PRIORITY) ) {
114                return thePolicyInfo.fifo.depress_priority;
115            }
116            return thePolicyInfo.fifo.base_priority;
117            break;
118
119        case POLICY_RR:
120            count = POLICY_RR_INFO_COUNT;
121            thread_info(pthread_mach_thread_np(thread), THREAD_SCHED_RR_INFO, (thread_info_t)&(thePolicyInfo.rr), &count);
122            if ( (thePolicyInfo.rr.depressed) && (inWhichPriority == THREAD_SCHEDULED_PRIORITY) ) {
123                return thePolicyInfo.rr.depress_priority;
124            }
125            return thePolicyInfo.rr.base_priority;
126            break;
127    }
128
129    return 0;
130}
131
132int JackMachThread::GetParams(jack_native_thread_t thread, UInt64* period, UInt64* computation, UInt64* constraint)
133{
134    thread_time_constraint_policy_data_t theTCPolicy;
135    mach_msg_type_number_t count = THREAD_TIME_CONSTRAINT_POLICY_COUNT;
136    boolean_t get_default = false;
137
138    kern_return_t res = thread_policy_get(pthread_mach_thread_np(thread),
139                                          THREAD_TIME_CONSTRAINT_POLICY,
140                                          (thread_policy_t) & theTCPolicy,
141                                          &count,
142                                          &get_default);
143    if (res == KERN_SUCCESS) {
144    #ifdef MY_TARGET_OS_IPHONE
145        *period = CAHostTimeBase::ConvertToNanos(theTCPolicy.period);
146        *computation = CAHostTimeBase::ConvertToNanos(theTCPolicy.computation);
147        *constraint = CAHostTimeBase::ConvertToNanos(theTCPolicy.constraint);
148    #else
149        *period = AudioConvertHostTimeToNanos(theTCPolicy.period);
150        *computation = AudioConvertHostTimeToNanos(theTCPolicy.computation);
151        *constraint = AudioConvertHostTimeToNanos(theTCPolicy.constraint);
152    #endif
153
154        jack_log("JackMachThread::GetParams period = %ld computation = %ld constraint = %ld", long(*period / 1000.0f), long(*computation / 1000.0f), long(*constraint / 1000.0f));
155        return 0;
156    } else {
157        return -1;
158    }
159}
160
161int JackMachThread::Kill()
162{
163    if (fThread != (jack_native_thread_t)NULL)  { // If thread has been started
164        jack_log("JackMachThread::Kill");
165        mach_port_t machThread = pthread_mach_thread_np(fThread);
166        int res = (thread_terminate(machThread) == KERN_SUCCESS) ? 0 : -1;
167        fStatus = kIdle;
168        fThread = (jack_native_thread_t)NULL;
169        return res;
170    } else {
171        return -1;
172    }
173}
174
175int JackMachThread::AcquireRealTime()
176{
177    jack_log("JackMachThread::AcquireRealTime fPeriod = %ld fComputation = %ld fConstraint = %ld",
178             long(fPeriod / 1000), long(fComputation / 1000), long(fConstraint / 1000));
179    return (fThread != (jack_native_thread_t)NULL) ? AcquireRealTimeImp(fThread, fPeriod, fComputation, fConstraint) : -1;
180}
181
182int JackMachThread::AcquireSelfRealTime()
183{
184    jack_log("JackMachThread::AcquireSelfRealTime fPeriod = %ld fComputation = %ld fConstraint = %ld",
185             long(fPeriod / 1000), long(fComputation / 1000), long(fConstraint / 1000));
186    return AcquireRealTimeImp(pthread_self(), fPeriod, fComputation, fConstraint);
187}
188
189int JackMachThread::AcquireRealTime(int priority)
190{
191    fPriority = priority;
192    return AcquireRealTime();
193}
194
195int JackMachThread::AcquireSelfRealTime(int priority)
196{
197    fPriority = priority;
198    return AcquireSelfRealTime();
199}
200
201int JackMachThread::AcquireRealTimeImp(jack_native_thread_t thread, UInt64 period, UInt64 computation, UInt64 constraint)
202{
203    SetThreadToPriority(thread, 96, true, period, computation, constraint);
204    return 0;
205}
206
207int JackMachThread::DropRealTime()
208{
209    return (fThread != (jack_native_thread_t)NULL) ? DropRealTimeImp(fThread) : -1;
210}
211
212int JackMachThread::DropSelfRealTime()
213{
214    return DropRealTimeImp(pthread_self());
215}
216
217int JackMachThread::DropRealTimeImp(jack_native_thread_t thread)
218{
219    SetThreadToPriority(thread, 63, false, 0, 0, 0);
220    return 0;
221}
222
223void JackMachThread::SetParams(UInt64 period, UInt64 computation, UInt64 constraint)
224{
225    fPeriod = period;
226    fComputation = computation;
227    fConstraint = constraint;
228}
229
230} // end of namespace
231
232