Cygwin: (mostly) drop NT4 and Samba < 3.0 support
[newlib-cygwin.git] / winsup / cygwin / sched.cc
blob71a1e868f9b428a11517c4db6a449ac0803b4866
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;
416 sched_getcpu ()
418 PROCESSOR_NUMBER pnum;
420 GetCurrentProcessorNumberEx (&pnum);
421 return pnum.Group * __get_cpus_per_group () + pnum.Number;
424 /* construct an affinity mask with just the 'count' lower-order bits set */
425 static __cpu_mask
426 groupmask (int count)
428 if (count >= (int) (NBBY * sizeof (__cpu_mask)))
429 return ~(__cpu_mask) 0;
430 else
431 return ((__cpu_mask) 1 << count) - 1;
434 /* return the affinity mask of the indicated group from the given cpu set */
435 static __cpu_mask
436 getgroup (size_t sizeof_set, const cpu_set_t *set, int groupnum)
438 int groupsize = __get_cpus_per_group ();
439 int bitindex = groupnum * groupsize;
441 int setsize = NBBY * sizeof_set; // bit size of whole cpu set
442 if (bitindex + groupsize > setsize)
443 return (__cpu_mask) 0;
445 int wordsize = NBBY * sizeof (cpu_set_t);
446 int wordindex = bitindex / wordsize;
448 __cpu_mask result = set->__bits[wordindex];
449 int offset = bitindex % wordsize;
450 if (offset)
452 result >>= offset;
453 offset = wordsize - offset;
455 else
456 offset = wordsize;
458 if (offset < groupsize)
459 result |= (set->__bits[wordindex + 1] << offset);
460 if (groupsize < wordsize)
461 result &= groupmask (groupsize);
463 return result;
466 /* set the given affinity mask for indicated group within the given cpu set */
467 static __cpu_mask
468 setgroup (size_t sizeof_set, cpu_set_t *set, int groupnum, __cpu_mask aff)
470 int groupsize = __get_cpus_per_group ();
471 int bitindex = groupnum * groupsize;
473 int setsize = NBBY * sizeof_set; // bit size of whole cpu set
474 if (bitindex + groupsize > setsize)
475 return (__cpu_mask) 0;
477 int wordsize = NBBY * sizeof (cpu_set_t);
478 int wordindex = bitindex / wordsize;
479 int offset = bitindex % wordsize;
480 __cpu_mask mask = groupmask (groupsize);
481 aff &= mask;
483 set->__bits[wordindex] &= ~(mask << offset);
484 set->__bits[wordindex] |= aff << offset;
486 if ((bitindex + groupsize - 1) / wordsize != wordindex)
488 offset = wordsize - offset;
489 set->__bits[wordindex + 1] &= ~(mask >> offset);
490 set->__bits[wordindex + 1] |= aff >> offset;
493 return aff;
496 /* figure out which processor group the set bits indicate; can only be one */
497 static int
498 whichgroup (size_t sizeof_set, const cpu_set_t *set)
500 int res = -1;
501 int maxgroup = min (__get_group_count (),
502 (NBBY * sizeof_set) / __get_cpus_per_group ());
504 for (int i = 0; i < maxgroup; ++i)
505 if (getgroup (sizeof_set, set, i))
507 if (res >= 0)
508 return -1; // error return if more than one group indicated
509 else
510 res = i; // remember first group found
513 return res;
517 sched_get_thread_affinity (HANDLE thread, size_t sizeof_set, cpu_set_t *set)
519 GROUP_AFFINITY ga;
520 int status = 0;
522 if (thread)
524 memset (set, 0, sizeof_set);
525 if (!GetThreadGroupAffinity (thread, &ga))
527 status = geterrno_from_win_error (GetLastError (), EPERM);
528 goto done;
530 setgroup (sizeof_set, set, ga.Group, ga.Mask);
532 else
533 status = ESRCH;
535 done:
536 return status;
540 __sched_getaffinity_sys (pid_t pid, size_t sizeof_set, cpu_set_t *set)
542 /* Emulate Linux raw sched_getaffinity syscall for benefit of taskset(1) */
543 HANDLE process = 0;
544 int status = 0;
546 pinfo p (pid ? pid : getpid ());
547 if (p)
549 process = pid && pid != myself->pid ?
550 OpenProcess (PROCESS_QUERY_LIMITED_INFORMATION, FALSE,
551 p->dwProcessId) : GetCurrentProcess ();
552 KAFFINITY procmask;
553 KAFFINITY sysmask;
554 USHORT groupcount = __CPU_GROUPMAX;
555 USHORT grouparray[__CPU_GROUPMAX];
557 if (!GetProcessAffinityMask (process, &procmask, &sysmask))
559 status = geterrno_from_win_error (GetLastError (), EPERM);
560 goto done;
562 memset (set, 0, sizeof_set);
563 if (!GetProcessGroupAffinity (process, &groupcount, grouparray))
565 status = geterrno_from_win_error (GetLastError (), EPERM);
566 goto done;
569 KAFFINITY miscmask = groupmask (__get_cpus_per_group ());
570 for (int i = 0; i < groupcount; i++)
571 setgroup (sizeof_set, set, grouparray[i], miscmask);
573 else
574 status = ESRCH;
576 done:
577 if (process && process != GetCurrentProcess ())
578 CloseHandle (process);
580 if (status)
582 set_errno (status);
583 return -1;
586 /* On successful return, we would ordinarily return 0, but instead we
587 emulate the behavior of the raw sched_getaffinity syscall on Linux. */
588 return min (sizeof_set, sizeof (cpu_set_t));
592 sched_getaffinity (pid_t pid, size_t sizeof_set, cpu_set_t *set)
594 /* Emulate the Linux glibc interface of sched_getaffinity() by calling
595 the raw syscall emulation and mapping positive results to 0. */
596 int status = __sched_getaffinity_sys (pid, sizeof_set, set);
597 return status > 0 ? 0 : status;
601 sched_set_thread_affinity (HANDLE thread, size_t sizeof_set, const cpu_set_t *set)
603 GROUP_AFFINITY ga;
604 int group = whichgroup (sizeof_set, set);
605 int status = 0;
607 if (thread)
609 if (group < 0)
611 status = EINVAL;
612 goto done;
614 memset (&ga, 0, sizeof (ga));
615 ga.Mask = getgroup (sizeof_set, set, group);
616 ga.Group = group;
617 if (!SetThreadGroupAffinity (thread, &ga, NULL))
619 status = geterrno_from_win_error (GetLastError (), EPERM);
620 goto done;
623 else
624 status = ESRCH;
626 done:
627 return status;
631 sched_setaffinity (pid_t pid, size_t sizeof_set, const cpu_set_t *set)
633 USHORT groupcount = __CPU_GROUPMAX;
634 USHORT grouparray[__CPU_GROUPMAX];
635 int group = whichgroup (sizeof_set, set);
636 HANDLE process = 0;
637 int status = 0;
639 pinfo p (pid ? pid : getpid ());
640 if (p)
642 process = pid && pid != myself->pid ?
643 OpenProcess (PROCESS_SET_INFORMATION, FALSE,
644 p->dwProcessId) : GetCurrentProcess ();
645 if (!GetProcessGroupAffinity (process, &groupcount, grouparray))
647 status = geterrno_from_win_error (GetLastError (), EPERM);
648 goto done;
650 if (group < 0)
652 status = EINVAL;
653 goto done;
655 if (groupcount == 1 && grouparray[0] == group)
657 if (!SetProcessAffinityMask (process, getgroup (sizeof_set, set, group)))
658 status = geterrno_from_win_error (GetLastError (), EPERM);
659 goto done;
662 /* If we get here, the user is trying to add the process to another
663 group or move it from current group to another group. These ops
664 are not allowed by Windows. One has to move one or more of the
665 process' threads to the new group(s) one by one. Here, we bail.
667 status = EINVAL;
668 goto done;
670 else
671 status = ESRCH;
673 done:
674 if (process && process != GetCurrentProcess ())
675 CloseHandle (process);
677 if (status)
679 set_errno (status);
680 return -1;
683 return 0;
686 cpu_set_t *
687 __cpuset_alloc (int num)
689 return (cpu_set_t *) malloc (CPU_ALLOC_SIZE(num));
692 void
693 __cpuset_free (cpu_set_t *set)
695 free (set);
698 EXPORT_ALIAS (sched_yield, pthread_yield)
699 } /* extern C */