EFL 1.7 svn doobies
[profile/ivi/eina.git] / src / lib / eina_cpu.c
1 /* EINA - EFL data type library
2  * Copyright (C) 2007-2008 Jorge Luis Zapata Muga
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library;
16  * if not, see <http://www.gnu.org/licenses/>.
17  */
18
19 #ifdef HAVE_CONFIG_H
20 # include "config.h"
21 #endif
22
23 #ifdef EFL_HAVE_THREADS
24 # ifdef _WIN32
25 #  define WIN32_LEAN_AND_MEAN
26 #  include <windows.h>
27 # elif defined (__sun) || defined(__GNU__)
28 #  include <unistd.h>
29 # elif defined (__FreeBSD__) || defined (__OpenBSD__) || \
30    defined (__NetBSD__) || defined (__DragonFly__) || defined (__MacOSX__) || \
31    (defined (__MACH__) && defined (__APPLE__))
32 #  include <unistd.h>
33 #  include <sys/param.h>
34 #  include <sys/sysctl.h>
35 # elif defined (__linux__) || defined(__GLIBC__)
36 #  include <sched.h>
37 # endif
38 # ifdef EFL_HAVE_POSIX_THREADS
39 #  include <pthread.h>
40 # endif
41
42 # define TH_MAX 8
43 #endif
44
45 #include <stdlib.h>
46 #include <stdio.h>
47 #include <string.h>
48 #include <errno.h>
49
50 #include "eina_cpu.h"
51
52 /*============================================================================*
53 *                                  Local                                     *
54 *============================================================================*/
55
56 /* FIXME this ifdefs should be replaced */
57 #if defined(__i386__) || defined(__x86_64__)
58 /* We save ebx and restore it to be PIC compatible */
59 static inline void _x86_cpuid(int op, int *a, int *b, int *c, int *d)
60 {
61    __asm__ volatile (
62 #if defined(__x86_64__)
63       "pushq %%rbx      \n\t" /* save %ebx */
64 #else
65       "pushl %%ebx      \n\t" /* save %ebx */
66 #endif
67       "cpuid            \n\t"
68       "movl %%ebx, %1   \n\t" /* save what cpuid just put in %ebx */
69 #if defined(__x86_64__)
70       "popq %%rbx       \n\t" /* restore the old %ebx */
71 #else
72       "popl %%ebx       \n\t" /* restore the old %ebx */
73 #endif
74       : "=a" (*a), "=r" (*b), "=c" (*c), "=d" (*d)
75       : "a" (op)
76       : "cc");
77 }
78
79 static
80 void _x86_simd(Eina_Cpu_Features *features)
81 {
82    int a, b, c, d;
83
84    _x86_cpuid(1, &a, &b, &c, &d);
85    /*
86     * edx
87     * 18 = PN (Processor Number)
88     * 19 = CLFlush (Cache Line Flush)
89     * 23 = MMX
90     * 25 = SSE
91     * 26 = SSE2
92     * 28 = HTT (Hyper Threading)
93     * ecx
94     * 0 = SSE3
95     */
96    if ((d >> 23) & 1)
97       *features |= EINA_CPU_MMX;
98
99    if ((d >> 25) & 1)
100       *features |= EINA_CPU_SSE;
101
102    if ((d >> 26) & 1)
103       *features |= EINA_CPU_SSE2;
104
105    if (c & 1)
106       *features |= EINA_CPU_SSE3;
107 }
108 #endif
109
110 /*============================================================================*
111 *                                 Global                                     *
112 *============================================================================*/
113
114 /*============================================================================*
115 *                                   API                                      *
116 *============================================================================*/
117
118 /* FIXME the features checks should be called when this function is called?
119  * or make it static by doing eina_cpu_init() and return a local var
120  */
121 /**
122  *
123  * @return
124  */
125 EAPI Eina_Cpu_Features eina_cpu_features_get(void)
126 {
127    Eina_Cpu_Features ecf = 0;
128 #if defined(__i386__) || defined(__x86_64__)
129    _x86_simd(&ecf);
130 #endif
131    return ecf;
132 }
133
134 static int _cpu_count = -1;
135
136 static int
137 _eina_cpu_count_internal(void)
138 {
139 #ifdef EFL_HAVE_THREADS
140
141 # if   defined (_WIN32)
142    SYSTEM_INFO sysinfo;
143
144    GetSystemInfo(&sysinfo);
145    return sysinfo.dwNumberOfProcessors;
146
147 # elif defined (__sun) || defined(__GNU__)
148    /*
149     * _SC_NPROCESSORS_ONLN: number of processors that are online, that
150                             is available when sysconf is called. The number
151                             of cpu can change by admins.
152     * _SC_NPROCESSORS_CONF: maximum number of processors that are available
153                             to the current OS instance. That number can be
154                             change after a reboot.
155     * _SC_NPROCESSORS_MAX : maximum number of processors that are on the
156                             motherboard.
157     */
158    return sysconf(_SC_NPROCESSORS_ONLN);
159
160 # elif defined (__FreeBSD__) || defined (__OpenBSD__) || \
161    defined (__NetBSD__) || defined (__DragonFly__) || defined (__MacOSX__) || \
162    (defined (__MACH__) && defined (__APPLE__))
163
164    int mib[4];
165    int cpus;
166    size_t len = sizeof(cpus);
167
168    mib[0] = CTL_HW;
169 #ifdef HW_AVAILCPU
170    mib[1] = HW_AVAILCPU;
171 #else
172    mib[1] = HW_NCPU;
173 #endif
174    sysctl(mib, 2, &cpus, &len, NULL, 0);
175    if (cpus < 1)
176       cpus = 1;
177
178    return cpus;
179
180 # elif defined (__linux__) || defined(__GLIBC__)
181    cpu_set_t cpu;
182    int i;
183    static int cpus = 0;
184
185    if (cpus != 0)
186       return cpus;
187
188    CPU_ZERO(&cpu);
189    if (sched_getaffinity(0, sizeof(cpu), &cpu) != 0)
190      {
191         fprintf(stderr, "[Eina] could not get cpu affinity: %s\n",
192                 strerror(errno));
193         return 1;
194      }
195
196    for (i = 0; i < TH_MAX; i++)
197      {
198         if (CPU_ISSET(i, &cpu))
199            cpus = i + 1;
200         else
201            break;
202      }
203    return cpus;
204
205 # else
206 #  error "eina_cpu_count() error: Platform not supported"
207 # endif
208 #else
209    return 1;
210 #endif
211 }
212
213 EAPI int eina_cpu_count(void)
214 {
215    return _cpu_count;
216 }
217
218 void eina_cpu_count_internal(void)
219 {
220    if (getenv("EINA_CPU_FAKE"))
221      _cpu_count = atoi(getenv("EINA_CPU_FAKE"));
222    else
223      _cpu_count = _eina_cpu_count_internal();
224 }