1/* 2 * Copyright 2019 Leorize <leorize+oss@disroot.org> 3 * Copyright 2015 Tiancheng "Timothy" Gu, timothygu99@gmail.com 4 * Copyright 2001-2002 Fran��ois Revol (mmu_man) 5 * All rights reserved. Distributed under the terms of the MIT License. 6 */ 7 8 9#ifndef _XOPEN_SOURCE 10#define _XOPEN_SOURCE 600 /* for NZERO */ 11#endif 12 13#include "pthread_private.h" 14 15#include <errno.h> 16#include <sys/resource.h> 17 18#include <OS.h> 19#include <sys/param.h> // for MAX() and MIN() macros 20#include <syscall_utils.h> // RETURN_AND_SET_ERRNO() 21 22// Part of this file is adapted from src/bin/renice.c, by Fran��ois Revol. 23 24// Some notes on different priority notations: 25// 26// Notation system | Real-time | High Prio | Default | Low Prio | 27// -------------------------|----------:|----------:|--------:|---------:| 28// BeOS/Haiku | 120* | 99 | 10 | 1** | 29// UNIX [gs]etpriority()*** | N.A. | -20 | 0 | 19 | 30// UNIX internal | N.A. | 0 | 20 | 39 | 31// 32// * Note that BeOS/Haiku does not have an absolute highest priority value 33// (else than the maximum of int32), and (B_REAL_TIME_PRIORITY - 1) is 34// the highest non-real-time priority and usually used as the priority 35// limit. On UNIX systems there is no such concept as "real-time." 36// ** 0 only for idle_thread 37// *** Also used for UNIX nice(1) and nice(3) 38 39#ifdef CLIP 40#undef CLIP 41#endif 42#define CLIP(n, max, min) MAX(MIN((n), (max)), (min)) 43 44// In these contexts, "min" does not mean "lowest value," but "lowest 45// priority," which is the maximum value in UNIX priority notation. 46// "Zero" means normal priority, which in UNIX function notation is 0. 47// POSIX specification also refers to it as "NZERO." 48#define NMAX 0 49#define NMIN (NZERO * 2 - 1) 50#define CLIP_TO_UNIX(n) CLIP(n, NMIN, NMAX) 51 52#define BZERO B_NORMAL_PRIORITY 53#define BMAX (B_REAL_TIME_DISPLAY_PRIORITY - 1) 54#define BMIN 1 55#define CLIP_TO_BEOS(n) CLIP(n, BMAX, BMIN) 56 57// To accurately convert the notation values, we need to use an exponential 58// function: 59// 60// f(x) = 99e^(-0.116x) 61// 62// where f() represents the BeOS priority value, and x represents the Unix 63// priority value. 64// 65// But that's too complicated. And slow. So we use a simple piecewise linear 66// approach here, by a simple rescaling of the values. 67 68// returns an equivalent UNIX priority for a given BeOS priority. 69static int32 70prio_be_to_unix(int32 prio) 71{ 72 int out; 73 if (prio >= BZERO) 74 out = NZERO 75 - ((prio - BZERO) * NZERO + (BMAX - BZERO) / 2) / (BMAX - BZERO); 76 // `(BMAX - BZERO) / 2` for rounding 77 else 78 out = NZERO 79 + ((BZERO - prio) * (NZERO - 1)) / (BZERO - BMIN) 80 + 1; 81 // `+1` for rounding 82 return CLIP_TO_UNIX(out); 83} 84 85 86// returns an equivalent BeOS priority for a given UNIX priority. 87static int32 88prio_unix_to_be(int32 prio) 89{ 90 int out; 91 // Do not need to care about rounding 92 if (prio >= NZERO) 93 out = BZERO - ((prio - NZERO) * (BZERO - BMIN)) / (NZERO - 1); 94 else 95 out = BZERO + ((NZERO - prio) * (BMAX - BZERO)) / (NZERO); 96 return CLIP_TO_BEOS(out); 97} 98 99 100int 101getpriority(int which, id_t who) 102{ 103 bool found = false; 104 int out = -100; 105 106 if (who < 0) 107 RETURN_AND_SET_ERRNO(EINVAL); 108 switch (which) { 109 case PRIO_PROCESS: 110 { 111 int32 th_cookie = 0; 112 thread_info thread; 113 114 while (get_next_thread_info(who, &th_cookie, &thread) == B_OK) { 115 if (thread.priority > out) { 116 found = true; 117 out = thread.priority; 118 } 119 } 120 break; 121 } 122 case PRIO_PGRP: 123 { 124 int32 team_cookie = 0, th_cookie = 0; 125 team_info team; 126 thread_info thread; 127 128 who = who == 0 ? getpgrp() : who; 129 while (get_next_team_info(&team_cookie, &team) == B_OK) { 130 if (getpgid(team.team) != who) 131 continue; 132 th_cookie = 0; 133 while (get_next_thread_info(team.team, &th_cookie, &thread) 134 == B_OK) { 135 if (thread.priority > out) { 136 found = true; 137 out = thread.priority; 138 } 139 } 140 } 141 break; 142 } 143 case PRIO_USER: 144 { 145 // `who` (id_t) is int32, but uid_t is uint32, so using this 146 // indirection to get rid of compiler warnings 147 // `who` can never be negative because of the `who < 0` check 148 // above. 149 uid_t euid = who == 0 ? geteuid() : (uid_t)who; 150 int32 team_cookie = 0, th_cookie = 0; 151 team_info team; 152 thread_info thread; 153 154 while (get_next_team_info(&team_cookie, &team) == B_OK) { 155 if (team.uid != euid) 156 continue; 157 th_cookie = 0; 158 while (get_next_thread_info(team.team, &th_cookie, &thread) 159 == B_OK) { 160 if (thread.priority > out) { 161 found = true; 162 out = thread.priority; 163 } 164 } 165 } 166 break; 167 } 168 default: 169 RETURN_AND_SET_ERRNO(EINVAL); 170 } 171 if (!found) 172 RETURN_AND_SET_ERRNO(ESRCH); 173 return prio_be_to_unix(out) - NZERO; 174} 175 176 177int 178setpriority(int which, id_t who, int value) 179{ 180 int32 th_cookie = 0; 181 thread_info thread; 182 183 // TODO: implement for other processes 184 if (who != 0 && which != PRIO_PROCESS) 185 RETURN_AND_SET_ERRNO(EINVAL); 186 value = value > NMIN ? NMIN : CLIP_TO_UNIX(value + NZERO); 187 value = prio_unix_to_be(value); 188 189 __pthread_set_default_priority(value); 190 while (get_next_thread_info(B_CURRENT_TEAM, &th_cookie, &thread) == B_OK) 191 set_thread_priority(thread.thread, value); 192 193 return 0; 194} 195