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. 69 static int32 70 prio_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. 87 static int32 88 prio_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 100 int 101 getpriority(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 177 int 178 setpriority(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