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
12 #include "miscfuncs.h"
18 #include <sys/param.h>
21 /* Win32 priority to UNIX priority Mapping. */
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
30 /* max priority for policy */
32 sched_get_priority_max (int policy
)
45 /* min priority for policy */
47 sched_get_priority_min (int policy
)
60 /* Check a scheduler parameter struct for valid settings */
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. */
72 sched_getparam (pid_t pid
, struct sched_param
*param
)
75 if (!param
|| pid
< 0)
81 localpid
= pid
? pid
: getpid ();
93 process
= OpenProcess (PROCESS_QUERY_LIMITED_INFORMATION
, FALSE
,
100 pclass
= GetPriorityClass (process
);
101 CloseHandle (process
);
107 /* calculate the unix priority. */
110 case IDLE_PRIORITY_CLASS
:
111 param
->sched_priority
= 3;
113 case BELOW_NORMAL_PRIORITY_CLASS
:
114 param
->sched_priority
= 9;
116 case NORMAL_PRIORITY_CLASS
:
118 param
->sched_priority
= 15;
120 case ABOVE_NORMAL_PRIORITY_CLASS
:
121 param
->sched_priority
= 21;
123 case HIGH_PRIORITY_CLASS
:
124 param
->sched_priority
= 27;
126 case REALTIME_PRIORITY_CLASS
:
127 param
->sched_priority
= 32;
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
)
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;
161 DWORD vfindex
, slindex
, qindex
, prisep
;
164 forwin
= GetForegroundWindow ();
166 GetWindowThreadProcessId (forwin
, &forprocid
);
170 reg_key
reg (HKEY_LOCAL_MACHINE
, KEY_READ
, L
"SYSTEM", L
"CurrentControlSet",
171 L
"Control", L
"PriorityControl", NULL
);
177 prisep
= reg
.get_dword (L
"Win32PrioritySeparation", 2);
178 pinfo
pi (pid
? pid
: myself
->pid
);
185 if (pi
->dwProcessId
== forprocid
)
188 qindex
= qindex
== 3 ? 2 : qindex
;
192 vfindex
= ((prisep
>> 2) & 3) % 3;
194 vfindex
= wincap
.is_server () || (prisep
& 3) == 0 ? 1 : 0;
197 slindex
= ((prisep
>> 4) & 3) % 3;
199 slindex
= wincap
.is_server () ? 1 : 0;
203 nsec
= quantable
[vfindex
][slindex
][qindex
] / quantapertick
204 * clocktickinterval
* (NSPERSEC
/ MSPERSEC
);
205 interval
->tv_sec
= nsec
/ NSPERSEC
;
206 interval
->tv_nsec
= nsec
% NSPERSEC
;
211 /* set the scheduling parameters */
213 sched_setparam (pid_t pid
, const struct sched_param
*param
)
220 if (!param
|| pid
< 0)
226 if (!valid_sched_parameters (param
))
232 pri
= param
->sched_priority
;
234 /* calculate our desired priority class. We only reserve a small area
235 (31/32) for realtime priority. */
237 pclass
= IDLE_PRIORITY_CLASS
;
239 pclass
= BELOW_NORMAL_PRIORITY_CLASS
;
241 pclass
= NORMAL_PRIORITY_CLASS
;
243 pclass
= ABOVE_NORMAL_PRIORITY_CLASS
;
245 pclass
= HIGH_PRIORITY_CLASS
;
247 pclass
= REALTIME_PRIORITY_CLASS
;
249 localpid
= pid
? pid
: getpid ();
260 process
= OpenProcess (PROCESS_SET_INFORMATION
, FALSE
, p
->dwProcessId
);
266 if (!SetPriorityClass (process
, pclass
))
268 CloseHandle (process
);
272 CloseHandle (process
);
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
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.
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...
299 ...multiplied by 2 to stretch the priorities over the entire range 1 - 32.
303 sched_base_prio_from_win_prio_class (DWORD pclass
)
309 case IDLE_PRIORITY_CLASS
:
312 case BELOW_NORMAL_PRIORITY_CLASS
:
315 case NORMAL_PRIORITY_CLASS
:
319 case ABOVE_NORMAL_PRIORITY_CLASS
:
322 case HIGH_PRIORITY_CLASS
:
323 case REALTIME_PRIORITY_CLASS
: /* See above note 1 */
331 sched_get_thread_priority (HANDLE thread
)
337 tprio
= GetThreadPriority (thread
);
338 pclass
= GetPriorityClass (GetCurrentProcess ());
341 case THREAD_PRIORITY_ERROR_RETURN
:
342 priority
= sched_base_prio_from_win_prio_class (pclass
);
344 case THREAD_PRIORITY_IDLE
:
347 case THREAD_PRIORITY_TIME_CRITICAL
:
351 priority
= tprio
+ sched_base_prio_from_win_prio_class (pclass
);
354 return priority
<< 1; /* See above note 3 */
358 sched_set_thread_priority (HANDLE thread
, int priority
)
363 pclass
= GetPriorityClass (GetCurrentProcess ());
366 if (priority
< 1 || priority
> 32)
369 priority
>>= 1; /* See above note 3 */
372 else if (priority
> 15)
376 tprio
= THREAD_PRIORITY_IDLE
;
377 else if (priority
== 15)
378 tprio
= THREAD_PRIORITY_TIME_CRITICAL
;
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. */
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! */
414 EXPORT_ALIAS (sched_yield
, pthread_yield
)
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 */
427 groupmask (int count
)
429 if (count
>= (int) (NBBY
* sizeof (__cpu_mask
)))
430 return ~(__cpu_mask
) 0;
432 return ((__cpu_mask
) 1 << count
) - 1;
435 /* return the affinity mask of the indicated group from the given cpu set */
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
;
454 offset
= wordsize
- offset
;
459 if (offset
< groupsize
)
460 result
|= (set
->__bits
[wordindex
+ 1] << offset
);
461 if (groupsize
< wordsize
)
462 result
&= groupmask (groupsize
);
467 /* set the given affinity mask for indicated group within the given cpu set */
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
);
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
;
497 /* figure out which processor group the set bits indicate; can only be one */
499 whichgroup (size_t sizeof_set
, const cpu_set_t
*set
)
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
))
509 return -1; // error return if more than one group indicated
511 res
= i
; // remember first group found
518 sched_get_thread_affinity (HANDLE thread
, size_t sizeof_set
, cpu_set_t
*set
)
525 memset (set
, 0, sizeof_set
);
526 if (!GetThreadGroupAffinity (thread
, &ga
))
528 status
= geterrno_from_win_error (GetLastError (), EPERM
);
531 setgroup (sizeof_set
, set
, ga
.Group
, ga
.Mask
);
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) */
547 pinfo
p (pid
? pid
: getpid ());
550 process
= pid
&& pid
!= myself
->pid
?
551 OpenProcess (PROCESS_QUERY_LIMITED_INFORMATION
, FALSE
,
552 p
->dwProcessId
) : GetCurrentProcess ();
555 USHORT groupcount
= __CPU_GROUPMAX
;
556 USHORT grouparray
[__CPU_GROUPMAX
];
558 if (!GetProcessAffinityMask (process
, &procmask
, &sysmask
))
560 status
= geterrno_from_win_error (GetLastError (), EPERM
);
563 memset (set
, 0, sizeof_set
);
564 if (!GetProcessGroupAffinity (process
, &groupcount
, grouparray
))
566 status
= geterrno_from_win_error (GetLastError (), EPERM
);
570 KAFFINITY miscmask
= groupmask (__get_cpus_per_group ());
571 for (int i
= 0; i
< groupcount
; i
++)
572 setgroup (sizeof_set
, set
, grouparray
[i
], miscmask
);
578 if (process
&& process
!= GetCurrentProcess ())
579 CloseHandle (process
);
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
)
605 int group
= whichgroup (sizeof_set
, set
);
615 memset (&ga
, 0, sizeof (ga
));
616 ga
.Mask
= getgroup (sizeof_set
, set
, group
);
618 if (!SetThreadGroupAffinity (thread
, &ga
, NULL
))
620 status
= geterrno_from_win_error (GetLastError (), EPERM
);
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
);
640 pinfo
p (pid
? pid
: getpid ());
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
);
656 if (groupcount
== 1 && grouparray
[0] == group
)
658 if (!SetProcessAffinityMask (process
, getgroup (sizeof_set
, set
, group
)))
659 status
= geterrno_from_win_error (GetLastError (), EPERM
);
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.
675 if (process
&& process
!= GetCurrentProcess ())
676 CloseHandle (process
);
688 __cpuset_alloc (int num
)
690 return (cpu_set_t
*) malloc (CPU_ALLOC_SIZE(num
));
694 __cpuset_free (cpu_set_t
*set
)