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