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! */
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 */
426 groupmask (int count
)
428 if (count
>= (int) (NBBY
* sizeof (__cpu_mask
)))
429 return ~(__cpu_mask
) 0;
431 return ((__cpu_mask
) 1 << count
) - 1;
434 /* return the affinity mask of the indicated group from the given cpu set */
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
;
453 offset
= wordsize
- offset
;
458 if (offset
< groupsize
)
459 result
|= (set
->__bits
[wordindex
+ 1] << offset
);
460 if (groupsize
< wordsize
)
461 result
&= groupmask (groupsize
);
466 /* set the given affinity mask for indicated group within the given cpu set */
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
);
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
;
496 /* figure out which processor group the set bits indicate; can only be one */
498 whichgroup (size_t sizeof_set
, const cpu_set_t
*set
)
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
))
508 return -1; // error return if more than one group indicated
510 res
= i
; // remember first group found
517 sched_get_thread_affinity (HANDLE thread
, size_t sizeof_set
, cpu_set_t
*set
)
524 memset (set
, 0, sizeof_set
);
525 if (!GetThreadGroupAffinity (thread
, &ga
))
527 status
= geterrno_from_win_error (GetLastError (), EPERM
);
530 setgroup (sizeof_set
, set
, ga
.Group
, ga
.Mask
);
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) */
546 pinfo
p (pid
? pid
: getpid ());
549 process
= pid
&& pid
!= myself
->pid
?
550 OpenProcess (PROCESS_QUERY_LIMITED_INFORMATION
, FALSE
,
551 p
->dwProcessId
) : GetCurrentProcess ();
554 USHORT groupcount
= __CPU_GROUPMAX
;
555 USHORT grouparray
[__CPU_GROUPMAX
];
557 if (!GetProcessAffinityMask (process
, &procmask
, &sysmask
))
559 status
= geterrno_from_win_error (GetLastError (), EPERM
);
562 memset (set
, 0, sizeof_set
);
563 if (!GetProcessGroupAffinity (process
, &groupcount
, grouparray
))
565 status
= geterrno_from_win_error (GetLastError (), EPERM
);
569 KAFFINITY miscmask
= groupmask (__get_cpus_per_group ());
570 for (int i
= 0; i
< groupcount
; i
++)
571 setgroup (sizeof_set
, set
, grouparray
[i
], miscmask
);
577 if (process
&& process
!= GetCurrentProcess ())
578 CloseHandle (process
);
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
)
604 int group
= whichgroup (sizeof_set
, set
);
614 memset (&ga
, 0, sizeof (ga
));
615 ga
.Mask
= getgroup (sizeof_set
, set
, group
);
617 if (!SetThreadGroupAffinity (thread
, &ga
, NULL
))
619 status
= geterrno_from_win_error (GetLastError (), EPERM
);
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
);
639 pinfo
p (pid
? pid
: getpid ());
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
);
655 if (groupcount
== 1 && grouparray
[0] == group
)
657 if (!SetProcessAffinityMask (process
, getgroup (sizeof_set
, set
, group
)))
658 status
= geterrno_from_win_error (GetLastError (), EPERM
);
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.
674 if (process
&& process
!= GetCurrentProcess ())
675 CloseHandle (process
);
687 __cpuset_alloc (int num
)
689 return (cpu_set_t
*) malloc (CPU_ALLOC_SIZE(num
));
693 __cpuset_free (cpu_set_t
*set
)
698 EXPORT_ALIAS (sched_yield
, pthread_yield
)