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 */
 |