428 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			428 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			C++
		
	
	
	
| /* sched.cc: scheduler interface for Cygwin
 | |
| 
 | |
|    Written by Robert Collins <rbtcollins@hotmail.com>
 | |
| 
 | |
|    This file is part of Cygwin.
 | |
| 
 | |
|    This software is a copyrighted work licensed under the terms of the
 | |
|    Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
 | |
|    details. */
 | |
| 
 | |
| #include "winsup.h"
 | |
| #include "miscfuncs.h"
 | |
| #include "cygerrno.h"
 | |
| #include "pinfo.h"
 | |
| #include "hires.h"
 | |
| /* for getpid */
 | |
| #include <unistd.h>
 | |
| #include <sys/param.h>
 | |
| #include "registry.h"
 | |
| 
 | |
| /* Win32 priority to UNIX priority Mapping. */
 | |
| 
 | |
| extern "C"
 | |
| {
 | |
| 
 | |
| /* We support prio values from 1 to 32.  This is marginally in line with Linux
 | |
|    (1 - 99) and matches the POSIX requirement to support at least 32 priority
 | |
|    values. */
 | |
| 
 | |
| /* max priority for policy */
 | |
| int
 | |
| sched_get_priority_max (int policy)
 | |
| {
 | |
|   switch (policy)
 | |
|     {
 | |
|     case SCHED_FIFO:
 | |
|     case SCHED_RR:
 | |
|     case SCHED_OTHER:
 | |
|       return 32;
 | |
|     }
 | |
|   set_errno (EINVAL);
 | |
|   return -1;
 | |
| }
 | |
| 
 | |
| /* min priority for policy */
 | |
| int
 | |
| sched_get_priority_min (int policy)
 | |
| {
 | |
|   switch (policy)
 | |
|     {
 | |
|     case SCHED_FIFO:
 | |
|     case SCHED_RR:
 | |
|     case SCHED_OTHER:
 | |
|       return 1;
 | |
|     }
 | |
|   set_errno (EINVAL);
 | |
|   return -1;
 | |
| }
 | |
| 
 | |
| /* Check a scheduler parameter struct for valid settings */
 | |
| bool
 | |
| valid_sched_parameters (const struct sched_param *param)
 | |
| {
 | |
|   return param->sched_priority >= 1 && param->sched_priority <= 32;
 | |
| }
 | |
| 
 | |
| /* get sched params for process
 | |
| 
 | |
|    Note, we're never returning EPERM, always ESRCH. This is by design.
 | |
|    Walking the pid values is a known hole in some OSes. */
 | |
| int
 | |
| sched_getparam (pid_t pid, struct sched_param *param)
 | |
| {
 | |
|   pid_t localpid;
 | |
|   if (!param || pid < 0)
 | |
|     {
 | |
|       set_errno (EINVAL);
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   localpid = pid ? pid : getpid ();
 | |
| 
 | |
|   DWORD pclass;
 | |
|   HANDLE process;
 | |
|   pinfo p (localpid);
 | |
| 
 | |
|   /* get the class */
 | |
|   if (!p)
 | |
|     {
 | |
|       set_errno (ESRCH);
 | |
|       return -1;
 | |
|     }
 | |
|   process = OpenProcess (PROCESS_QUERY_LIMITED_INFORMATION, FALSE,
 | |
| 			 p->dwProcessId);
 | |
|   if (!process)
 | |
|     {
 | |
|       set_errno (ESRCH);
 | |
|       return -1;
 | |
|     }
 | |
|   pclass = GetPriorityClass (process);
 | |
|   CloseHandle (process);
 | |
|   if (!pclass)
 | |
|     {
 | |
|       set_errno (ESRCH);
 | |
|       return -1;
 | |
|     }
 | |
|   /* calculate the unix priority. */
 | |
|   switch (pclass)
 | |
|     {
 | |
|     case IDLE_PRIORITY_CLASS:
 | |
|       param->sched_priority = 3;
 | |
|       break;
 | |
|     case BELOW_NORMAL_PRIORITY_CLASS:
 | |
|       param->sched_priority = 9;
 | |
|       break;
 | |
|     case NORMAL_PRIORITY_CLASS:
 | |
|     default:
 | |
|       param->sched_priority = 15;
 | |
|       break;
 | |
|     case ABOVE_NORMAL_PRIORITY_CLASS:
 | |
|       param->sched_priority = 21;
 | |
|       break;
 | |
|     case HIGH_PRIORITY_CLASS:
 | |
|       param->sched_priority = 27;
 | |
|       break;
 | |
|     case REALTIME_PRIORITY_CLASS:
 | |
|       param->sched_priority = 32;
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| /* get the scheduler for pid
 | |
| 
 | |
|    All process's on WIN32 run with SCHED_FIFO.  So we just give an answer.
 | |
|    (WIN32 uses a multi queue FIFO).
 | |
| */
 | |
| int
 | |
| sched_getscheduler (pid_t pid)
 | |
| {
 | |
|   if (pid < 0)
 | |
|     return ESRCH;
 | |
|   else
 | |
|     return SCHED_FIFO;
 | |
| }
 | |
| 
 | |
| /* get the time quantum for pid */
 | |
| int
 | |
| sched_rr_get_interval (pid_t pid, struct timespec *interval)
 | |
| {
 | |
|   static const char quantable[2][2][3] =
 | |
|     {{{12, 24, 36}, { 6, 12, 18}},
 | |
|      {{36, 36, 36}, {18, 18, 18}}};
 | |
|   /* FIXME: Clocktickinterval can be 15 ms for multi-processor system. */
 | |
|   static const int clocktickinterval = 10;
 | |
|   static const int quantapertick = 3;
 | |
| 
 | |
|   HWND forwin;
 | |
|   DWORD forprocid;
 | |
|   DWORD vfindex, slindex, qindex, prisep;
 | |
|   long nsec;
 | |
| 
 | |
|   forwin = GetForegroundWindow ();
 | |
|   if (!forwin)
 | |
|     GetWindowThreadProcessId (forwin, &forprocid);
 | |
|   else
 | |
|     forprocid = 0;
 | |
| 
 | |
|   reg_key reg (HKEY_LOCAL_MACHINE, KEY_READ, L"SYSTEM", L"CurrentControlSet",
 | |
| 	       L"Control", L"PriorityControl", NULL);
 | |
|   if (reg.error ())
 | |
|     {
 | |
|       set_errno (ESRCH);
 | |
|       return -1;
 | |
|     }
 | |
|   prisep = reg.get_dword (L"Win32PrioritySeparation", 2);
 | |
|   pinfo pi (pid ? pid : myself->pid);
 | |
|   if (!pi)
 | |
|     {
 | |
|       set_errno (ESRCH);
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   if (pi->dwProcessId == forprocid)
 | |
|     {
 | |
|       qindex = prisep & 3;
 | |
|       qindex = qindex == 3 ? 2 : qindex;
 | |
|     }
 | |
|   else
 | |
|     qindex = 0;
 | |
|   vfindex = ((prisep >> 2) & 3) % 3;
 | |
|   if (vfindex == 0)
 | |
|     vfindex = wincap.is_server () || (prisep & 3) == 0 ? 1 : 0;
 | |
|   else
 | |
|     vfindex -= 1;
 | |
|   slindex = ((prisep >> 4) & 3) % 3;
 | |
|   if (slindex == 0)
 | |
|     slindex = wincap.is_server () ? 1 : 0;
 | |
|   else
 | |
|     slindex -= 1;
 | |
| 
 | |
|   nsec = quantable[vfindex][slindex][qindex] / quantapertick
 | |
| 	 * clocktickinterval * (NSPERSEC / MSPERSEC);
 | |
|   interval->tv_sec = nsec / NSPERSEC;
 | |
|   interval->tv_nsec = nsec % NSPERSEC;
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| /* set the scheduling parameters */
 | |
| int
 | |
| sched_setparam (pid_t pid, const struct sched_param *param)
 | |
| {
 | |
|   pid_t localpid;
 | |
|   int pri;
 | |
|   DWORD pclass;
 | |
|   HANDLE process;
 | |
| 
 | |
|   if (!param || pid < 0)
 | |
|     {
 | |
|       set_errno (EINVAL);
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   if (!valid_sched_parameters (param))
 | |
|     {
 | |
|       set_errno (EINVAL);
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   pri = param->sched_priority;
 | |
| 
 | |
|   /* calculate our desired priority class.  We only reserve a small area
 | |
|      (31/32) for realtime priority. */
 | |
|   if (pri <= 6)
 | |
|     pclass = IDLE_PRIORITY_CLASS;
 | |
|   else if (pri <= 12)
 | |
|     pclass = BELOW_NORMAL_PRIORITY_CLASS;
 | |
|   else if (pri <= 18)
 | |
|     pclass = NORMAL_PRIORITY_CLASS;
 | |
|   else if (pri <= 24)
 | |
|     pclass = ABOVE_NORMAL_PRIORITY_CLASS;
 | |
|   else if (pri <= 30)
 | |
|     pclass = HIGH_PRIORITY_CLASS;
 | |
|   else
 | |
|     pclass = REALTIME_PRIORITY_CLASS;
 | |
| 
 | |
|   localpid = pid ? pid : getpid ();
 | |
| 
 | |
|   pinfo p (localpid);
 | |
| 
 | |
|   /* set the class */
 | |
| 
 | |
|   if (!p)
 | |
|     {
 | |
|       set_errno (ESRCH);
 | |
|       return -1;
 | |
|     }
 | |
|   process = OpenProcess (PROCESS_SET_INFORMATION, FALSE, p->dwProcessId);
 | |
|   if (!process)
 | |
|     {
 | |
|       set_errno (ESRCH);
 | |
|       return -1;
 | |
|     }
 | |
|   if (!SetPriorityClass (process, pclass))
 | |
|     {
 | |
|       CloseHandle (process);
 | |
|       set_errno (EPERM);
 | |
|       return -1;
 | |
|     }
 | |
|   CloseHandle (process);
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| /* POSIX thread priorities loosely compare to Windows thread base priorities.
 | |
| 
 | |
|    Base priority is a function of process priority class and thread priority.
 | |
|    https://msdn.microsoft.com/en-us/library/windows/desktop/ms685100%28v=vs.85%29.aspx
 | |
| 
 | |
|    Note 1:
 | |
| 
 | |
|      We deliberately handle the REALTIME prority class the same as the HIGH
 | |
|      priority class.  Realtime has it's own range from 16 to 31 so half the
 | |
|      arena is reserved for REALTIME.  The problem is that this isn't visible
 | |
|      nor expected in the POSIX scenario.  Therefore we hide this here and
 | |
|      fold REALTIME into HIGH.
 | |
| 
 | |
|    Note 2:
 | |
| 
 | |
|      sched_get_thread_priority is only called internally and only for threads
 | |
|      of the current process, with no good reason for the caller to fail.
 | |
|      Therefore it never returns an error but a valid priority (base value
 | |
|      equivalent to process priority class + THREAD_PRIORITY_NORMAL...
 | |
| 
 | |
|    Note 3:
 | |
| 
 | |
|      ...multiplied by 2 to stretch the priorities over the entire range 1 - 32.
 | |
| */
 | |
| 
 | |
| static int
 | |
| sched_base_prio_from_win_prio_class (DWORD pclass)
 | |
| {
 | |
|   int base;
 | |
| 
 | |
|   switch (pclass)
 | |
|     {
 | |
|     case IDLE_PRIORITY_CLASS:
 | |
|       base = 4;
 | |
|       break;
 | |
|     case BELOW_NORMAL_PRIORITY_CLASS:
 | |
|       base = 6;
 | |
|       break;
 | |
|     case NORMAL_PRIORITY_CLASS:
 | |
|     default:
 | |
|       base = 8;
 | |
|       break;
 | |
|     case ABOVE_NORMAL_PRIORITY_CLASS:
 | |
|       base = 10;
 | |
|       break;
 | |
|     case HIGH_PRIORITY_CLASS:
 | |
|     case REALTIME_PRIORITY_CLASS: /* See above note 1 */
 | |
|       base = 13;
 | |
|       break;
 | |
|     }
 | |
|   return base;
 | |
| }
 | |
| 
 | |
| int
 | |
| sched_get_thread_priority (HANDLE thread)
 | |
| {
 | |
|   int tprio;
 | |
|   DWORD pclass;
 | |
|   int priority;
 | |
| 
 | |
|   tprio = GetThreadPriority (thread);
 | |
|   pclass = GetPriorityClass (GetCurrentProcess ());
 | |
|   switch (tprio)
 | |
|     {
 | |
|     case THREAD_PRIORITY_ERROR_RETURN:
 | |
|       priority = sched_base_prio_from_win_prio_class (pclass);
 | |
|       break;
 | |
|     case THREAD_PRIORITY_IDLE:
 | |
|       priority = 1;
 | |
|       break;
 | |
|     case THREAD_PRIORITY_TIME_CRITICAL:
 | |
|       priority = 15;
 | |
|       break;
 | |
|     default:
 | |
|       priority = tprio + sched_base_prio_from_win_prio_class (pclass);
 | |
|       break;
 | |
|     }
 | |
|   return priority << 1; /* See above note 3 */
 | |
| }
 | |
| 
 | |
| int
 | |
| sched_set_thread_priority (HANDLE thread, int priority)
 | |
| {
 | |
|   DWORD pclass;
 | |
|   int tprio;
 | |
| 
 | |
|   pclass = GetPriorityClass (GetCurrentProcess ());
 | |
|   if (!pclass)
 | |
|     return EPERM;
 | |
|   if (priority < 1 || priority > 32)
 | |
|     return EINVAL;
 | |
| 
 | |
|   priority >>= 1; /* See above note 3 */
 | |
|   if (priority < 1)
 | |
|     priority = 1;
 | |
|   else if (priority > 15)
 | |
|     priority = 15;
 | |
| 
 | |
|   if (priority == 1)
 | |
|     tprio = THREAD_PRIORITY_IDLE;
 | |
|   else if (priority == 15)
 | |
|     tprio = THREAD_PRIORITY_TIME_CRITICAL;
 | |
|   else
 | |
|     {
 | |
|       tprio = priority - sched_base_prio_from_win_prio_class (pclass);
 | |
|       /* Intermediate values only allowed in REALTIME_PRIORITY_CLASS. */
 | |
|       if (pclass != REALTIME_PRIORITY_CLASS)
 | |
| 	{
 | |
| 	  if (tprio < THREAD_PRIORITY_LOWEST)
 | |
| 	    tprio = THREAD_PRIORITY_LOWEST;
 | |
| 	  else if (tprio > THREAD_PRIORITY_HIGHEST)
 | |
| 	    tprio = THREAD_PRIORITY_HIGHEST;
 | |
| 	}
 | |
|     }
 | |
|   if (!SetThreadPriority (thread, tprio))
 | |
|     /* invalid handle, no access are the only expected errors. */
 | |
|     return EPERM;
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| /* set the scheduler */
 | |
| int
 | |
| sched_setscheduler (pid_t pid, int policy,
 | |
| 		    const struct sched_param *param)
 | |
| {
 | |
|   /* on win32, you can't change the scheduler. Doh! */
 | |
|   set_errno (ENOSYS);
 | |
|   return -1;
 | |
| }
 | |
| 
 | |
| /* yield the cpu */
 | |
| int
 | |
| sched_yield ()
 | |
| {
 | |
|   SwitchToThread ();
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| int
 | |
| sched_getcpu ()
 | |
| {
 | |
|   if (!wincap.has_processor_groups ())
 | |
|     return (int) GetCurrentProcessorNumber ();
 | |
| 
 | |
|   PROCESSOR_NUMBER pnum;
 | |
| 
 | |
|   GetCurrentProcessorNumberEx (&pnum);
 | |
|   return pnum.Group * __get_cpus_per_group () + pnum.Number;
 | |
| }
 | |
| 
 | |
| } /* extern C */
 |