1 /*******************************************************************************
2 
3     Functions to set a process' CPU affinity.
4 
5     This module uses the GNU API and contains declarations and functionality
6     found in <sched.h> on GNU/Linux. See
7     http://www.gnu.org/software/libc/manual/html_node/CPU-Affinity.html
8 
9     Copyright:
10         Copyright (c) 2009-2016 dunnhumby Germany GmbH.
11         All rights reserved.
12 
13     License:
14         Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
15         Alternatively, this file may be distributed under the terms of the Tango
16         3-Clause BSD License (see LICENSE_BSD.txt for details).
17 
18 *******************************************************************************/
19 
20 module ocean.sys.CpuAffinity;
21 
22 import ocean.meta.types.Qualifiers;
23 import ocean.meta.types.Typedef;
24 
25 import core.sys.posix.sys.types : pid_t;
26 
27 /*******************************************************************************
28 
29     Definition of external functions required to set cpu affinity.
30 
31 *******************************************************************************/
32 
33 private extern ( C )
34 {
35     /* Type for array elements in 'cpu_set_t'.  */
36     mixin(Typedef!(uint, "__cpu_mask"));
37 
38     /* Size definition for CPU sets.  */
39     static immutable __CPU_SETSIZE = 1024;
40     static immutable __NCPUBITS = (8 * __cpu_mask.sizeof);
41 
42     /* Data structure to describe CPU mask.  */
43     struct cpu_set_t
44     {
45         __cpu_mask[__CPU_SETSIZE / __NCPUBITS] __bits;
46     }
47 
48     int sched_setaffinity(pid_t pid, size_t cpusetsize, cpu_set_t *mask);
49 }
50 
51 
52 
53 /*******************************************************************************
54 
55     Struct containing static functions for cpu affinity.
56 
57 *******************************************************************************/
58 
59 public struct CpuAffinity
60 {
61     import ocean.sys.ErrnoException;
62     import core.stdc.errno: EINVAL;
63 
64 static:
65 
66     /***************************************************************************
67 
68         Sets the CPU affinity of the calling process.
69 
70         Params:
71             cpu = index of cpu to run process on
72 
73         Throws:
74             ErrnoException on failure. Possible errors:
75             - EINVAL: The processor is not currently physically on the system
76                 and permitted to the process according to any restrictions that
77                 may be imposed by the "cpuset" mechanism described in cpuset(7).
78             - EPERM: The calling process does not have appropriate privileges.
79 
80     ***************************************************************************/
81 
82     public void set ( uint cpu )
83     {
84         try
85         {
86             cpu_set_t cpu_set;
87             CPU_SET(cast(__cpu_mask)cpu, cpu_set);
88 
89             enum pid_t pid = 0; // 0 := calling process
90             if (sched_setaffinity(pid, cpu_set_t.sizeof, &cpu_set))
91             {
92                 throw (new ErrnoException).useGlobalErrno("sched_setaffinity");
93             }
94         }
95         catch (ErrnoException e)
96         {
97             /*
98              * Add a sensible error message for EINVAL because this error can be
99              * caused by a bad parameter value in the config.ini, and the
100              * standard message "sched_setaffinity: Invalid argument" may appear
101              * cryptic to the user.
102              */
103 
104             if (e.errorNumber == EINVAL)
105             {
106                 e.append(" - probably attempted to set the CPU affinity to a " ~
107                          "CPU that doesn't exist in this system.");
108             }
109 
110             throw e;
111         }
112     }
113 
114 
115     // TODO: multiple CPU affinity setter (if needed)
116 
117 
118     /***************************************************************************
119 
120         CPU index bit mask array index. Converted from the __CPUELT macro
121         defined in bits/sched.h:
122 
123         ---
124 
125             # define __CPUELT(cpu)    ((cpu) / __NCPUBITS)
126 
127         ---
128 
129         Params:
130             cpu = cpu index
131 
132         Returns:
133             index of bit mask array element which the indexed cpu is within
134 
135     ***************************************************************************/
136 
137     private size_t CPUELT ( uint cpu )
138     {
139         return (cpu / __NCPUBITS);
140     }
141 
142 
143     /***************************************************************************
144 
145         CPU index bit mask. Converted from the __CPUMASK macro defined in
146         bits/sched.h:
147 
148         ---
149 
150             # define __CPUMASK(cpu) ((__cpu_mask) 1 << ((cpu) % __NCPUBITS))
151 
152         ---
153 
154         Params:
155             cpu = cpu index
156 
157         Returns:
158             bit mask with the indexed cpu set to 1
159 
160     ***************************************************************************/
161 
162     private __cpu_mask CPUMASK ( uint cpu )
163     {
164         return cast(__cpu_mask)(1 << (cpu % __NCPUBITS));
165     }
166 
167 
168     /***************************************************************************
169 
170         Sets the bit mask of the provided cpu_set_t to the indexed cpu.
171         Converted from the __CPU_SET macro defined in bits/sched.h:
172 
173         ---
174 
175             # define __CPU_SET(cpu, cpusetp) \
176               ((cpusetp)->__bits[__CPUELT (cpu)] |= __CPUMASK (cpu))
177 
178         ---
179 
180         Params:
181             cpu = cpu index
182             set = cpu set
183 
184         Throws:
185             ErrnoException (EINVAL) if cpu is too high to fit in set.
186 
187     ***************************************************************************/
188 
189     private void CPU_SET ( uint cpu, ref cpu_set_t set )
190     {
191         auto i = CPUELT(cpu);
192 
193         if (i < set.__bits.length)
194         {
195             set.__bits[i] |= CPUMASK(cpu);
196         }
197         else
198         {
199             throw (new ErrnoException).set(EINVAL, "CPU_SET")
200                                       .append(" - CPU index too high");
201         }
202     }
203 }