OSDN Git Service

Merge remote branch 'origin/master' into nptl
[uclinux-h8/uClibc.git] / libpthread / nptl / sysdeps / pthread / tpp.c
1 /* Thread Priority Protect helpers.
2    Copyright (C) 2006, 2007 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4    Contributed by Jakub Jelinek <jakub@redhat.com>, 2006.
5
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.
10
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.
15
16    You should have received a copy of the GNU Lesser General Public
17    License along with the GNU C Library; if not, write to the Free
18    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19    02111-1307 USA.  */
20
21 #include <assert.h>
22 #include <atomic.h>
23 #include <errno.h>
24 #include <pthreadP.h>
25 #include <sched.h>
26 #include <stdlib.h>
27 #include "uClibc-glue.h"
28
29 int __sched_fifo_min_prio = -1;
30 int __sched_fifo_max_prio = -1;
31
32 void
33 __init_sched_fifo_prio (void)
34 {
35   __sched_fifo_max_prio = sched_get_priority_max (SCHED_FIFO);
36   atomic_write_barrier ();
37   __sched_fifo_min_prio = sched_get_priority_min (SCHED_FIFO);
38 }
39
40 int
41 __pthread_tpp_change_priority (int previous_prio, int new_prio)
42 {
43   struct pthread *self = THREAD_SELF;
44   struct priority_protection_data *tpp = THREAD_GETMEM (self, tpp);
45
46   if (tpp == NULL)
47     {
48       if (__sched_fifo_min_prio == -1)
49         __init_sched_fifo_prio ();
50
51       size_t size = sizeof *tpp;
52       size += (__sched_fifo_max_prio - __sched_fifo_min_prio + 1)
53               * sizeof (tpp->priomap[0]);
54       tpp = calloc (size, 1);
55       if (tpp == NULL)
56         return ENOMEM;
57       tpp->priomax = __sched_fifo_min_prio - 1;
58       THREAD_SETMEM (self, tpp, tpp);
59     }
60
61   assert (new_prio == -1
62           || (new_prio >= __sched_fifo_min_prio
63               && new_prio <= __sched_fifo_max_prio));
64   assert (previous_prio == -1
65           || (previous_prio >= __sched_fifo_min_prio
66               && previous_prio <= __sched_fifo_max_prio));
67
68   int priomax = tpp->priomax;
69   int newpriomax = priomax;
70   if (new_prio != -1)
71     {
72       if (tpp->priomap[new_prio - __sched_fifo_min_prio] + 1 == 0)
73         return EAGAIN;
74       ++tpp->priomap[new_prio - __sched_fifo_min_prio];
75       if (new_prio > priomax)
76         newpriomax = new_prio;
77     }
78
79   if (previous_prio != -1)
80     {
81       if (--tpp->priomap[previous_prio - __sched_fifo_min_prio] == 0
82           && priomax == previous_prio
83           && previous_prio > new_prio)
84         {
85           int i;
86           for (i = previous_prio - 1; i >= __sched_fifo_min_prio; --i)
87             if (tpp->priomap[i - __sched_fifo_min_prio])
88               break;
89           newpriomax = i;
90         }
91     }
92
93   if (priomax == newpriomax)
94     return 0;
95
96   lll_lock (self->lock, LLL_PRIVATE);
97
98   tpp->priomax = newpriomax;
99
100   int result = 0;
101
102   if ((self->flags & ATTR_FLAG_SCHED_SET) == 0)
103     {
104       if (__sched_getparam (self->tid, &self->schedparam) != 0)
105         result = errno;
106       else
107         self->flags |= ATTR_FLAG_SCHED_SET;
108     }
109
110   if ((self->flags & ATTR_FLAG_POLICY_SET) == 0)
111     {
112       self->schedpolicy = __sched_getscheduler (self->tid);
113       if (self->schedpolicy == -1)
114         result = errno;
115       else
116         self->flags |= ATTR_FLAG_POLICY_SET;
117     }
118
119   if (result == 0)
120     {
121       struct sched_param sp = self->schedparam;
122       if (sp.sched_priority < newpriomax || sp.sched_priority < priomax)
123         {
124           if (sp.sched_priority < newpriomax)
125             sp.sched_priority = newpriomax;
126
127           if (__sched_setscheduler (self->tid, self->schedpolicy, &sp) < 0)
128             result = errno;
129         }
130     }
131
132   lll_unlock (self->lock, LLL_PRIVATE);
133
134   return result;
135 }
136
137 int
138 __pthread_current_priority (void)
139 {
140   struct pthread *self = THREAD_SELF;
141   if ((self->flags & (ATTR_FLAG_POLICY_SET | ATTR_FLAG_SCHED_SET))
142       == (ATTR_FLAG_POLICY_SET | ATTR_FLAG_SCHED_SET))
143     return self->schedparam.sched_priority;
144
145   int result = 0;
146
147   lll_lock (self->lock, LLL_PRIVATE);
148
149   if ((self->flags & ATTR_FLAG_SCHED_SET) == 0)
150     {
151       if (__sched_getparam (self->tid, &self->schedparam) != 0)
152         result = -1;
153       else
154         self->flags |= ATTR_FLAG_SCHED_SET;
155     }
156
157   if ((self->flags & ATTR_FLAG_POLICY_SET) == 0)
158     {
159       self->schedpolicy = __sched_getscheduler (self->tid);
160       if (self->schedpolicy == -1)
161         result = -1;
162       else
163         self->flags |= ATTR_FLAG_POLICY_SET;
164     }
165
166   if (result != -1)
167     result = self->schedparam.sched_priority;
168
169   lll_unlock (self->lock, LLL_PRIVATE);
170
171   return result;
172 }