415 lines
		
	
	
		
			9.0 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			415 lines
		
	
	
		
			9.0 KiB
		
	
	
	
		
			C++
		
	
	
	
| /* sched.cc: scheduler interface for Cygwin
 | |
| 
 | |
|    Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2010, 2011, 2012,
 | |
|    2013 Red Hat, Inc.
 | |
| 
 | |
|    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"
 | |
| /* for getpid */
 | |
| #include <unistd.h>
 | |
| #include "registry.h"
 | |
| 
 | |
| /* Win32 priority to UNIX priority Mapping.
 | |
| 
 | |
|    For now, I'm just following the spec: any range of priorities is ok.
 | |
|    There are probably many many issues with this...
 | |
| 
 | |
|    FIXME: We don't support pre-Windows 2000 so we should fix the priority
 | |
|           computation.  Here's the description for the current code:
 | |
| 
 | |
|      We don't want process's going realtime. Well, they probably could, but
 | |
|      the issues with avoiding the priority values 17-22 and 27-30 (not
 | |
|      supported before win2k) make that inefficient.
 | |
| 
 | |
|      However to complicate things most unixes use lower is better priorities.
 | |
| 
 | |
|      So we map -14 to 15, and 15 to 1 via (16- ((n+16) >> 1)).  We then map 1
 | |
|      to 15 to various process class and thread priority combinations.  Then we
 | |
|      need to look at the threads process priority.  As win95, 98 and NT 4
 | |
|      don't support opening threads cross-process (unless a thread HANDLE is
 | |
|      passed around) for now, we'll just use the priority class.
 | |
| 
 | |
|      The code and logic are present to calculate the priority for thread, if a
 | |
|      thread handle can be obtained.  Alternatively, if the symbols wouldn't be
 | |
|      resolved until they are used we could support this.
 | |
| 
 | |
|    Lastly, because we can't assume that the pid we're given are Windows pids,
 | |
|    we can't alter non-cygwin started programs.  */
 | |
| 
 | |
| extern "C"
 | |
| {
 | |
| 
 | |
| /* max priority for policy */
 | |
| int
 | |
| sched_get_priority_max (int policy)
 | |
| {
 | |
|   if (policy < 1 || policy > 3)
 | |
|     {
 | |
|       set_errno (EINVAL);
 | |
|       return -1;
 | |
|     }
 | |
|   return -14;
 | |
| }
 | |
| 
 | |
| /* min priority for policy */
 | |
| int
 | |
| sched_get_priority_min (int policy)
 | |
| {
 | |
|   if (policy < 1 || policy > 3)
 | |
|     {
 | |
|       set_errno (EINVAL);
 | |
|       return -1;
 | |
|     }
 | |
|   return 15;
 | |
| }
 | |
| 
 | |
| /* Check a scheduler parameter struct for valid settings */
 | |
| int
 | |
| valid_sched_parameters (const struct sched_param *param)
 | |
| {
 | |
|   if (param->sched_priority < -14 || param->sched_priority > 15)
 | |
|     {
 | |
|       return 0;
 | |
|     }
 | |
|   return -1;
 | |
| 
 | |
| }
 | |
| 
 | |
| /* get sched params for process
 | |
| 
 | |
|    Note, I'm never returning EPERM,
 | |
|    Always ESRCH. This is by design (If cygwin ever looks at paranoid security
 | |
|    Walking the pid values is a known hole in some os's)
 | |
| */
 | |
| int
 | |
| sched_getparam (pid_t pid, struct sched_param *param)
 | |
| {
 | |
|   pid_t localpid;
 | |
|   int winpri;
 | |
|   if (!param || pid < 0)
 | |
|     {
 | |
|       set_errno (EINVAL);
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   localpid = pid ? pid : getpid ();
 | |
| 
 | |
|   DWORD Class;
 | |
|   int ThreadPriority;
 | |
|   HANDLE process;
 | |
|   pinfo p (localpid);
 | |
| 
 | |
|   /* get the class */
 | |
| 
 | |
|   if (!p)
 | |
|     {
 | |
|       set_errno (ESRCH);
 | |
|       return -1;
 | |
|     }
 | |
|   process = OpenProcess (PROCESS_QUERY_INFORMATION, FALSE, p->dwProcessId);
 | |
|   if (!process)
 | |
|     {
 | |
|       set_errno (ESRCH);
 | |
|       return -1;
 | |
|     }
 | |
|   Class = GetPriorityClass (process);
 | |
|   CloseHandle (process);
 | |
|   if (!Class)
 | |
|     {
 | |
|       set_errno (ESRCH);
 | |
|       return -1;
 | |
|     }
 | |
|   ThreadPriority = THREAD_PRIORITY_NORMAL;
 | |
| 
 | |
|   /* calculate the unix priority. */
 | |
| 
 | |
|   switch (Class)
 | |
|     {
 | |
|     case IDLE_PRIORITY_CLASS:
 | |
|       switch (ThreadPriority)
 | |
| 	{
 | |
| 	case THREAD_PRIORITY_IDLE:
 | |
| 	  winpri = 1;
 | |
| 	  break;
 | |
| 	case THREAD_PRIORITY_LOWEST:
 | |
| 	  winpri = 2;
 | |
| 	  break;
 | |
| 	case THREAD_PRIORITY_BELOW_NORMAL:
 | |
| 	  winpri = 3;
 | |
| 	  break;
 | |
| 	case THREAD_PRIORITY_NORMAL:
 | |
| 	  winpri = 4;
 | |
| 	  break;
 | |
| 	case THREAD_PRIORITY_ABOVE_NORMAL:
 | |
| 	  winpri = 5;
 | |
| 	  break;
 | |
| 	case THREAD_PRIORITY_HIGHEST:
 | |
| 	default:
 | |
| 	  winpri = 6;
 | |
| 	  break;
 | |
| 	}
 | |
|       break;
 | |
|     case HIGH_PRIORITY_CLASS:
 | |
|       switch (ThreadPriority)
 | |
| 	{
 | |
| 	case THREAD_PRIORITY_IDLE:
 | |
| 	  winpri = 1;
 | |
| 	  break;
 | |
| 	case THREAD_PRIORITY_LOWEST:
 | |
| 	  winpri = 11;
 | |
| 	  break;
 | |
| 	case THREAD_PRIORITY_BELOW_NORMAL:
 | |
| 	  winpri = 12;
 | |
| 	  break;
 | |
| 	case THREAD_PRIORITY_NORMAL:
 | |
| 	  winpri = 13;
 | |
| 	  break;
 | |
| 	case THREAD_PRIORITY_ABOVE_NORMAL:
 | |
| 	  winpri = 14;
 | |
| 	  break;
 | |
| 	case THREAD_PRIORITY_HIGHEST:
 | |
| 	default:
 | |
| 	  winpri = 15;
 | |
| 	  break;
 | |
| 	}
 | |
|       break;
 | |
|     case NORMAL_PRIORITY_CLASS:
 | |
|     default:
 | |
|       switch (ThreadPriority)
 | |
| 	{
 | |
| 	case THREAD_PRIORITY_IDLE:
 | |
| 	  winpri = 1;
 | |
| 	  break;
 | |
| 	case THREAD_PRIORITY_LOWEST:
 | |
| 	  winpri = 7;
 | |
| 	  break;
 | |
| 	case THREAD_PRIORITY_BELOW_NORMAL:
 | |
| 	  winpri = 8;
 | |
| 	  break;
 | |
| 	case THREAD_PRIORITY_NORMAL:
 | |
| 	  winpri = 9;
 | |
| 	  break;
 | |
| 	case THREAD_PRIORITY_ABOVE_NORMAL:
 | |
| 	  winpri = 10;
 | |
| 	  break;
 | |
| 	case THREAD_PRIORITY_HIGHEST:
 | |
| 	default:
 | |
| 	  winpri = 11;
 | |
| 	  break;
 | |
| 	}
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|   /* reverse out winpri = (16- ((unixpri+16) >> 1)) */
 | |
|   /*
 | |
|      winpri-16 = -  (unixpri +16 ) >> 1
 | |
| 
 | |
|      -(winpri-16) = unixpri +16 >> 1
 | |
|      (-(winpri-16)) << 1 = unixpri+16
 | |
|      ((-(winpri - 16)) << 1) - 16 = unixpri
 | |
|    */
 | |
| 
 | |
|   param->sched_priority = ((-(winpri - 16)) << 1) - 16;
 | |
| 
 | |
|   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 * 1000000;
 | |
|   interval->tv_sec = nsec / 1000000000;
 | |
|   interval->tv_nsec = nsec % 1000000000;
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| /* set the scheduling parameters */
 | |
| int
 | |
| sched_setparam (pid_t pid, const struct sched_param *param)
 | |
| {
 | |
|   pid_t localpid;
 | |
|   int winpri;
 | |
|   DWORD Class;
 | |
|   HANDLE process;
 | |
| 
 | |
|   if (!param || pid < 0)
 | |
|     {
 | |
|       set_errno (EINVAL);
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   if (!valid_sched_parameters (param))
 | |
|     {
 | |
|       set_errno (EINVAL);
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   /*  winpri = (16- ((unixpri+16) >> 1)) */
 | |
|   winpri = 16 - ((param->sched_priority + 16) >> 1);
 | |
| 
 | |
|   /* calculate our desired priority class and thread priority */
 | |
| 
 | |
|   if (winpri < 7)
 | |
|     Class = IDLE_PRIORITY_CLASS;
 | |
|   else if (winpri > 10)
 | |
|     Class = HIGH_PRIORITY_CLASS;
 | |
|   else
 | |
|     Class = NORMAL_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, (DWORD) p->dwProcessId);
 | |
|   if (!process)
 | |
|     {
 | |
|       set_errno (2);		//ESRCH);
 | |
|       return -1;
 | |
|     }
 | |
|   if (!SetPriorityClass (process, Class))
 | |
|     {
 | |
|       CloseHandle (process);
 | |
|       set_errno (EPERM);
 | |
|       return -1;
 | |
|     }
 | |
|   CloseHandle (process);
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| /* we map -14 to 15, and 15 to 1 via (16- ((n+16) >> 1)). This lines up with
 | |
|    the allowed values we return elsewhere in the sched* functions. We then
 | |
|    map in groups of three to allowed thread priority's. The reason for dropping
 | |
|    accuracy while still returning a wide range of values is to allow more
 | |
|    flexible code in the future. */
 | |
| int
 | |
| sched_set_thread_priority (HANDLE thread, int priority)
 | |
| {
 | |
|   int real_pri;
 | |
|   real_pri = 16 - ((priority + 16) >> 1);
 | |
|   if (real_pri <1 || real_pri > 15)
 | |
|     return EINVAL;
 | |
| 
 | |
|   if (real_pri < 4)
 | |
|     real_pri = THREAD_PRIORITY_LOWEST;
 | |
|   else if (real_pri < 7)
 | |
|     real_pri = THREAD_PRIORITY_BELOW_NORMAL;
 | |
|   else if (real_pri < 10)
 | |
|     real_pri = THREAD_PRIORITY_NORMAL;
 | |
|   else if (real_pri < 13)
 | |
|     real_pri = THREAD_PRIORITY_ABOVE_NORMAL;
 | |
|   else
 | |
|     real_pri = THREAD_PRIORITY_HIGHEST;
 | |
| 
 | |
|   if (!SetThreadPriority (thread, real_pri))
 | |
|     /* 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;
 | |
| }
 | |
| }
 |