]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - 6/lib/libpthread/thread/thr_concurrency.c
Clone Kip's Xen on stable/6 tree so that I can work on improving FreeBSD/amd64
[FreeBSD/FreeBSD.git] / 6 / lib / libpthread / thread / thr_concurrency.c
1 /*
2  * Copyright (c) 2003 Daniel M. Eischen <deischen@freebsd.org>
3  * Copyright (c) 2003 Sergey Osokin <osa@freebsd.org.ru>.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Neither the name of the author nor the names of any co-contributors
12  *    may be used to endorse or promote products derived from this software
13  *    without specific prior written permission.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  * $FreeBSD$
28  */
29 #include <errno.h>
30 #include <pthread.h>
31 #include <sys/types.h>
32 #include <sys/sysctl.h>
33
34 #include "thr_private.h"
35
36 /*#define DEBUG_CONCURRENCY */
37 #ifdef DEBUG_CONCURRENCY
38 #define DBG_MSG         stdout_debug
39 #else
40 #define DBG_MSG(x...)
41 #endif
42
43 static int level = 0;
44
45 __weak_reference(_pthread_getconcurrency, pthread_getconcurrency);
46 __weak_reference(_pthread_setconcurrency, pthread_setconcurrency);
47
48 int
49 _pthread_getconcurrency(void)
50 {
51         return (level);
52 }
53
54 int
55 _pthread_setconcurrency(int new_level)
56 {
57         int ret;
58
59         if (new_level < 0)
60                 ret = EINVAL;
61         else if (new_level == level)
62                 ret = 0;
63         else if (new_level == 0) {
64                 level = 0;
65                 ret = 0;
66         } else if ((_kse_isthreaded() == 0) && (_kse_setthreaded(1) != 0)) {
67                 DBG_MSG("Can't enable threading.\n");
68                 ret = EAGAIN;
69         } else {
70                 ret = _thr_setconcurrency(new_level);
71                 if (ret == 0)
72                         level = new_level;
73         }
74         return (ret);
75 }
76
77 int
78 _thr_setconcurrency(int new_level)
79 {
80         struct pthread *curthread;
81         struct kse *newkse, *kse;
82         kse_critical_t crit;
83         int kse_count;
84         int i;
85         int ret;
86
87         /*
88          * Turn on threaded mode, if failed, it is unnecessary to
89          * do further work.
90          */
91         if (_kse_isthreaded() == 0 && _kse_setthreaded(1))
92                 return (EAGAIN);
93
94         ret = 0;
95         curthread = _get_curthread();
96         /* Race condition, but so what. */
97         kse_count = _kse_initial->k_kseg->kg_ksecount;
98         if (new_level > kse_count) {
99                 for (i = kse_count; i < new_level; i++) {
100                         newkse = _kse_alloc(curthread, 0);
101                         if (newkse == NULL) {
102                                 DBG_MSG("Can't alloc new KSE.\n");
103                                 ret = EAGAIN;
104                                 break;
105                         }
106                         newkse->k_kseg = _kse_initial->k_kseg;
107                         newkse->k_schedq = _kse_initial->k_schedq;
108                         newkse->k_curthread = NULL;
109                         crit = _kse_critical_enter();
110                         KSE_SCHED_LOCK(curthread->kse, newkse->k_kseg);
111                         TAILQ_INSERT_TAIL(&newkse->k_kseg->kg_kseq,
112                             newkse, k_kgqe);
113                         newkse->k_kseg->kg_ksecount++;
114                         newkse->k_flags |= KF_STARTED;
115                         KSE_SCHED_UNLOCK(curthread->kse, newkse->k_kseg);
116                         if (kse_create(&newkse->k_kcb->kcb_kmbx, 0) != 0) {
117                                 KSE_SCHED_LOCK(curthread->kse, newkse->k_kseg);
118                                 TAILQ_REMOVE(&newkse->k_kseg->kg_kseq,
119                                     newkse, k_kgqe);
120                                 newkse->k_kseg->kg_ksecount--;
121                                 KSE_SCHED_UNLOCK(curthread->kse,
122                                     newkse->k_kseg);
123                                 _kse_critical_leave(crit);
124                                 _kse_free(curthread, newkse);
125                                 DBG_MSG("kse_create syscall failed.\n");
126                                 ret = EAGAIN;
127                                 break;
128                         } else {
129                                 _kse_critical_leave(crit);
130                         }
131                 }
132         } else if (new_level < kse_count) {
133                 kse_count = 0;
134                 crit = _kse_critical_enter();
135                 KSE_SCHED_LOCK(curthread->kse, _kse_initial->k_kseg);
136                 /* Count the number of active KSEs */
137                 TAILQ_FOREACH(kse, &_kse_initial->k_kseg->kg_kseq, k_kgqe) {
138                         if ((kse->k_flags & KF_TERMINATED) == 0)
139                                 kse_count++;
140                 }
141                 /* Reduce the number of active KSEs appropriately. */
142                 kse = TAILQ_FIRST(&_kse_initial->k_kseg->kg_kseq);
143                 while ((kse != NULL) && (kse_count > new_level)) {
144                         if ((kse != _kse_initial) &&
145                             ((kse->k_flags & KF_TERMINATED) == 0)) {
146                                 kse->k_flags |= KF_TERMINATED;
147                                 kse_count--;
148                                 /* Wakup the KSE in case it is idle. */
149                                 kse_wakeup(&kse->k_kcb->kcb_kmbx);
150                         }
151                         kse = TAILQ_NEXT(kse, k_kgqe);
152                 }
153                 KSE_SCHED_UNLOCK(curthread->kse, _kse_initial->k_kseg);
154                 _kse_critical_leave(crit);
155         }
156         return (ret);
157 }
158
159 int
160 _thr_setmaxconcurrency(void)
161 {
162         int vcpu;
163         size_t len;
164         int ret;
165
166         len = sizeof(vcpu);
167         ret = sysctlbyname("kern.threads.virtual_cpu", &vcpu, &len, NULL, 0);
168         if (ret == 0 && vcpu > 0)
169                 ret = _thr_setconcurrency(vcpu);
170         return (ret);
171 }
172