OSDN Git Service

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