Cygwin: strptime: add release note
[newlib-cygwin.git] / winsup / cygwin / sched.cc
blob845fcef5702cb5c963111413078de27bd56071e0
1 /* sched.cc: scheduler interface for Cygwin
3 Written by Robert Collins <rbtcollins@hotmail.com>
5 This file is part of Cygwin.
7 This software is a copyrighted work licensed under the terms of the
8 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
9 details. */
11 #include "winsup.h"
12 #include "miscfuncs.h"
13 #include "cygerrno.h"
14 #include "pinfo.h"
15 #include "clock.h"
16 /* for getpid */
17 #include <unistd.h>
18 #include <sys/param.h>
19 #include "registry.h"
21 /* Win32 priority to UNIX priority Mapping. */
23 extern "C"
26 /* We support prio values from 1 to 32. This is marginally in line with Linux
27 (1 - 99) and matches the POSIX requirement to support at least 32 priority
28 values. */
30 /* max priority for policy */
31 int
32 sched_get_priority_max (int policy)
34 switch (policy)
36 case SCHED_FIFO:
37 case SCHED_RR:
38 case SCHED_OTHER:
39 return 32;
41 set_errno (EINVAL);
42 return -1;
45 /* min priority for policy */
46 int
47 sched_get_priority_min (int policy)
49 switch (policy)
51 case SCHED_FIFO:
52 case SCHED_RR:
53 case SCHED_OTHER:
54 return 1;
56 set_errno (EINVAL);
57 return -1;
60 /* Check a scheduler parameter struct for valid settings */
61 bool
62 valid_sched_parameters (const struct sched_param *param)
64 return param->sched_priority >= 1 && param->sched_priority <= 32;
67 /* get sched params for process
69 Note, we're never returning EPERM, always ESRCH. This is by design.
70 Walking the pid values is a known hole in some OSes. */
71 int
72 sched_getparam (pid_t pid, struct sched_param *param)
74 pid_t localpid;
75 if (!param || pid < 0)
77 set_errno (EINVAL);
78 return -1;
81 localpid = pid ? pid : getpid ();
83 DWORD pclass;
84 HANDLE process;
85 pinfo p (localpid);
87 /* get the class */
88 if (!p)
90 set_errno (ESRCH);
91 return -1;
93 process = OpenProcess (PROCESS_QUERY_LIMITED_INFORMATION, FALSE,
94 p->dwProcessId);
95 if (!process)
97 set_errno (ESRCH);
98 return -1;
100 pclass = GetPriorityClass (process);
101 CloseHandle (process);
102 if (!pclass)
104 set_errno (ESRCH);
105 return -1;
107 /* calculate the unix priority. */
108 switch (pclass)
110 case IDLE_PRIORITY_CLASS:
111 param->sched_priority = 3;
112 break;
113 case BELOW_NORMAL_PRIORITY_CLASS:
114 param->sched_priority = 9;
115 break;
116 case NORMAL_PRIORITY_CLASS:
117 default:
118 param->sched_priority = 15;
119 break;
120 case ABOVE_NORMAL_PRIORITY_CLASS:
121 param->sched_priority = 21;
122 break;
123 case HIGH_PRIORITY_CLASS:
124 param->sched_priority = 27;
125 break;
126 case REALTIME_PRIORITY_CLASS:
127 param->sched_priority = 32;
128 break;
131 return 0;
134 /* get the scheduler for pid
136 All process's on WIN32 run with SCHED_FIFO. So we just give an answer.
137 (WIN32 uses a multi queue FIFO).
140 sched_getscheduler (pid_t pid)
142 if (pid < 0)
143 return ESRCH;
144 else
145 return SCHED_FIFO;
148 /* get the time quantum for pid */
150 sched_rr_get_interval (pid_t pid, struct timespec *interval)
152 static const char quantable[2][2][3] =
153 {{{12, 24, 36}, { 6, 12, 18}},
154 {{36, 36, 36}, {18, 18, 18}}};
155 /* FIXME: Clocktickinterval can be 15 ms for multi-processor system. */
156 static const int clocktickinterval = 10;
157 static const int quantapertick = 3;
159 HWND forwin;
160 DWORD forprocid;
161 DWORD vfindex, slindex, qindex, prisep;
162 long nsec;
164 forwin = GetForegroundWindow ();
165 if (!forwin)
166 GetWindowThreadProcessId (forwin, &forprocid);
167 else
168 forprocid = 0;
170 reg_key reg (HKEY_LOCAL_MACHINE, KEY_READ, L"SYSTEM", L"CurrentControlSet",
171 L"Control", L"PriorityControl", NULL);
172 if (reg.error ())
174 set_errno (ESRCH);
175 return -1;
177 prisep = reg.get_dword (L"Win32PrioritySeparation", 2);
178 pinfo pi (pid ? pid : myself->pid);
179 if (!pi)
181 set_errno (ESRCH);
182 return -1;
185 if (pi->dwProcessId == forprocid)
187 qindex = prisep & 3;
188 qindex = qindex == 3 ? 2 : qindex;
190 else
191 qindex = 0;
192 vfindex = ((prisep >> 2) & 3) % 3;
193 if (vfindex == 0)
194 vfindex = wincap.is_server () || (prisep & 3) == 0 ? 1 : 0;
195 else
196 vfindex -= 1;
197 slindex = ((prisep >> 4) & 3) % 3;
198 if (slindex == 0)
199 slindex = wincap.is_server () ? 1 : 0;
200 else
201 slindex -= 1;
203 nsec = quantable[vfindex][slindex][qindex] / quantapertick
204 * clocktickinterval * (NSPERSEC / MSPERSEC);
205 interval->tv_sec = nsec / NSPERSEC;
206 interval->tv_nsec = nsec % NSPERSEC;
208 return 0;
211 /* set the scheduling parameters */
213 sched_setparam (pid_t pid, const struct sched_param *param)
215 pid_t localpid;
216 int pri;
217 DWORD pclass;
218 HANDLE process;
220 if (!param || pid < 0)
222 set_errno (EINVAL);
223 return -1;
226 if (!valid_sched_parameters (param))
228 set_errno (EINVAL);
229 return -1;
232 pri = param->sched_priority;
234 /* calculate our desired priority class. We only reserve a small area
235 (31/32) for realtime priority. */
236 if (pri <= 6)
237 pclass = IDLE_PRIORITY_CLASS;
238 else if (pri <= 12)
239 pclass = BELOW_NORMAL_PRIORITY_CLASS;
240 else if (pri <= 18)
241 pclass = NORMAL_PRIORITY_CLASS;
242 else if (pri <= 24)
243 pclass = ABOVE_NORMAL_PRIORITY_CLASS;
244 else if (pri <= 30)
245 pclass = HIGH_PRIORITY_CLASS;
246 else
247 pclass = REALTIME_PRIORITY_CLASS;
249 localpid = pid ? pid : getpid ();
251 pinfo p (localpid);
253 /* set the class */
255 if (!p)
257 set_errno (ESRCH);
258 return -1;
260 process = OpenProcess (PROCESS_SET_INFORMATION, FALSE, p->dwProcessId);
261 if (!process)
263 set_errno (ESRCH);
264 return -1;
266 if (!SetPriorityClass (process, pclass))
268 CloseHandle (process);
269 set_errno (EPERM);
270 return -1;
272 CloseHandle (process);
274 return 0;
277 /* POSIX thread priorities loosely compare to Windows thread base priorities.
279 Base priority is a function of process priority class and thread priority.
280 https://msdn.microsoft.com/en-us/library/windows/desktop/ms685100%28v=vs.85%29.aspx
282 Note 1:
284 We deliberately handle the REALTIME prority class the same as the HIGH
285 priority class. Realtime has it's own range from 16 to 31 so half the
286 arena is reserved for REALTIME. The problem is that this isn't visible
287 nor expected in the POSIX scenario. Therefore we hide this here and
288 fold REALTIME into HIGH.
290 Note 2:
292 sched_get_thread_priority is only called internally and only for threads
293 of the current process, with no good reason for the caller to fail.
294 Therefore it never returns an error but a valid priority (base value
295 equivalent to process priority class + THREAD_PRIORITY_NORMAL...
297 Note 3:
299 ...multiplied by 2 to stretch the priorities over the entire range 1 - 32.
302 static int
303 sched_base_prio_from_win_prio_class (DWORD pclass)
305 int base;
307 switch (pclass)
309 case IDLE_PRIORITY_CLASS:
310 base = 4;
311 break;
312 case BELOW_NORMAL_PRIORITY_CLASS:
313 base = 6;
314 break;
315 case NORMAL_PRIORITY_CLASS:
316 default:
317 base = 8;
318 break;
319 case ABOVE_NORMAL_PRIORITY_CLASS:
320 base = 10;
321 break;
322 case HIGH_PRIORITY_CLASS:
323 case REALTIME_PRIORITY_CLASS: /* See above note 1 */
324 base = 13;
325 break;
327 return base;
331 sched_get_thread_priority (HANDLE thread)
333 int tprio;
334 DWORD pclass;
335 int priority;
337 tprio = GetThreadPriority (thread);
338 pclass = GetPriorityClass (GetCurrentProcess ());
339 switch (tprio)
341 case THREAD_PRIORITY_ERROR_RETURN:
342 priority = sched_base_prio_from_win_prio_class (pclass);
343 break;
344 case THREAD_PRIORITY_IDLE:
345 priority = 1;
346 break;
347 case THREAD_PRIORITY_TIME_CRITICAL:
348 priority = 15;
349 break;
350 default:
351 priority = tprio + sched_base_prio_from_win_prio_class (pclass);
352 break;
354 return priority << 1; /* See above note 3 */
358 sched_set_thread_priority (HANDLE thread, int priority)
360 DWORD pclass;
361 int tprio;
363 pclass = GetPriorityClass (GetCurrentProcess ());
364 if (!pclass)
365 return EPERM;
366 if (priority < 1 || priority > 32)
367 return EINVAL;
369 priority >>= 1; /* See above note 3 */
370 if (priority < 1)
371 priority = 1;
372 else if (priority > 15)
373 priority = 15;
375 if (priority == 1)
376 tprio = THREAD_PRIORITY_IDLE;
377 else if (priority == 15)
378 tprio = THREAD_PRIORITY_TIME_CRITICAL;
379 else
381 tprio = priority - sched_base_prio_from_win_prio_class (pclass);
382 /* Intermediate values only allowed in REALTIME_PRIORITY_CLASS. */
383 if (pclass != REALTIME_PRIORITY_CLASS)
385 if (tprio < THREAD_PRIORITY_LOWEST)
386 tprio = THREAD_PRIORITY_LOWEST;
387 else if (tprio > THREAD_PRIORITY_HIGHEST)
388 tprio = THREAD_PRIORITY_HIGHEST;
391 if (!SetThreadPriority (thread, tprio))
392 /* invalid handle, no access are the only expected errors. */
393 return EPERM;
394 return 0;
397 /* set the scheduler */
399 sched_setscheduler (pid_t pid, int policy,
400 const struct sched_param *param)
402 /* on win32, you can't change the scheduler. Doh! */
403 set_errno (ENOSYS);
404 return -1;
407 /* yield the cpu */
409 sched_yield ()
411 SwitchToThread ();
412 return 0;
414 EXPORT_ALIAS (sched_yield, pthread_yield)
417 sched_getcpu ()
419 PROCESSOR_NUMBER pnum;
421 GetCurrentProcessorNumberEx (&pnum);
422 return pnum.Group * __get_cpus_per_group () + pnum.Number;
425 /* construct an affinity mask with just the 'count' lower-order bits set */
426 static __cpu_mask
427 groupmask (int count)
429 if (count >= (int) (NBBY * sizeof (__cpu_mask)))
430 return ~(__cpu_mask) 0;
431 else
432 return ((__cpu_mask) 1 << count) - 1;
435 /* return the affinity mask of the indicated group from the given cpu set */
436 static __cpu_mask
437 getgroup (size_t sizeof_set, const cpu_set_t *set, int groupnum)
439 int groupsize = __get_cpus_per_group ();
440 int bitindex = groupnum * groupsize;
442 int setsize = NBBY * sizeof_set; // bit size of whole cpu set
443 if (bitindex + groupsize > setsize)
444 return (__cpu_mask) 0;
446 int wordsize = NBBY * sizeof (cpu_set_t);
447 int wordindex = bitindex / wordsize;
449 __cpu_mask result = set->__bits[wordindex];
450 int offset = bitindex % wordsize;
451 if (offset)
453 result >>= offset;
454 offset = wordsize - offset;
456 else
457 offset = wordsize;
459 if (offset < groupsize)
460 result |= (set->__bits[wordindex + 1] << offset);
461 if (groupsize < wordsize)
462 result &= groupmask (groupsize);
464 return result;
467 /* set the given affinity mask for indicated group within the given cpu set */
468 static __cpu_mask
469 setgroup (size_t sizeof_set, cpu_set_t *set, int groupnum, __cpu_mask aff)
471 int groupsize = __get_cpus_per_group ();
472 int bitindex = groupnum * groupsize;
474 int setsize = NBBY * sizeof_set; // bit size of whole cpu set
475 if (bitindex + groupsize > setsize)
476 return (__cpu_mask) 0;
478 int wordsize = NBBY * sizeof (cpu_set_t);
479 int wordindex = bitindex / wordsize;
480 int offset = bitindex % wordsize;
481 __cpu_mask mask = groupmask (groupsize);
482 aff &= mask;
484 set->__bits[wordindex] &= ~(mask << offset);
485 set->__bits[wordindex] |= aff << offset;
487 if ((bitindex + groupsize - 1) / wordsize != wordindex)
489 offset = wordsize - offset;
490 set->__bits[wordindex + 1] &= ~(mask >> offset);
491 set->__bits[wordindex + 1] |= aff >> offset;
494 return aff;
497 /* figure out which processor group the set bits indicate; can only be one */
498 static int
499 whichgroup (size_t sizeof_set, const cpu_set_t *set)
501 int res = -1;
502 int maxgroup = min (__get_group_count (),
503 (NBBY * sizeof_set) / __get_cpus_per_group ());
505 for (int i = 0; i < maxgroup; ++i)
506 if (getgroup (sizeof_set, set, i))
508 if (res >= 0)
509 return -1; // error return if more than one group indicated
510 else
511 res = i; // remember first group found
514 return res;
518 sched_get_thread_affinity (HANDLE thread, size_t sizeof_set, cpu_set_t *set)
520 GROUP_AFFINITY ga;
521 int status = 0;
523 if (thread)
525 memset (set, 0, sizeof_set);
526 if (!GetThreadGroupAffinity (thread, &ga))
528 status = geterrno_from_win_error (GetLastError (), EPERM);
529 goto done;
531 setgroup (sizeof_set, set, ga.Group, ga.Mask);
533 else
534 status = ESRCH;
536 done:
537 return status;
541 __sched_getaffinity_sys (pid_t pid, size_t sizeof_set, cpu_set_t *set)
543 /* Emulate Linux raw sched_getaffinity syscall for benefit of taskset(1) */
544 HANDLE process = 0;
545 int status = 0;
547 pinfo p (pid ? pid : getpid ());
548 if (p)
550 process = pid && pid != myself->pid ?
551 OpenProcess (PROCESS_QUERY_LIMITED_INFORMATION, FALSE,
552 p->dwProcessId) : GetCurrentProcess ();
553 KAFFINITY procmask;
554 KAFFINITY sysmask;
555 USHORT groupcount = __CPU_GROUPMAX;
556 USHORT grouparray[__CPU_GROUPMAX];
558 if (!GetProcessAffinityMask (process, &procmask, &sysmask))
560 status = geterrno_from_win_error (GetLastError (), EPERM);
561 goto done;
563 memset (set, 0, sizeof_set);
564 if (!GetProcessGroupAffinity (process, &groupcount, grouparray))
566 status = geterrno_from_win_error (GetLastError (), EPERM);
567 goto done;
570 KAFFINITY miscmask = groupmask (__get_cpus_per_group ());
571 for (int i = 0; i < groupcount; i++)
572 setgroup (sizeof_set, set, grouparray[i], miscmask);
574 else
575 status = ESRCH;
577 done:
578 if (process && process != GetCurrentProcess ())
579 CloseHandle (process);
581 if (status)
583 set_errno (status);
584 return -1;
587 /* On successful return, we would ordinarily return 0, but instead we
588 emulate the behavior of the raw sched_getaffinity syscall on Linux. */
589 return min (sizeof_set, sizeof (cpu_set_t));
593 sched_getaffinity (pid_t pid, size_t sizeof_set, cpu_set_t *set)
595 /* Emulate the Linux glibc interface of sched_getaffinity() by calling
596 the raw syscall emulation and mapping positive results to 0. */
597 int status = __sched_getaffinity_sys (pid, sizeof_set, set);
598 return status > 0 ? 0 : status;
602 sched_set_thread_affinity (HANDLE thread, size_t sizeof_set, const cpu_set_t *set)
604 GROUP_AFFINITY ga;
605 int group = whichgroup (sizeof_set, set);
606 int status = 0;
608 if (thread)
610 if (group < 0)
612 status = EINVAL;
613 goto done;
615 memset (&ga, 0, sizeof (ga));
616 ga.Mask = getgroup (sizeof_set, set, group);
617 ga.Group = group;
618 if (!SetThreadGroupAffinity (thread, &ga, NULL))
620 status = geterrno_from_win_error (GetLastError (), EPERM);
621 goto done;
624 else
625 status = ESRCH;
627 done:
628 return status;
632 sched_setaffinity (pid_t pid, size_t sizeof_set, const cpu_set_t *set)
634 USHORT groupcount = __CPU_GROUPMAX;
635 USHORT grouparray[__CPU_GROUPMAX];
636 int group = whichgroup (sizeof_set, set);
637 HANDLE process = 0;
638 int status = 0;
640 pinfo p (pid ? pid : getpid ());
641 if (p)
643 process = pid && pid != myself->pid ?
644 OpenProcess (PROCESS_SET_INFORMATION, FALSE,
645 p->dwProcessId) : GetCurrentProcess ();
646 if (!GetProcessGroupAffinity (process, &groupcount, grouparray))
648 status = geterrno_from_win_error (GetLastError (), EPERM);
649 goto done;
651 if (group < 0)
653 status = EINVAL;
654 goto done;
656 if (groupcount == 1 && grouparray[0] == group)
658 if (!SetProcessAffinityMask (process, getgroup (sizeof_set, set, group)))
659 status = geterrno_from_win_error (GetLastError (), EPERM);
660 goto done;
663 /* If we get here, the user is trying to add the process to another
664 group or move it from current group to another group. These ops
665 are not allowed by Windows. One has to move one or more of the
666 process' threads to the new group(s) one by one. Here, we bail.
668 status = EINVAL;
669 goto done;
671 else
672 status = ESRCH;
674 done:
675 if (process && process != GetCurrentProcess ())
676 CloseHandle (process);
678 if (status)
680 set_errno (status);
681 return -1;
684 return 0;
687 cpu_set_t *
688 __cpuset_alloc (int num)
690 return (cpu_set_t *) malloc (CPU_ALLOC_SIZE(num));
693 void
694 __cpuset_free (cpu_set_t *set)
696 free (set);
699 } /* extern C */