summaryrefslogtreecommitdiffstats
path: root/cmake/tuklib_cpucores.cmake
blob: e5e9c34d7fec9c17a82aedf459a293e83af6dd46 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# SPDX-License-Identifier: 0BSD

#############################################################################
#
# tuklib_cpucores.cmake - see tuklib_cpucores.m4 for description and comments
#
# Author: Lasse Collin
#
#############################################################################

include("${CMAKE_CURRENT_LIST_DIR}/tuklib_common.cmake")
include(CheckCSourceCompiles)
include(CheckIncludeFile)

function(tuklib_cpucores_internal_check)
    if(WIN32 OR CYGWIN)
        # Nothing to do, the tuklib_cpucores.c handles it.
        set(TUKLIB_CPUCORES_DEFINITIONS "" CACHE INTERNAL "")
        return()
    endif()

    # glibc-based systems (GNU/Linux and GNU/kFreeBSD) have
    # sched_getaffinity(). The CPU_COUNT() macro was added in glibc 2.9.
    # glibc 2.9 is old enough that if someone uses the code on older glibc,
    # the fallback to sysconf() should be good enough.
    #
    # NOTE: This required that _GNU_SOURCE is defined. We assume that whatever
    #       feature test macros the caller wants to use are already set in
    #       CMAKE_REQUIRED_DEFINES and in the target defines.
    check_c_source_compiles("
            #include <sched.h>
            int main(void)
            {
                cpu_set_t cpu_mask;
                sched_getaffinity(0, sizeof(cpu_mask), &cpu_mask);
                return CPU_COUNT(&cpu_mask);
            }
        "
        TUKLIB_CPUCORES_SCHED_GETAFFINITY)
    if(TUKLIB_CPUCORES_SCHED_GETAFFINITY)
        set(TUKLIB_CPUCORES_DEFINITIONS
            "TUKLIB_CPUCORES_SCHED_GETAFFINITY"
            CACHE INTERNAL "")
        return()
    endif()

    # FreeBSD has both cpuset and sysctl. Look for cpuset first because
    # it's a better approach.
    #
    # This test would match on GNU/kFreeBSD too but it would require
    # -lfreebsd-glue when linking and thus in the current form this would
    # fail on GNU/kFreeBSD. The above test for sched_getaffinity() matches
    # on GNU/kFreeBSD so the test below should never run on that OS.
    check_c_source_compiles("
            #include <sys/param.h>
            #include <sys/cpuset.h>
            int main(void)
            {
                cpuset_t set;
                cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, -1,
                                   sizeof(set), &set);
                return 0;
            }
        "
        TUKLIB_CPUCORES_CPUSET)
    if(TUKLIB_CPUCORES_CPUSET)
        set(TUKLIB_CPUCORES_DEFINITIONS "HAVE_PARAM_H;TUKLIB_CPUCORES_CPUSET"
            CACHE INTERNAL "")
        return()
    endif()

    # On OS/2, both sysconf() and sysctl() pass the tests in this file,
    # but only sysctl() works. On QNX it's the opposite: only sysconf() works
    # (although it assumes that _POSIX_SOURCE, _XOPEN_SOURCE, and
    # _POSIX_C_SOURCE are undefined or alternatively _QNX_SOURCE is defined).
    #
    # We test sysctl() first and intentionally break the sysctl() test on QNX
    # so that sysctl() is never used on QNX.
    check_include_file(sys/param.h HAVE_SYS_PARAM_H)
    if(HAVE_SYS_PARAM_H)
        list(APPEND CMAKE_REQUIRED_DEFINITIONS -DHAVE_SYS_PARAM_H)
    endif()
    check_c_source_compiles("
            #ifdef __QNX__
            compile error
            #endif
            #ifdef HAVE_SYS_PARAM_H
            #   include <sys/param.h>
            #endif
            #include <sys/sysctl.h>
            int main(void)
            {
            #ifdef HW_NCPUONLINE
                /* This is preferred on OpenBSD, see tuklib_cpucores.c. */
                int name[2] = { CTL_HW, HW_NCPUONLINE };
            #else
                int name[2] = { CTL_HW, HW_NCPU };
            #endif
                int cpus;
                size_t cpus_size = sizeof(cpus);
                sysctl(name, 2, &cpus, &cpus_size, NULL, 0);
                return 0;
            }
        "
        TUKLIB_CPUCORES_SYSCTL)
    if(TUKLIB_CPUCORES_SYSCTL)
        if(HAVE_SYS_PARAM_H)
            set(TUKLIB_CPUCORES_DEFINITIONS
                "HAVE_PARAM_H;TUKLIB_CPUCORES_SYSCTL"
                CACHE INTERNAL "")
        else()
            set(TUKLIB_CPUCORES_DEFINITIONS
                "TUKLIB_CPUCORES_SYSCTL"
                CACHE INTERNAL "")
        endif()
        return()
    endif()

    # Many platforms support sysconf().
    check_c_source_compiles("
            #include <unistd.h>
            int main(void)
            {
                long i;
            #ifdef _SC_NPROCESSORS_ONLN
                /* Many systems using sysconf() */
                i = sysconf(_SC_NPROCESSORS_ONLN);
            #else
                /* IRIX */
                i = sysconf(_SC_NPROC_ONLN);
            #endif
                return 0;
            }
        "
        TUKLIB_CPUCORES_SYSCONF)
    if(TUKLIB_CPUCORES_SYSCONF)
        set(TUKLIB_CPUCORES_DEFINITIONS "TUKLIB_CPUCORES_SYSCONF"
            CACHE INTERNAL "")
        return()
    endif()

    # HP-UX
    check_c_source_compiles("
            #include <sys/param.h>
            #include <sys/pstat.h>
            int main(void)
            {
                struct pst_dynamic pst;
                pstat_getdynamic(&pst, sizeof(pst), 1, 0);
                (void)pst.psd_proc_cnt;
                return 0;
            }
        "
        TUKLIB_CPUCORES_PSTAT_GETDYNAMIC)
    if(TUKLIB_CPUCORES_PSTAT_GETDYNAMIC)
        set(TUKLIB_CPUCORES_DEFINITIONS "TUKLIB_CPUCORES_PSTAT_GETDYNAMIC"
            CACHE INTERNAL "")
        return()
    endif()
endfunction()

function(tuklib_cpucores TARGET_OR_ALL)
    if(NOT DEFINED TUKLIB_CPUCORES_FOUND)
        message(STATUS
                "Checking how to detect the number of available CPU cores")
        tuklib_cpucores_internal_check()

        if(DEFINED TUKLIB_CPUCORES_DEFINITIONS)
            set(TUKLIB_CPUCORES_FOUND 1 CACHE INTERNAL "")
        else()
            set(TUKLIB_CPUCORES_FOUND 0 CACHE INTERNAL "")
            message(WARNING
                    "No method to detect the number of CPU cores was found")
        endif()
    endif()

    if(TUKLIB_CPUCORES_FOUND)
        tuklib_add_definitions("${TARGET_OR_ALL}"
                               "${TUKLIB_CPUCORES_DEFINITIONS}")
    endif()
endfunction()