Update install.texi, and regenerate INSTALL.
[glibc.git] / nptl / tpp.c
blob7f58a75731d15ff3c429f03ccde463251837ca14
1 /* Thread Priority Protect helpers.
2 Copyright (C) 2006-2021 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Jakub Jelinek <jakub@redhat.com>, 2006.
6 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
11 The GNU C Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with the GNU C Library; if not, see
18 <https://www.gnu.org/licenses/>. */
20 #include <assert.h>
21 #include <atomic.h>
22 #include <errno.h>
23 #include <pthreadP.h>
24 #include <sched.h>
25 #include <stdlib.h>
26 #include <atomic.h>
28 int __sched_fifo_min_prio = -1;
29 libc_hidden_data_def (__sched_fifo_min_prio)
30 int __sched_fifo_max_prio = -1;
31 libc_hidden_data_def (__sched_fifo_max_prio)
33 /* We only want to initialize __sched_fifo_min_prio and __sched_fifo_max_prio
34 once. The standard solution would be similar to pthread_once, but then
35 readers would need to use an acquire fence. In this specific case,
36 initialization is comprised of just idempotent writes to two variables
37 that have an initial value of -1. Therefore, we can treat each variable as
38 a separate, at-least-once initialized value. This enables using just
39 relaxed MO loads and stores, but requires that consumers check for
40 initialization of each value that is to be used; see
41 __pthread_tpp_change_priority for an example.
43 void
44 __init_sched_fifo_prio (void)
46 atomic_store_relaxed (&__sched_fifo_max_prio,
47 __sched_get_priority_max (SCHED_FIFO));
48 atomic_store_relaxed (&__sched_fifo_min_prio,
49 __sched_get_priority_min (SCHED_FIFO));
51 libc_hidden_def (__init_sched_fifo_prio)
53 int
54 __pthread_tpp_change_priority (int previous_prio, int new_prio)
56 struct pthread *self = THREAD_SELF;
57 struct priority_protection_data *tpp = THREAD_GETMEM (self, tpp);
58 int fifo_min_prio = atomic_load_relaxed (&__sched_fifo_min_prio);
59 int fifo_max_prio = atomic_load_relaxed (&__sched_fifo_max_prio);
61 if (tpp == NULL)
63 /* See __init_sched_fifo_prio. We need both the min and max prio,
64 so need to check both, and run initialization if either one is
65 not initialized. The memory model's write-read coherence rule
66 makes this work. */
67 if (fifo_min_prio == -1 || fifo_max_prio == -1)
69 __init_sched_fifo_prio ();
70 fifo_min_prio = atomic_load_relaxed (&__sched_fifo_min_prio);
71 fifo_max_prio = atomic_load_relaxed (&__sched_fifo_max_prio);
74 size_t size = sizeof *tpp;
75 size += (fifo_max_prio - fifo_min_prio + 1)
76 * sizeof (tpp->priomap[0]);
77 tpp = calloc (size, 1);
78 if (tpp == NULL)
79 return ENOMEM;
80 tpp->priomax = fifo_min_prio - 1;
81 THREAD_SETMEM (self, tpp, tpp);
84 assert (new_prio == -1
85 || (new_prio >= fifo_min_prio
86 && new_prio <= fifo_max_prio));
87 assert (previous_prio == -1
88 || (previous_prio >= fifo_min_prio
89 && previous_prio <= fifo_max_prio));
91 int priomax = tpp->priomax;
92 int newpriomax = priomax;
93 if (new_prio != -1)
95 if (tpp->priomap[new_prio - fifo_min_prio] + 1 == 0)
96 return EAGAIN;
97 ++tpp->priomap[new_prio - fifo_min_prio];
98 if (new_prio > priomax)
99 newpriomax = new_prio;
102 if (previous_prio != -1)
104 if (--tpp->priomap[previous_prio - fifo_min_prio] == 0
105 && priomax == previous_prio
106 && previous_prio > new_prio)
108 int i;
109 for (i = previous_prio - 1; i >= fifo_min_prio; --i)
110 if (tpp->priomap[i - fifo_min_prio])
111 break;
112 newpriomax = i;
116 if (priomax == newpriomax)
117 return 0;
119 /* See CREATE THREAD NOTES in nptl/pthread_create.c. */
120 lll_lock (self->lock, LLL_PRIVATE);
122 tpp->priomax = newpriomax;
124 int result = 0;
126 if ((self->flags & ATTR_FLAG_SCHED_SET) == 0)
128 if (__sched_getparam (self->tid, &self->schedparam) != 0)
129 result = errno;
130 else
131 self->flags |= ATTR_FLAG_SCHED_SET;
134 if ((self->flags & ATTR_FLAG_POLICY_SET) == 0)
136 self->schedpolicy = __sched_getscheduler (self->tid);
137 if (self->schedpolicy == -1)
138 result = errno;
139 else
140 self->flags |= ATTR_FLAG_POLICY_SET;
143 if (result == 0)
145 struct sched_param sp = self->schedparam;
146 if (sp.sched_priority < newpriomax || sp.sched_priority < priomax)
148 if (sp.sched_priority < newpriomax)
149 sp.sched_priority = newpriomax;
151 if (__sched_setscheduler (self->tid, self->schedpolicy, &sp) < 0)
152 result = errno;
156 lll_unlock (self->lock, LLL_PRIVATE);
158 return result;
160 libc_hidden_def (__pthread_tpp_change_priority)
163 __pthread_current_priority (void)
165 struct pthread *self = THREAD_SELF;
166 if ((self->flags & (ATTR_FLAG_POLICY_SET | ATTR_FLAG_SCHED_SET))
167 == (ATTR_FLAG_POLICY_SET | ATTR_FLAG_SCHED_SET))
168 return self->schedparam.sched_priority;
170 int result = 0;
172 /* See CREATE THREAD NOTES in nptl/pthread_create.c. */
173 lll_lock (self->lock, LLL_PRIVATE);
175 if ((self->flags & ATTR_FLAG_SCHED_SET) == 0)
177 if (__sched_getparam (self->tid, &self->schedparam) != 0)
178 result = -1;
179 else
180 self->flags |= ATTR_FLAG_SCHED_SET;
183 if ((self->flags & ATTR_FLAG_POLICY_SET) == 0)
185 self->schedpolicy = __sched_getscheduler (self->tid);
186 if (self->schedpolicy == -1)
187 result = -1;
188 else
189 self->flags |= ATTR_FLAG_POLICY_SET;
192 if (result != -1)
193 result = self->schedparam.sched_priority;
195 lll_unlock (self->lock, LLL_PRIVATE);
197 return result;
199 libc_hidden_def (__pthread_current_priority)