]>
Commit | Line | Data |
---|---|---|
6b2a2aa4 CF |
1 | /* sched.cc: scheduler interface for Cygwin |
2 | ||
bc837d22 CF |
3 | Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2010, 2011, 2012 |
4 | Red Hat, Inc. | |
6b2a2aa4 CF |
5 | |
6 | Written by Robert Collins <rbtcollins@hotmail.com> | |
7 | ||
8 | This file is part of Cygwin. | |
9 | ||
10 | This software is a copyrighted work licensed under the terms of the | |
11 | Cygwin license. Please consult the file "CYGWIN_LICENSE" for | |
12 | details. */ | |
13 | ||
6b2a2aa4 | 14 | #include "winsup.h" |
ade47a34 | 15 | #include "miscfuncs.h" |
6b2a2aa4 | 16 | #include "cygerrno.h" |
6b2a2aa4 CF |
17 | #include "pinfo.h" |
18 | /* for getpid */ | |
19 | #include <unistd.h> | |
aff96307 CF |
20 | #include "registry.h" |
21 | ||
6b2a2aa4 CF |
22 | /* Win32 priority to UNIX priority Mapping. |
23 | For now, I'm just following the spec: any range of priorities is ok. | |
24 | There are probably many many issues with this... | |
462f4eff CF |
25 | |
26 | We don't want process's going realtime. Well, they probably could, but the issues | |
6b2a2aa4 | 27 | with avoiding the priority values 17-22 and 27-30 (not supported before win2k) |
462f4eff | 28 | make that inefficient. |
6b2a2aa4 | 29 | However to complicate things most unixes use lower is better priorities. |
462f4eff CF |
30 | |
31 | So we map -14 to 15, and 15 to 1 via (16- ((n+16) >> 1)) | |
6b2a2aa4 | 32 | we then map 1 to 15 to various process class and thread priority combinations |
462f4eff CF |
33 | |
34 | Then we need to look at the threads vi process priority. As win95 98 and NT 4 | |
6b2a2aa4 | 35 | Don't support opening threads cross-process (unless a thread HANDLE is passed around) |
462f4eff CF |
36 | for now, we'll just use the priority class. |
37 | ||
6b2a2aa4 | 38 | The code and logic are present to calculate the priority for thread |
462f4eff | 39 | , if a thread handle can be obtained. Alternatively, if the symbols wouldn't be |
6b2a2aa4 | 40 | resolved until they are used |
462f4eff | 41 | we could support this on windows 2000 and ME now, and just fall back to the |
6b2a2aa4 | 42 | class only on pre win2000 machines. |
462f4eff | 43 | |
6b2a2aa4 | 44 | Lastly, because we can't assume that the pid we're given are Windows pids, we can't |
462f4eff | 45 | alter non-cygwin started programs. |
6b2a2aa4 CF |
46 | */ |
47 | ||
48 | extern "C" | |
49 | { | |
50 | ||
51 | /* max priority for policy */ | |
52 | int | |
53 | sched_get_priority_max (int policy) | |
54 | { | |
55 | if (policy < 1 || policy > 3) | |
56 | { | |
57 | set_errno (EINVAL); | |
58 | return -1; | |
59 | } | |
60 | return -14; | |
61 | } | |
62 | ||
63 | /* min priority for policy */ | |
64 | int | |
65 | sched_get_priority_min (int policy) | |
66 | { | |
67 | if (policy < 1 || policy > 3) | |
68 | { | |
69 | set_errno (EINVAL); | |
70 | return -1; | |
71 | } | |
72 | return 15; | |
73 | } | |
74 | ||
5c83f260 RC |
75 | /* Check a scheduler parameter struct for valid settings */ |
76 | int | |
f0227ea3 | 77 | valid_sched_parameters (const struct sched_param *param) |
5c83f260 RC |
78 | { |
79 | if (param->sched_priority < -14 || param->sched_priority > 15) | |
80 | { | |
81 | return 0; | |
82 | } | |
83 | return -1; | |
84 | ||
85 | } | |
86 | ||
6b2a2aa4 CF |
87 | /* get sched params for process |
88 | ||
462f4eff | 89 | Note, I'm never returning EPERM, |
6b2a2aa4 | 90 | Always ESRCH. This is by design (If cygwin ever looks at paranoid security |
462f4eff | 91 | Walking the pid values is a known hole in some os's) |
6b2a2aa4 CF |
92 | */ |
93 | int | |
94 | sched_getparam (pid_t pid, struct sched_param *param) | |
95 | { | |
96 | pid_t localpid; | |
97 | int winpri; | |
98 | if (!param || pid < 0) | |
99 | { | |
100 | set_errno (EINVAL); | |
101 | return -1; | |
102 | } | |
103 | ||
104 | localpid = pid ? pid : getpid (); | |
105 | ||
106 | DWORD Class; | |
107 | int ThreadPriority; | |
108 | HANDLE process; | |
109 | pinfo p (localpid); | |
110 | ||
111 | /* get the class */ | |
112 | ||
113 | if (!p) | |
114 | { | |
115 | set_errno (ESRCH); | |
116 | return -1; | |
117 | } | |
118 | process = OpenProcess (PROCESS_QUERY_INFORMATION, FALSE, p->dwProcessId); | |
119 | if (!process) | |
120 | { | |
121 | set_errno (ESRCH); | |
122 | return -1; | |
123 | } | |
124 | Class = GetPriorityClass (process); | |
125 | CloseHandle (process); | |
126 | if (!Class) | |
127 | { | |
128 | set_errno (ESRCH); | |
129 | return -1; | |
130 | } | |
131 | ThreadPriority = THREAD_PRIORITY_NORMAL; | |
132 | ||
133 | /* calculate the unix priority. | |
134 | ||
135 | FIXME: windows 2000 supports ABOVE_NORMAL and BELOW_NORMAL class's | |
136 | So this logic just defaults those class factors to NORMAL in the calculations */ | |
137 | ||
138 | switch (Class) | |
139 | { | |
140 | case IDLE_PRIORITY_CLASS: | |
141 | switch (ThreadPriority) | |
142 | { | |
143 | case THREAD_PRIORITY_IDLE: | |
144 | winpri = 1; | |
145 | break; | |
146 | case THREAD_PRIORITY_LOWEST: | |
147 | winpri = 2; | |
148 | break; | |
149 | case THREAD_PRIORITY_BELOW_NORMAL: | |
150 | winpri = 3; | |
151 | break; | |
152 | case THREAD_PRIORITY_NORMAL: | |
153 | winpri = 4; | |
154 | break; | |
155 | case THREAD_PRIORITY_ABOVE_NORMAL: | |
156 | winpri = 5; | |
157 | break; | |
158 | case THREAD_PRIORITY_HIGHEST: | |
159 | default: | |
160 | winpri = 6; | |
161 | break; | |
162 | } | |
163 | break; | |
164 | case HIGH_PRIORITY_CLASS: | |
165 | switch (ThreadPriority) | |
166 | { | |
167 | case THREAD_PRIORITY_IDLE: | |
168 | winpri = 1; | |
169 | break; | |
170 | case THREAD_PRIORITY_LOWEST: | |
171 | winpri = 11; | |
172 | break; | |
173 | case THREAD_PRIORITY_BELOW_NORMAL: | |
174 | winpri = 12; | |
175 | break; | |
176 | case THREAD_PRIORITY_NORMAL: | |
177 | winpri = 13; | |
178 | break; | |
179 | case THREAD_PRIORITY_ABOVE_NORMAL: | |
180 | winpri = 14; | |
181 | break; | |
182 | case THREAD_PRIORITY_HIGHEST: | |
183 | default: | |
184 | winpri = 15; | |
185 | break; | |
186 | } | |
187 | break; | |
188 | case NORMAL_PRIORITY_CLASS: | |
189 | default: | |
190 | switch (ThreadPriority) | |
191 | { | |
192 | case THREAD_PRIORITY_IDLE: | |
193 | winpri = 1; | |
194 | break; | |
195 | case THREAD_PRIORITY_LOWEST: | |
196 | winpri = 7; | |
197 | break; | |
198 | case THREAD_PRIORITY_BELOW_NORMAL: | |
199 | winpri = 8; | |
200 | break; | |
201 | case THREAD_PRIORITY_NORMAL: | |
202 | winpri = 9; | |
203 | break; | |
204 | case THREAD_PRIORITY_ABOVE_NORMAL: | |
205 | winpri = 10; | |
206 | break; | |
207 | case THREAD_PRIORITY_HIGHEST: | |
208 | default: | |
209 | winpri = 11; | |
210 | break; | |
211 | } | |
212 | break; | |
213 | } | |
214 | ||
215 | /* reverse out winpri = (16- ((unixpri+16) >> 1)) */ | |
462f4eff | 216 | /* |
6b2a2aa4 CF |
217 | winpri-16 = - (unixpri +16 ) >> 1 |
218 | ||
219 | -(winpri-16) = unixpri +16 >> 1 | |
220 | (-(winpri-16)) << 1 = unixpri+16 | |
221 | ((-(winpri - 16)) << 1) - 16 = unixpri | |
222 | */ | |
223 | ||
224 | param->sched_priority = ((-(winpri - 16)) << 1) - 16; | |
225 | ||
226 | return 0; | |
227 | } | |
228 | ||
229 | /* get the scheduler for pid | |
230 | ||
231 | All process's on WIN32 run with SCHED_FIFO. | |
462f4eff | 232 | So we just give an answer. |
6b2a2aa4 CF |
233 | (WIN32 uses a multi queue FIFO). |
234 | */ | |
235 | int | |
236 | sched_getscheduler (pid_t pid) | |
237 | { | |
238 | if (pid < 0) | |
239 | return ESRCH; | |
240 | else | |
241 | return SCHED_FIFO; | |
242 | } | |
243 | ||
244 | /* get the time quantum for pid | |
245 | ||
aff96307 CF |
246 | Implemented only for NT systems, it fails and sets errno to ESRCH |
247 | for non-NT systems. | |
6b2a2aa4 CF |
248 | */ |
249 | int | |
250 | sched_rr_get_interval (pid_t pid, struct timespec *interval) | |
251 | { | |
aff96307 CF |
252 | static const char quantable[2][2][3] = |
253 | {{{12, 24, 36}, { 6, 12, 18}}, | |
254 | {{36, 36, 36}, {18, 18, 18}}}; | |
255 | /* FIXME: Clocktickinterval can be 15 ms for multi-processor system. */ | |
256 | static const int clocktickinterval = 10; | |
257 | static const int quantapertick = 3; | |
258 | ||
259 | HWND forwin; | |
260 | DWORD forprocid; | |
cca89be9 | 261 | DWORD vfindex, slindex, qindex, prisep; |
aff96307 CF |
262 | long nsec; |
263 | ||
aff96307 CF |
264 | forwin = GetForegroundWindow (); |
265 | if (!forwin) | |
266 | GetWindowThreadProcessId (forwin, &forprocid); | |
267 | else | |
268 | forprocid = 0; | |
269 | ||
b18cb86b CV |
270 | reg_key reg (HKEY_LOCAL_MACHINE, KEY_READ, L"SYSTEM", L"CurrentControlSet", |
271 | L"Control", L"PriorityControl", NULL); | |
aff96307 CF |
272 | if (reg.error ()) |
273 | { | |
274 | set_errno (ESRCH); | |
275 | return -1; | |
276 | } | |
cca89be9 | 277 | prisep = reg.get_dword (L"Win32PrioritySeparation", 2); |
aff96307 CF |
278 | pinfo pi (pid ? pid : myself->pid); |
279 | if (!pi) | |
280 | { | |
281 | set_errno (ESRCH); | |
282 | return -1; | |
283 | } | |
284 | ||
285 | if (pi->dwProcessId == forprocid) | |
286 | { | |
287 | qindex = prisep & 3; | |
288 | qindex = qindex == 3 ? 2 : qindex; | |
289 | } | |
290 | else | |
291 | qindex = 0; | |
292 | vfindex = ((prisep >> 2) & 3) % 3; | |
293 | if (vfindex == 0) | |
cb7e1879 | 294 | vfindex = wincap.is_server () || (prisep & 3) == 0 ? 1 : 0; |
aff96307 CF |
295 | else |
296 | vfindex -= 1; | |
297 | slindex = ((prisep >> 4) & 3) % 3; | |
298 | if (slindex == 0) | |
299 | slindex = wincap.is_server () ? 1 : 0; | |
300 | else | |
301 | slindex -= 1; | |
302 | ||
303 | nsec = quantable[vfindex][slindex][qindex] / quantapertick | |
304 | * clocktickinterval * 1000000; | |
305 | interval->tv_sec = nsec / 1000000000; | |
306 | interval->tv_nsec = nsec % 1000000000; | |
307 | ||
308 | return 0; | |
6b2a2aa4 CF |
309 | } |
310 | ||
311 | /* set the scheduling parameters */ | |
312 | int | |
313 | sched_setparam (pid_t pid, const struct sched_param *param) | |
314 | { | |
315 | pid_t localpid; | |
316 | int winpri; | |
317 | DWORD Class; | |
318 | int ThreadPriority; | |
319 | HANDLE process; | |
320 | ||
321 | if (!param || pid < 0) | |
322 | { | |
323 | set_errno (EINVAL); | |
324 | return -1; | |
325 | } | |
326 | ||
f0227ea3 | 327 | if (!valid_sched_parameters (param)) |
6b2a2aa4 CF |
328 | { |
329 | set_errno (EINVAL); | |
330 | return -1; | |
331 | } | |
332 | ||
333 | /* winpri = (16- ((unixpri+16) >> 1)) */ | |
334 | winpri = 16 - ((param->sched_priority + 16) >> 1); | |
335 | ||
336 | /* calculate our desired priority class and thread priority */ | |
337 | ||
338 | if (winpri < 7) | |
339 | Class = IDLE_PRIORITY_CLASS; | |
340 | else if (winpri > 10) | |
341 | Class = HIGH_PRIORITY_CLASS; | |
342 | else | |
343 | Class = NORMAL_PRIORITY_CLASS; | |
344 | ||
345 | switch (Class) | |
346 | { | |
347 | case IDLE_PRIORITY_CLASS: | |
348 | switch (winpri) | |
349 | { | |
350 | case 1: | |
351 | ThreadPriority = THREAD_PRIORITY_IDLE; | |
352 | break; | |
353 | case 2: | |
354 | ThreadPriority = THREAD_PRIORITY_LOWEST; | |
355 | break; | |
356 | case 3: | |
357 | ThreadPriority = THREAD_PRIORITY_BELOW_NORMAL; | |
358 | break; | |
359 | case 4: | |
360 | ThreadPriority = THREAD_PRIORITY_NORMAL; | |
361 | break; | |
362 | case 5: | |
363 | ThreadPriority = THREAD_PRIORITY_ABOVE_NORMAL; | |
364 | break; | |
365 | case 6: | |
366 | ThreadPriority = THREAD_PRIORITY_HIGHEST; | |
367 | break; | |
368 | } | |
369 | break; | |
370 | case NORMAL_PRIORITY_CLASS: | |
371 | switch (winpri) | |
372 | { | |
373 | case 7: | |
374 | ThreadPriority = THREAD_PRIORITY_LOWEST; | |
375 | break; | |
376 | case 8: | |
377 | ThreadPriority = THREAD_PRIORITY_BELOW_NORMAL; | |
378 | break; | |
379 | case 9: | |
380 | ThreadPriority = THREAD_PRIORITY_NORMAL; | |
381 | break; | |
382 | case 10: | |
383 | ThreadPriority = THREAD_PRIORITY_ABOVE_NORMAL; | |
384 | break; | |
385 | case 11: | |
386 | ThreadPriority = THREAD_PRIORITY_HIGHEST; | |
387 | break; | |
388 | } | |
389 | break; | |
390 | case HIGH_PRIORITY_CLASS: | |
391 | switch (winpri) | |
392 | { | |
393 | case 12: | |
394 | ThreadPriority = THREAD_PRIORITY_BELOW_NORMAL; | |
395 | break; | |
396 | case 13: | |
397 | ThreadPriority = THREAD_PRIORITY_NORMAL; | |
398 | break; | |
399 | case 14: | |
400 | ThreadPriority = THREAD_PRIORITY_ABOVE_NORMAL; | |
401 | break; | |
402 | case 15: | |
403 | ThreadPriority = THREAD_PRIORITY_HIGHEST; | |
404 | break; | |
405 | } | |
406 | break; | |
407 | } | |
408 | ||
409 | localpid = pid ? pid : getpid (); | |
410 | ||
411 | pinfo p (localpid); | |
412 | ||
413 | /* set the class */ | |
414 | ||
415 | if (!p) | |
416 | { | |
417 | set_errno (1); //ESRCH); | |
418 | return -1; | |
419 | } | |
420 | process = | |
421 | OpenProcess (PROCESS_SET_INFORMATION, FALSE, (DWORD) p->dwProcessId); | |
422 | if (!process) | |
423 | { | |
424 | set_errno (2); //ESRCH); | |
425 | return -1; | |
426 | } | |
427 | if (!SetPriorityClass (process, Class)) | |
428 | { | |
429 | CloseHandle (process); | |
430 | set_errno (EPERM); | |
431 | return -1; | |
432 | } | |
433 | CloseHandle (process); | |
434 | ||
435 | return 0; | |
436 | } | |
437 | ||
5c83f260 RC |
438 | /* we map -14 to 15, and 15 to 1 via (16- ((n+16) >> 1)). This lines up with the allowed |
439 | * valueswe return elsewhere in the sched* functions. We then map in groups of three to | |
440 | * allowed thread priority's. The reason for dropping accuracy while still returning | |
441 | * a wide range of values is to allow more flexible code in the future. | |
442 | */ | |
443 | int | |
f0227ea3 | 444 | sched_set_thread_priority (HANDLE thread, int priority) |
5c83f260 RC |
445 | { |
446 | int real_pri; | |
447 | real_pri = 16 - ((priority + 16) >> 1); | |
448 | if (real_pri <1 || real_pri > 15) | |
449 | return EINVAL; | |
462f4eff CF |
450 | |
451 | if (real_pri < 4) | |
5c83f260 RC |
452 | real_pri = THREAD_PRIORITY_LOWEST; |
453 | else if (real_pri < 7) | |
454 | real_pri = THREAD_PRIORITY_BELOW_NORMAL; | |
455 | else if (real_pri < 10) | |
456 | real_pri = THREAD_PRIORITY_NORMAL; | |
457 | else if (real_pri < 13) | |
458 | real_pri = THREAD_PRIORITY_ABOVE_NORMAL; | |
459 | else | |
460 | real_pri = THREAD_PRIORITY_HIGHEST; | |
461 | ||
f0227ea3 | 462 | if (!SetThreadPriority (thread, real_pri)) |
5c83f260 RC |
463 | /* invalid handle, no access are the only expected errors. */ |
464 | return EPERM; | |
465 | return 0; | |
466 | } | |
467 | ||
6b2a2aa4 CF |
468 | /* set the scheduler */ |
469 | int | |
470 | sched_setscheduler (pid_t pid, int policy, | |
471 | const struct sched_param *param) | |
472 | { | |
473 | /* on win32, you can't change the scheduler. Doh! */ | |
474 | set_errno (ENOSYS); | |
475 | return -1; | |
476 | } | |
477 | ||
478 | /* yield the cpu */ | |
479 | int | |
2f9ae2ed | 480 | sched_yield () |
6b2a2aa4 | 481 | { |
5e6a9154 | 482 | SwitchToThread (); |
6b2a2aa4 CF |
483 | return 0; |
484 | } | |
485 | } |