OSDN Git Service

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