Fixed and unified pthread_once.
[glibc.git] / nptl / tpp.c
blobee9a2fe0d096a16c5b93777fea8a219ff08fa381
1 /* Thread Priority Protect helpers.
2 Copyright (C) 2006-2014 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 <http://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>
28 int __sched_fifo_min_prio = -1;
29 int __sched_fifo_max_prio = -1;
31 void
32 __init_sched_fifo_prio (void)
34 __sched_fifo_max_prio = sched_get_priority_max (SCHED_FIFO);
35 atomic_write_barrier ();
36 __sched_fifo_min_prio = sched_get_priority_min (SCHED_FIFO);
39 int
40 __pthread_tpp_change_priority (int previous_prio, int new_prio)
42 struct pthread *self = THREAD_SELF;
43 struct priority_protection_data *tpp = THREAD_GETMEM (self, tpp);
45 if (tpp == NULL)
47 if (__sched_fifo_min_prio == -1)
48 __init_sched_fifo_prio ();
50 size_t size = sizeof *tpp;
51 size += (__sched_fifo_max_prio - __sched_fifo_min_prio + 1)
52 * sizeof (tpp->priomap[0]);
53 tpp = calloc (size, 1);
54 if (tpp == NULL)
55 return ENOMEM;
56 tpp->priomax = __sched_fifo_min_prio - 1;
57 THREAD_SETMEM (self, tpp, tpp);
60 assert (new_prio == -1
61 || (new_prio >= __sched_fifo_min_prio
62 && new_prio <= __sched_fifo_max_prio));
63 assert (previous_prio == -1
64 || (previous_prio >= __sched_fifo_min_prio
65 && previous_prio <= __sched_fifo_max_prio));
67 int priomax = tpp->priomax;
68 int newpriomax = priomax;
69 if (new_prio != -1)
71 if (tpp->priomap[new_prio - __sched_fifo_min_prio] + 1 == 0)
72 return EAGAIN;
73 ++tpp->priomap[new_prio - __sched_fifo_min_prio];
74 if (new_prio > priomax)
75 newpriomax = new_prio;
78 if (previous_prio != -1)
80 if (--tpp->priomap[previous_prio - __sched_fifo_min_prio] == 0
81 && priomax == previous_prio
82 && previous_prio > new_prio)
84 int i;
85 for (i = previous_prio - 1; i >= __sched_fifo_min_prio; --i)
86 if (tpp->priomap[i - __sched_fifo_min_prio])
87 break;
88 newpriomax = i;
92 if (priomax == newpriomax)
93 return 0;
95 lll_lock (self->lock, LLL_PRIVATE);
97 tpp->priomax = newpriomax;
99 int result = 0;
101 if ((self->flags & ATTR_FLAG_SCHED_SET) == 0)
103 if (__sched_getparam (self->tid, &self->schedparam) != 0)
104 result = errno;
105 else
106 self->flags |= ATTR_FLAG_SCHED_SET;
109 if ((self->flags & ATTR_FLAG_POLICY_SET) == 0)
111 self->schedpolicy = __sched_getscheduler (self->tid);
112 if (self->schedpolicy == -1)
113 result = errno;
114 else
115 self->flags |= ATTR_FLAG_POLICY_SET;
118 if (result == 0)
120 struct sched_param sp = self->schedparam;
121 if (sp.sched_priority < newpriomax || sp.sched_priority < priomax)
123 if (sp.sched_priority < newpriomax)
124 sp.sched_priority = newpriomax;
126 if (__sched_setscheduler (self->tid, self->schedpolicy, &sp) < 0)
127 result = errno;
131 lll_unlock (self->lock, LLL_PRIVATE);
133 return result;
137 __pthread_current_priority (void)
139 struct pthread *self = THREAD_SELF;
140 if ((self->flags & (ATTR_FLAG_POLICY_SET | ATTR_FLAG_SCHED_SET))
141 == (ATTR_FLAG_POLICY_SET | ATTR_FLAG_SCHED_SET))
142 return self->schedparam.sched_priority;
144 int result = 0;
146 lll_lock (self->lock, LLL_PRIVATE);
148 if ((self->flags & ATTR_FLAG_SCHED_SET) == 0)
150 if (__sched_getparam (self->tid, &self->schedparam) != 0)
151 result = -1;
152 else
153 self->flags |= ATTR_FLAG_SCHED_SET;
156 if ((self->flags & ATTR_FLAG_POLICY_SET) == 0)
158 self->schedpolicy = __sched_getscheduler (self->tid);
159 if (self->schedpolicy == -1)
160 result = -1;
161 else
162 self->flags |= ATTR_FLAG_POLICY_SET;
165 if (result != -1)
166 result = self->schedparam.sched_priority;
168 lll_unlock (self->lock, LLL_PRIVATE);
170 return result;