File Coverage

blib/lib/Sys/CpuAffinity.pm
Criterion Covered Total %
statement 248 825 30.0
branch 120 520 23.0
condition 25 151 16.5
subroutine 67 79 84.8
pod 3 4 75.0
total 463 1579 29.3


line stmt bran cond sub pod time code
1             package Sys::CpuAffinity;
2 6     6   115395 use Math::BigInt;
  6         173311  
  6         30  
3 6     6   144392 use Carp;
  6         13  
  6         391  
4 6     6   36 use warnings;
  6         12  
  6         1641  
5 6     6   34 use strict;
  6         11  
  6         164  
6 6     6   1645 use base qw(DynaLoader);
  6         14  
  6         5392  
7 6     6   4345 use Data::Dumper;
  6         43074  
  6         52283  
8              
9             ## no critic (ProhibitBacktick,RequireExtendedFormatting)
10             ## no critic (DotMatch,LineBoundary,Sigils,Punctuation,Quotes,Magic,Checked)
11             ## no critic (NamingConventions::Capitalization,BracedFileHandle)
12              
13             our $VERSION = '1.13_05';
14             our $DEBUG = $ENV{DEBUG} || 0;
15             our $XS_LOADED = 0;
16             eval { bootstrap Sys::CpuAffinity $VERSION; $XS_LOADED = 1 };
17              
18 5533     5533 0 4843862 sub TWO () { Math::BigInt->new(2) }
19              
20             #
21             # Development guide:
22             #
23             # when you figure out a new way to perform a task
24             # (in this case, getting cpu affinity), write the method and insert
25             # the call into the chain here.
26             #
27             # Methods should be named _getAffinity_with_XXX, _setAffinity_with_XXX,
28             # or _getNumCpus_from_XXX. The t/inventory.pl file will identify these
29             # methods so they can be included in the tests.
30             #
31             # The new method should return false (0 or '' or undef) whenever it
32             # knows it is the wrong tool for the current system or any other time
33             # that it can't figure out the answer.
34             #
35             # For XS-based solutions, the stub will go in the distributions
36             # contrib/ directory, and will be available if it successfully
37             # compiles during the installation process. See
38             # _getAffinity_with_xs_sched_getaffinity for an example of
39             # how to use a compiled function. All exported XS function names
40             # should begin with "xs_" and all function names, even the ones
41             # that aren't exported to XS, should be unique across the whole
42             # /contrib space.
43             #
44             # Methods that might return with the wrong answer (for example, methods
45             # that make a guess) should go toward the end of the chain. This
46             # probably should include methods that read environment variables
47             # or methods that rely on external commands as these methods are
48             # easier to spoof, even accidentally.
49             #
50              
51             sub getAffinity {
52 49     49 1 16604 my ($pid, %flags) = @_; # %flags reserved for future use
53 49         117 my $wpid = $pid;
54              
55             # 2020-12-18: taskset only returns 32 bits? Give xs_sched more priority
56            
57 49   50     210 my $mask = 0
58             || _getAffinity_with_xs_sched_getaffinity($pid)
59             || _getAffinity_with_xs_pthread_self_getaffinity($pid)
60             || _getAffinity_with_taskset($pid)
61             || _getAffinity_with_BSD_Process_Affinity($pid)
62             || _getAffinity_with_xs_freebsd_getaffinity($pid)
63             || _getAffinity_with_cpuset($pid)
64             || _getAffinity_with_xs_processor_affinity($pid)
65             || _getAffinity_with_pbind($pid)
66             || _getAffinity_with_xs_processor_bind($pid)
67             || _getAffinity_with_psaix($pid)
68             || _getAffinity_with_xs_win32($pid)
69             || _getAffinity_with_xs_irix_sysmp($pid)
70             || _getAffinity_with_Win32Process($wpid)
71             || _getAffinity_with_Win32API($wpid)
72             || 0;
73              
74 49 100       1441 return if $mask == 0;
75 43 50       7370 return wantarray ? _maskToArray($mask) : $mask;
76             }
77              
78             sub _sanitize_set_affinity_args {
79 19     19   59 my ($pid,$mask) = @_;
80              
81 19 50       65 if ($DEBUG) {
82 0         0 print STDERR "sanitize_set_affinity_args: input is ",Dumper(@_),"\n";
83             }
84              
85 19 50       79 return if ! $pid;
86 19 50       80 if (ref $mask eq 'ARRAY') {
87 0         0 $mask = _arrayToMask(@$mask);
88 0 0       0 if ($DEBUG) {
89 0         0 print STDERR "sanitize_set_affinity_args: ",
90             Dumper($_[1])," => $mask\n";
91             }
92             }
93 19         128 my $np = getNumCpus();
94 19 100 66     122 if ($mask == -1 && $np > 0) {
95 4         14 $mask = (TWO ** $np) - 1;
96 4 50       3518 if ($DEBUG) {
97 0         0 print STDERR "sanitize_set_affinity_args: -1 => ",
98             $mask," ",Dumper($mask),"\n";
99             }
100             }
101 19 100       1520 if ($mask <= 0) {
102 2         1300 carp "Sys::CpuAffinity: invalid mask $mask in call to setAffinty\n";
103 2         28 return;
104             }
105              
106 17         3406 my $maxmask = TWO ** $np;
107 17 100 66     6335 if ($maxmask > 1 && $mask >= $maxmask) {
108 2         460 my $newmask = $mask & ($maxmask - 1);
109 2 50       1512 if ($newmask == 0) {
110 2         492 carp "Sys::CpuAffinity: mask $mask is not valid for system with ",
111             "$np processors.\n";
112 2         862 return;
113             } else {
114 0         0 carp "Sys::CpuAffinity: mask $mask adjusted to $newmask for ",
115             "system with $np processors\n";
116 0         0 $mask = $newmask;
117             }
118             }
119 15         2701 $_[1] = $mask;
120 15         61 return 1;
121             }
122              
123             sub setAffinity {
124 19     19 1 11992 my ($pid, $mask, %flags) = @_; # %flags reserved for future use
125              
126 19 100       139 return 0 if ! _sanitize_set_affinity_args($pid, $mask);
127              
128 15   50     95 return _setAffinity_with_Win32API($pid,$mask)
129             || _setAffinity_with_xs_win32($pid,$mask)
130             || _setAffinity_with_Win32Process($pid,$mask)
131             || _setAffinity_with_xs_sched_setaffinity($pid,$mask)
132             || _setAffinity_with_taskset($pid,$mask)
133             || _setAffinity_with_BSD_Process_Affinity($pid,$mask)
134             || _setAffinity_with_xs_freebsd_setaffinity($pid,$mask)
135             || _setAffinity_with_xs_processor_affinity($pid,$mask)
136             || _setAffinity_with_pbind($pid,$mask)
137             || _setAffinity_with_xs_processor_bind($pid,$mask)
138             || _setAffinity_with_xs_pthread_self_setaffinity($pid,$mask)
139             || _setAffinity_with_bindprocessor($pid,$mask)
140             || _setAffinity_with_cpuset($pid,$mask)
141             || _setAffinity_with_xs_irix_sysmp($pid,$mask)
142             || 0;
143             }
144              
145             our $_NUM_CPUS_CACHED = 0;
146             sub getNumCpus {
147 22 100   22 1 2531 if ($_NUM_CPUS_CACHED) {
148 18         74 return $_NUM_CPUS_CACHED;
149             }
150 4   0     23 return $_NUM_CPUS_CACHED =
151             _getNumCpus_from_xs_Win32API_System_Info()
152             || _getNumCpus_from_xs_cpusetGetCPUCount()
153             || _getNumCpus_from_proc_cpuinfo()
154             || _getNumCpus_from_proc_stat()
155             || _getNumCpus_from_lsdev()
156             || _getNumCpus_from_bindprocessor()
157             || _getNumCpus_from_BSD_Process_Affinity()
158             || _getNumCpus_from_sysctl_freebsd()
159             || _getNumCpus_from_sysctl()
160             || _getNumCpus_from_dmesg_bsd()
161             || _getNumCpus_from_xs_solaris()
162             || _getNumCpus_from_dmesg_solaris()
163             || _getNumCpus_from_psrinfo()
164             || _getNumCpus_from_hinv()
165             || _getNumCpus_from_hwprefs()
166             || _getNumCpus_from_system_profiler()
167             || _getNumCpus_from_Win32API_System_Info()
168             || _getNumCpus_from_Test_Smoke_SysInfo()
169             || _getNumCpus_from_prtconf() # slower than bindprocessor, lsdev
170             || _getNumCpus_from_ENV()
171             || _getNumCpus_from_taskset()
172             || -1;
173             }
174              
175             ######################################################################
176              
177             # count processors toolbox
178              
179             sub _getNumCpus_from_ENV {
180             # in some OS, the number of processors is part of the default environment
181             # this also makes it easy to spoof the value (is that good or bad?)
182 1 50 33 1   32 if ($^O eq 'MSWin32' || $^O eq 'cygwin') {
183 0 0       0 if (defined $ENV{NUMBER_OF_PROCESSORS}) {
184 0         0 _debug("from Windows ENV: nproc=$ENV{NUMBER_OF_PROCESSORS}");
185 0         0 return $ENV{NUMBER_OF_PROCESSORS};
186             }
187             }
188 1         2 return 0;
189             }
190              
191             our %WIN32_SYSTEM_INFO = ();
192             our %WIN32API = ();
193              
194             sub __is_wow64 {
195             # determines whether this (Windows) program is running the WOW64 emulator
196             # (to let 32-bit apps run on 64-bit architecture)
197              
198             # used in _getNumCpus_from_Win32API_System_Info to decide whether to use
199             # GetSystemInfo or GetNativeSystemInfo in the Windows API.
200              
201 0 0 0 0   0 return 0 if $^O ne 'MSWin32' && $^O ne 'cygwin';
202 0 0       0 return 0 if !_configModule('Win32::API');
203 0 0       0 return $Sys::CpuAffinity::IS_WOW64
204             if $Sys::CpuAffinity::IS_WOW64_INITIALIZED++;
205              
206 0         0 my $hmodule = _win32api('GetModuleHandle', 'kernel32');
207 0 0       0 return 0 if $hmodule == 0;
208              
209 0         0 my $proc = _win32api('GetProcAddress', $hmodule, 'IsWow64Process');
210 0 0       0 return 0 if $proc == 0;
211              
212 0         0 my $current = _win32api('GetCurrentProcess');
213 0 0       0 return 0 if $current == 0; # carp ...
214              
215 0         0 my $bool = 0;
216 0         0 my $result = _win32api('IsWow64Process', $current, $bool);
217 0 0       0 if ($result != 0) {
218 0         0 $Sys::CpuAffinity::IS_WOW64 = $bool;
219             }
220 0         0 $Sys::CpuAffinity::IS_WOW64_INITIALIZED++;
221 0         0 return $Sys::CpuAffinity::IS_WOW64;
222             }
223              
224             sub _getNumCpus_from_Win32API_System_Info {
225 1 50 33 1   176 return 0 if $^O ne 'MSWin32' && $^O ne 'cygwin';
226 0 0       0 return 0 if !_configModule('Win32::API');
227              
228 0 0       0 if (0 == scalar keys %WIN32_SYSTEM_INFO) {
229 0 0       0 if (!defined $WIN32API{'GetSystemInfo'}) {
230 0         0 my $is_wow64 = __is_wow64();
231 0         0 my $lpsysinfo_type_avail
232             = Win32::API::Type::is_known('LPSYSTEM_INFO');
233              
234 0 0       0 my $proto = sprintf 'BOOL %s(%s i)',
    0          
235             $is_wow64 ? 'GetNativeSystemInfo' : 'GetSystemInfo',
236             $lpsysinfo_type_avail ? 'LPSYSTEM_INFO' : 'PCHAR';
237              
238 0         0 $WIN32API{'GetSystemInfo'} = Win32::API->new('kernel32', $proto);
239             }
240              
241             # does this part break on 64-bit machines? Don't think so.
242 0         0 my $buffer = chr(0) x 36;
243 0         0 $WIN32API{'GetSystemInfo'}->Call($buffer);
244             ($WIN32_SYSTEM_INFO{'PageSize'},
245             $WIN32_SYSTEM_INFO{'...'},
246             $WIN32_SYSTEM_INFO{'...'},
247             $WIN32_SYSTEM_INFO{'...'},
248             $WIN32_SYSTEM_INFO{'NumberOfProcessors'},
249             $WIN32_SYSTEM_INFO{'...'},
250             $WIN32_SYSTEM_INFO{'...'},
251             $WIN32_SYSTEM_INFO{'...'},
252 0         0 $WIN32_SYSTEM_INFO{'...'})
253             = unpack 'VVVVVVVvv', substr $buffer,4;
254             }
255 0   0     0 return $WIN32_SYSTEM_INFO{'NumberOfProcessors'} || 0;
256             }
257              
258              
259             sub _getNumCpus_from_xs_cpusetGetCPUCount { # NOT TESTED irix
260 5 50 33 5   191 if ($XS_LOADED && defined &xs_cpusetGetCPUCount) {
261 0         0 return xs_cpusetGetCPUCount();
262             } else {
263 5         33 return 0;
264             }
265             }
266              
267             sub _getNumCpus_from_xs_Win32API_System_Info {
268 5 50   5   172 if (defined &xs_get_numcpus_from_windows_system_info) {
    50          
269 0         0 return xs_get_numcpus_from_windows_system_info();
270             } elsif (defined &xs_get_numcpus_from_windows_system_info_alt) {
271 0         0 return xs_get_numcpus_from_windows_system_info_alt();
272             } else {
273 5         33 return 0;
274             }
275             }
276              
277             sub _getNumCpus_from_proc_cpuinfo {
278              
279             # I'm told this could give the wrong answer with a "non-SMP kernel"
280             # http://www-oss.fnal.gov/fss/hypermail/archives/hyp-linux/0746.html
281              
282 5 50   5   160 return 0 if ! -r '/proc/cpuinfo';
283              
284 5         19 my $num_processors = 0;
285 5         15 my $cpuinfo_fh;
286 5 50       319 if (open $cpuinfo_fh, '<', '/proc/cpuinfo') {
287 5         554 while (<$cpuinfo_fh>) {
288 2160 100       5293 if (/^processor\s/) {
289 80         222 $num_processors++;
290             }
291             }
292 5         66 close $cpuinfo_fh;
293             }
294 5         53 _debug("from /proc/cpuinfo: nproc=$num_processors");
295 5   50     77 return $num_processors || 0;
296             }
297              
298             sub _getNumCpus_from_proc_stat {
299              
300 1 50   1   63 return 0 if ! -r '/proc/stat';
301              
302 1         4 my $num_processors = 0;
303 1         2 my $stat_fh;
304 1 50       206 if (open $stat_fh, '<', '/proc/stat') {
305 1         48 while (<$stat_fh>) {
306 24 100       70 if (/^cpu\d/i) {
307 16         33 $num_processors++;
308             }
309             }
310 1         11 close $stat_fh;
311             }
312 1         7 _debug("from /proc/stat: nproc=$num_processors");
313 1   50     7 return $num_processors || 0;
314             }
315              
316             sub __set_aix_hints {
317 0     0   0 my ($bindprocessor) = @_;
318 0         0 our $AIX_HINTS = { READY => 0 };
319 0 0       0 if (!$bindprocessor) {
320 0         0 $bindprocessor = _configExternalProgram('bindprocessor');
321             }
322 0 0       0 return unless $bindprocessor;
323              
324 0         0 my $vp_output = qx('$bindprocessor' -q 2>/dev/null);
325 0 0       0 if ($vp_output !~ s/The available process\S+ are:\s*//) {
326 0         0 return;
327             }
328 0         0 my @vp = split /\s+/, $vp_output;
329 0         0 @vp = sort { $a <=> $b } @vp;
  0         0  
330 0         0 $AIX_HINTS->{VIRTUAL_PROCESSORS} = \@vp;
331 0         0 my %vp = map {; $_ => -1 } @vp;
  0         0  
332 0         0 my $proc_output = qx('$bindprocessor' -s 0 2>/dev/null);
333 0 0       0 if ($proc_output !~ s/The available process\S+ are:\s*//) {
334 0         0 $AIX_HINTS->{PROCESSORS} = $AIX_HINTS->{VIRTUAL_PROCESSORS};
335 0         0 $AIX_HINTS->{NUM_CORES} = @vp;
336 0         0 return;
337             }
338 0         0 my @procs = split /\s+/, $proc_output;
339 0         0 @procs = sort { $a <=> $b } @procs;
  0         0  
340 0         0 $AIX_HINTS->{PROCESSORS} = \@procs;
341 0         0 $AIX_HINTS->{NUM_CORES} = @procs;
342 0         0 $AIX_HINTS->{READY} = 1;
343 0 0       0 if (@procs == @vp) {
344 0         0 foreach my $proc (@procs) {
345 0         0 $AIX_HINTS->{PROC_MAP}{$_} = $_;
346             }
347             } else {
348 0         0 my $core = -1;
349 0         0 foreach my $proc (@procs) {
350 0         0 $core++;
351 0         0 my $bound_output = qx('$bindprocessor' -b $proc 2>/dev/null);
352 0 0       0 if ($bound_output =~ s/The available process\S+ are:\s*//) {
353 0         0 my @bound_proc = split /\s+/, $bound_output;
354 0         0 foreach my $bound_proc (@bound_proc) {
355 0         0 $AIX_HINTS->{PROC_MAP}{$bound_proc} = $core;
356             }
357             }
358             }
359             }
360             }
361              
362             sub _is_solarisMultiCpuBinding {
363 0     0   0 our $SOLARIS_HINTS;
364 0 0       0 return unless $^O =~ /solaris/i;
365 0 0 0     0 if (!$SOLARIS_HINTS || !$SOLARIS_HINTS->{multicpu}) {
366 0         0 local $?;
367 0         0 my ($maj,$min) = split /[.]/, qx(uname -v);
368 0 0 0     0 if ($? == 0 && ($maj > 11 || ($maj == 11 && $min >= 2))) {
    0 0        
369 0         0 $SOLARIS_HINTS->{multicpu} = 'yes';
370             } elsif (defined &xs_setaffinity_processor_affinity) {
371 0         0 $SOLARIS_HINTS->{multicpu} = 'yes';
372             } else {
373 0         0 $SOLARIS_HINTS->{multicpu} = 'no';
374             }
375             }
376 0         0 return $SOLARIS_HINTS->{multicpu} eq 'yes';
377             }
378              
379             sub _getNumCpus_from_bindprocessor {
380 1 50   1   3431 return 0 if $^O !~ /aix/i;
381 0 0       0 return 0 if !_configExternalProgram('bindprocessor');
382 0         0 my $cmd = _configExternalProgram('bindprocessor');
383 0         0 our $AIX_HINTS;
384 0 0       0 __set_aix_hints($cmd) unless $AIX_HINTS;
385 0   0     0 return $AIX_HINTS->{NUM_CORES} || 0;
386             #my $bindprocessor_output = qx($cmd -s 0 2>/dev/null); # or $cmd -q ?
387 0         0 my $bindprocessor_output = qx($cmd -q 2>/dev/null); # or $cmd -s 0 ?
388 0         0 $bindprocessor_output =~ s/\s+$//;
389 0 0       0 return 0 if !$bindprocessor_output;
390              
391             # Typical output: "The available processors are: 0 1 2 3"
392 0         0 $bindprocessor_output =~ s/.*:\s+//;
393 0         0 my @p = split /\s+/, $bindprocessor_output;
394 0         0 return 0+@p;
395             }
396              
397             sub _getNumCpus_from_lsdev {
398 1 50   1   46 return 0 if $^O !~ /aix/i;
399 0 0       0 return 0 if !_configExternalProgram('lsdev');
400 0         0 my $cmd = _configExternalProgram('lsdev');
401 0         0 my @lsdev_output = qx($cmd -Cc processor 2>/dev/null);
402 0         0 return 0+@lsdev_output;
403             }
404              
405             sub _getNumCpus_from_dmesg_bsd {
406 1 50   1   29 return 0 if $^O !~ /bsd/i;
407              
408 0         0 my @dmesg;
409 0 0 0     0 if (-r '/var/run/dmesg.boot' && open my $fh, '<', '/var/run/dmesg.boot') {
    0          
410 0         0 @dmesg = <$fh>;
411 0         0 close $fh;
412             } elsif (! _configExternalProgram('dmesg')) {
413 0         0 return 0;
414             } else {
415 0         0 my $cmd = _configExternalProgram('dmesg');
416 0         0 @dmesg = qx($cmd 2> /dev/null);
417             }
418             # on the version of FreeBSD that I have to play with
419             # (8.0), dmesg contains this message:
420             #
421             # FreeBSD/SMP: Multiprocessor System Detected: 2 CPUs
422             #
423             # so we'll go with that.
424             #
425             # on NetBSD, the message is:
426             #
427             # cpu3 at mainbus0 apid 3: AMD 686-class, 1975MHz, id 0x100f53
428              
429             # try FreeBSD format
430 0         0 my @d = grep { /Multiprocessor System Detected:/i } @dmesg;
  0         0  
431 0         0 my $ncpus;
432 0 0       0 if (@d > 0) {
433 0         0 _debug("dmesg_bsd contains:\n@d");
434 0         0 ($ncpus) = $d[0] =~ /Detected: (\d+) CPUs/i;
435             }
436              
437             # try NetBSD format. This will also probably work for OpenBSD.
438 0 0       0 if (!$ncpus) {
439             # 1.05 - account for duplicates in @dmesg
440 0         0 my %d = ();
441 0         0 @d = grep { /^cpu\d+ at / } @dmesg;
  0         0  
442 0         0 foreach my $dmesg (@d) {
443 0 0       0 if ($dmesg =~ /^cpu(\d+) at /) {
444 0         0 $d{$1}++;
445             }
446             }
447 0         0 _debug("dmesg_bsd[2] contains:\n",@d);
448 0         0 $ncpus = scalar keys %d;
449             }
450 0 0       0 if (@dmesg < 50) {
451 0         0 _debug("full dmesg log:\n", @dmesg);
452             }
453 0   0     0 return $ncpus || 0;
454             }
455              
456             sub _getNumCpus_from_xs_solaris {
457 1 50   1   137 return 0 if $^O !~ /solaris/i;
458 0 0       0 return 0 if !defined &xs_solaris_numCpus;
459 0         0 my $n = eval { xs_solaris_numCpus() };
  0         0  
460 0   0     0 return $n || 0;
461             }
462              
463             sub _getNumCpus_from_sysctl_freebsd {
464 1 50   1   351 return 0 unless defined &xs_num_cpus_freebsd;
465 0   0     0 return xs_num_cpus_freebsd() || 0;
466             }
467              
468             sub _getNumCpus_from_dmesg_solaris {
469 1 50   1   28 return 0 if $^O !~ /solaris/i;
470 0 0       0 return 0 if !_configExternalProgram('dmesg');
471 0         0 my $cmd = _configExternalProgram('dmesg');
472 0         0 my @dmesg = qx($cmd 2> /dev/null);
473              
474             # a few clues that I see on my system (opensolaris 5.11 i86pc):
475             # ... blah blah is bound to cpu
476             # ^cpu: x86 blah blah
477 0         0 my $ncpus = 0;
478 0         0 foreach my $dmesg (@dmesg) {
479 0 0       0 if ($dmesg =~ /is bound to cpu (\d+)/) {
480 0         0 my $n = $1;
481 0 0       0 if ($ncpus <= $n) {
482 0         0 $ncpus = $n + 1;
483             }
484             }
485 0 0       0 if ($dmesg =~ /^cpu(\d+):/) {
486 0         0 my $n = $1;
487 0 0       0 if ($ncpus <= $n) {
488 0         0 $ncpus = $n + 1;
489             }
490             }
491             }
492              
493             # this doesn't always work
494             # (www.cpantesters.org/cpan/report/35d7685a-70b0-11e0-9552-4df9775ebe45)
495             # what else should we check for in @dmesg ?
496 0 0       0 if ($ncpus == 0) {
497             # ...
498             }
499              
500 0         0 return $ncpus;
501             }
502              
503             sub _getNumCpus_from_sysctl {
504             # sysctl works on a number of systems including MacOS
505 1 50   1   103 return 0 if !_configExternalProgram('sysctl');
506 1         15 my $cmd = _configExternalProgram('sysctl');
507 1         54418 my @sysctl = qx($cmd -a 2> /dev/null);
508 1         48 my @results = grep { /^hw.(?:avail|n)cpu\s*[:=]/ } @sysctl;
  1016         1367  
509 1         35 _debug("sysctl output:\n@results");
510 1 50       98 return 0 if @results == 0;
511 0         0 my ($ncpus) = $results[0] =~ /[:=]\s*(\d+)/;
512              
513 0 0       0 if ($ncpus == 0) {
514 0         0 my $result = qx($cmd -n hw.ncpu 2> /dev/null);
515 0         0 _debug("sysctl[2] result: $result");
516 0         0 $ncpus = 0 + $result;
517             }
518 0 0       0 if ($ncpus == 0) {
519 0         0 my $result = qx($cmd -n hw.ncpufound 2> /dev/null);
520 0         0 _debug("sysctl[3] result: $result");
521 0         0 $ncpus = 0 + $result;
522             }
523 0 0       0 if ($ncpus == 0) {
524 0         0 my $result = qx($cmd -n hw.availcpu 2> /dev/null);
525 0         0 _debug("sysctl[4] result: $result");
526 0         0 $ncpus = 0 + $result;
527             }
528              
529              
530 0   0     0 return $ncpus || 0;
531              
532             # there are also sysctl/sysctlbyname system calls
533             }
534              
535             sub _getNumCpus_from_psrinfo {
536 1 50   1   28 return 0 if !_configExternalProgram('psrinfo');
537 0         0 my $cmd = _configExternalProgram('psrinfo');
538 0         0 my @info = qx($cmd 2> /dev/null);
539             # return scalar grep /core/, qx($cmd -t 2>/dev/null);
540 0         0 return scalar @info;
541             }
542              
543             sub _getNumCpus_from_hinv { # NOT TESTED irix
544 1 50   1   29 return 0 if $^O =~ /irix/i;
545 1 50       4 return 0 if !_configExternalProgram('hinv');
546 0         0 my $cmd = _configExternalProgram('hinv');
547              
548             # test debug
549 0 0 0     0 if ($Sys::CpuAffinity::IS_TEST && !$Sys::CpuAffinity::HINV_CALLED++) {
550 0         0 print STDERR "$cmd output:\n";
551 0         0 print STDERR qx($cmd);
552 0         0 print STDERR "\n\n";
553 0         0 print STDERR "$cmd -c processor output:\n";
554 0         0 print STDERR qx($cmd -c processor);
555 0         0 print STDERR "\n\n";
556             }
557              
558             # found this in Test::Smoke::SysInfo v0.042 in Test-Smoke-1.43 module
559 0         0 my @processor = qx($cmd -c processor 2> /dev/null);
560 0         0 _debug('"hinv -c processor" output: ', @processor);
561 0         0 my ($cpu_cnt) = grep { /\d+.+processors?$/i } @processor;
  0         0  
562 0         0 my $ncpu = (split ' ', $cpu_cnt)[0];
563              
564 0 0       0 if ($ncpu == 0) {
565             # there might be output like:
566             # PU 30 at Module 001c35/Slot 0/Slice C: 400 Mhz MIPS R12000 Processor
567 0         0 $ncpu = grep { /^CPU / } @processor;
  0         0  
568             }
569              
570 0         0 return $ncpu;
571             }
572              
573             sub _getNumCpus_from_hwprefs {
574 1 50 33 1   116 return 0 if $^O !~ /darwin/i && $^O !~ /MacOS/i;
575 0 0       0 return 0 if !_configExternalProgram('hwprefs');
576 0         0 my $cmd = _configExternalProgram('hwprefs');
577 0         0 my $result = qx($cmd cpu_count 2> /dev/null);
578 0         0 $result =~ s/\s+$//;
579 0         0 _debug("\"$cmd cpu_count\" output: ", $result);
580 0   0     0 return $result || 0;
581             }
582              
583             sub _getNumCpus_from_system_profiler { # NOT TESTED darwin
584 1 50 33 1   199 return 0 if $^O !~ /darwin/ && $^O !~ /MacOS/i;
585 0 0       0 return 0 if !_configExternalProgram('system_profiler');
586              
587             # with help from Test::Smoke::SysInfo
588 0         0 my $cmd = _configExternalProgram('system_profiler');
589 0         0 my $system_profiler_output
590             = qx($cmd -detailLevel mini SPHardwardDataType 2> /dev/null);
591 0         0 my %system_profiler;
592 0         0 while ($system_profiler_output =~ m/^\s*([\w ]+):\s+(.+)$/gm) {
593 0         0 $system_profiler{uc $1} = $2;
594             }
595              
596 0         0 my $ncpus = $system_profiler{'NUMBER OF CPUS'};
597 0 0       0 if (!defined $ncpus) {
598 0         0 $ncpus = $system_profiler{'TOTAL NUMBER OF CORES'};
599             }
600 0         0 return $ncpus;
601             }
602              
603             sub _getNumCpus_from_prtconf {
604             # solaris has a prtconf command, but I don't think it outputs #cpus.
605 1 50   1   39 return 0 if $^O !~ /aix/i;
606 0 0       0 return 0 if !_configExternalProgram('prtconf');
607 0         0 my $cmd = _configExternalProgram('prtconf');
608              
609             # prtconf can take a long time to run, so cache the result
610 0         0 our $AIX_prtconf_cache;
611 0 0       0 if (!defined($AIX_prtconf_cache)) {
612 0         0 my @result = qx($cmd 2> /dev/null);
613 0         0 my ($result) = grep { /Number Of Processors:/ } @result;
  0         0  
614 0 0       0 return 0 if !$result;
615 0         0 my ($ncpus) = $result =~ /:\s+(\d+)/;
616 0   0     0 $AIX_prtconf_cache = $ncpus || 0;
617             }
618 0         0 return $AIX_prtconf_cache;
619             }
620              
621             sub _getNumCpus_from_Test_Smoke_SysInfo { # NOT TESTED
622 1 50   1   209 return 0 if !_configModule('Test::Smoke::SysInfo');
623 0         0 my $sysinfo = Test::Smoke::SysInfo->new();
624 0 0 0     0 if (defined $sysinfo && defined $sysinfo->{_ncpu}) {
625             # darwin: result might have format "1 [2 cores]", see
626             # www.cpantesters.org/cpan/report/db6067c4-9a66-11e0-91fb-39e97f60f2f7
627 0         0 $sysinfo->{_ncpu} =~ s/\d+ \[(\d+) cores\]/$1/;
628 0         0 return $sysinfo->{_ncpu};
629             }
630 0         0 return;
631             }
632              
633             sub _getNumCpus_from_taskset {
634 1 50   1   162 return 0 if $^O !~ /linux/i;
635 1         20 my $taskset = _configExternalProgram('taskset');
636 1 50       27 return 0 unless $taskset;
637              
638             # neither of these approaches are foolproof
639             # 1. read affinity mask of PID 1
640             # 2. try different affinity settings until it fails
641             #
642             # Will probably undercount if there are >64 cpus!
643              
644 1         4405 my $result = qx($taskset -p 1 2> /dev/null);
645 1         44 my ($mask) = $result =~ /:\s+(\w+)/;
646 1 50       30 if ($mask) {
647 1         26 my $n = 1+__hex($mask);
648 1         24 return int(0.5+log($n)/log(2));
649             }
650              
651 0         0 my $n = 0;
652 0   0     0 do {
653 0         0 my $cmd = sprintf '%s -p %x $$', $taskset, 1<<$n;
654 0         0 my $result = qx($cmd >/dev/null 2>/dev/null);
655 0         0 $n++;
656             } while ($?==0 && $n < 64);
657              
658 0 0       0 if ($n > 1) { # n==1 could be a false positive
659 0         0 return $n;
660             }
661              
662 0         0 $n = 0;
663 0         0 while ( do { qx($taskset -pc $n $$ >/dev/null 2>/dev/null); $?==0 } ) {
  0         0  
  0         0  
664 0         0 $n++;
665 0 0       0 last if $n >= 256;
666             }
667 0         0 return 0;
668             }
669              
670             ######################################################################
671              
672             # get affinity toolbox
673              
674             sub _getAffinity_with_Win32API {
675 7     7   218 my $opid = shift;
676 7 50 33     160 return 0 if $^O ne 'MSWin32' && $^O ne 'cygwin';
677 0 0       0 return 0 if !_configModule('Win32::API');
678              
679 0         0 my $pid = $opid;
680 0 0       0 if ($^O eq 'cygwin') {
681 0         0 $pid = __pid_to_winpid($opid);
682             # return 0 if !defined $pid;
683             }
684 0 0       0 return 0 if !$pid;
685              
686 0 0       0 if ($pid > 0) {
687 0         0 return _getProcessAffinity_with_Win32API($pid);
688             } else { # $pid is a Windows pseudo-process (thread ID)
689 0         0 return _getThreadAffinity_with_Win32API(-$pid);
690             }
691             }
692              
693             sub _getProcessAffinity_with_Win32API {
694 0     0   0 my $pid = shift;
695 0         0 my ($processMask, $systemMask, $processHandle) = (' ' x 16, ' ' x 16);
696              
697             # 0x0400 - PROCESS_QUERY_INFORMATION,
698             # 0x1000 - PROCESS_QUERY_LIMITED_INFORMATION
699 0   0     0 $processHandle = _win32api('OpenProcess',0x0400,0,$pid)
700             || _win32api('OpenProcess',0x1000,0,$pid);
701 0 0       0 return 0 if ! $processHandle;
702 0 0       0 return 0 if ! _win32api('GetProcessAffinityMask', $processHandle,
703             $processMask, $systemMask);
704              
705 0         0 my $mask = _unpack_Win32_mask($processMask);
706 0         0 _debug("affinity with Win32::API: $mask");
707 0         0 return $mask;
708             }
709              
710             sub _getThreadAffinity_with_Win32API {
711 0     0   0 my $thrid = shift;
712 0         0 my ($processMask, $systemMask, $threadHandle) = (' 'x16, ' 'x16);
713              
714             # 0x0020: THREAD_QUERY_INFORMATION
715             # 0x0400: THREAD_QUERY_LIMITED_INFORMATION
716             # 0x0040: THREAD_SET_INFORMATION
717             # 0x0200: THREAD_SET_LIMITED_INFORMATION
718 0   0     0 $threadHandle = _win32api('OpenThread', 0x0060, 0, $thrid)
719             || _win32api('OpenThread', 0x0600, 0, $thrid)
720             || _win32api('OpenThread', 0x0020, 0, $thrid)
721             || _win32api('OpenThread', 0x0400, 0, $thrid);
722 0 0       0 if (! $threadHandle) {
723 0         0 return 0;
724             }
725              
726             # The Win32 API does not have a GetThreadAffinityMask function.
727             # SetThreadAffinityMask will return the previous affinity,
728             # but then you have to call it again to restore the original affinity.
729             # Also, SetThreadAffinityMask won't work if you don't have permission
730             # to change the affinity.
731              
732             # SetThreadAffinityMask argument has to be compatible with
733             # process affinity, so get process affinity.
734              
735             # XXX - this function only works for threads that are contained
736             # by the current process, and that should cover most use
737             # cases of this module. But how would you get the process
738             # id of an arbitrary Win32 thread?
739 0         0 my $cpid = _win32api('GetCurrentProcessId');
740              
741 0   0     0 my $processHandle
742             = _win32api('OpenProcess', 0x0400, 0, $cpid)
743             || _win32api('OpenProcess', 0x1000, 0, $cpid);
744              
745 0         0 local ($!,$^E) = (0,0);
746 0         0 my $result = _win32api('GetProcessAffinityMask',
747             $processHandle, $processMask, $systemMask);
748              
749 0 0       0 if ($result == 0) {
750 0         0 carp 'Could not determine process affinity ',
751             "(required to get thread affinity)\n";
752 0         0 return 0;
753             }
754              
755 0         0 $processMask = _unpack_Win32_mask($processMask);
756 0 0       0 if ($processMask == 0) {
757 0         0 carp 'Process affinity apparently set to zero, ',
758             "will not be able to set/get compatible thread affinity\n";
759 0         0 return 0;
760             }
761              
762 0         0 my $previous_affinity = _win32api('SetThreadAffinityMask',
763             $threadHandle, $processMask);
764              
765 0 0       0 if ($previous_affinity == 0) {
766 0         0 Carp::cluck "Win32::API::SetThreadAffinityMask: $! / $^E\n";
767 0         0 return 0;
768             }
769              
770             # hope we can restore it.
771 0 0       0 if ($previous_affinity != $processMask) {
772 0         0 local $! = 0;
773 0         0 local $^E = 0;
774 0         0 my $new_affinity = _win32api('SetThreadAffinityMask',
775             $threadHandle, $previous_affinity);
776 0 0       0 if ($new_affinity == 0) {
777              
778             # http://msdn.microsoft.com/en-us/library/ms686247(v=vs.85).aspx:
779             #
780             # "If the thread affinity mask requests a processor that is not
781             # selected for the process affinity mask, the last error code
782             # is ERROR_INVALID_PARAMETER." ($! => 87)
783             #
784             # In MSWin32, the result of a fork() is a "pseudo-process",
785             # a Win32 thread that is still contained by its parent.
786             # So on MSWin32 a race condition exists where the parent
787             # process can choose an incompatible set of affinities
788             # during the execution of this function (basically, between
789             # the two calls to SetThreadAffinityMask , above).
790              
791 0         0 carp "Sys::CpuAffinity::_getThreadAffinity_with_Win32API:\n",
792             "set thread $thrid affinity to $processMask ",
793             "in order to retrieve\naffinity, but was unable to ",
794             "restore previous value:\nHandle=$threadHandle, ",
795             "Prev=$previous_affinity, Error=$! / $^E\n";
796             }
797             }
798 0         0 return $previous_affinity;
799             }
800              
801             sub _unpack_Win32_mask {
802             # The Win32 GetProcessAffinityMask function takes
803             # "PDWORD" arguments. We pass (arbitrary) integers for these
804             # arguments, but on return they are changed to 1-4 bytes
805             # representing a packed integer.
806              
807 0     0   0 my $packed = shift;
808 0         0 return unpack "L", substr($packed . "\0\0\0\0", 0, 4);
809             }
810              
811              
812              
813             sub _getAffinity_with_Win32Process {
814 7     7   133 my $pid = shift;
815              
816 7 50 33     188 return 0 if $^O ne 'MSWin32' && $^O ne 'cygwin';
817 0 0       0 return 0 if !_configModule('Win32::Process');
818 0 0       0 return 0 if $pid < 0; # pseudo-process / thread id
819              
820 0 0       0 if ($^O eq 'cygwin') {
821 0         0 $pid = __pid_to_winpid($pid);
822 0 0       0 return 0 if !defined $pid;
823             }
824              
825 0         0 my ($processMask, $systemMask, $result, $processHandle) = (' 'x16, ' 'x16);
826 0 0 0     0 if (! Win32::Process::Open($processHandle, $pid, 0)
827             || ref($processHandle) ne 'Win32::Process') {
828 0         0 return 0;
829             }
830 0 0       0 if (! $processHandle->GetProcessAffinityMask($processMask, $systemMask)) {
831 0         0 return 0;
832             }
833 0         0 _debug("affinity with Win32::Process: $processMask");
834 0         0 return $processMask;
835             }
836              
837             sub _getAffinity_with_taskset {
838 7     7   167 my $pid = shift;
839 7 50       28 return 0 if $^O ne 'linux';
840 7 50       53 return 0 if !_configExternalProgram('taskset');
841 7         45 my $taskset = _configExternalProgram('taskset');
842 7         41431 my $taskset_output = qx($taskset -p $pid 2> /dev/null);
843 7         185 $taskset_output =~ s/\s+$//;
844 7         387 _debug("taskset output: $taskset_output");
845 7 100       491 return 0 if ! $taskset_output;
846 1         11 my ($mask) = $taskset_output =~ /: (\S+)/;
847 1         15 _debug("affinity with taskset: $mask");
848 1         11 return __hex($mask);
849             }
850              
851             sub __hex {
852             # hex() method with better support for input > 0xffffffff
853 2     2   12 my $mask = shift;
854 2 50       20 if (length($mask) > 8) {
855 0         0 my $mask2 = substr($mask,-8);
856 0         0 my $mask1 = substr($mask,0,-8);
857 0         0 return hex($mask2) + (__hex($mask1) << 32);
858             } else {
859 2         28 return hex($mask);
860             }
861             }
862              
863             sub _getAffinity_with_xs_sched_getaffinity {
864 50     50   219 my $pid = shift;
865 50 50       175 return 0 if !defined &xs_sched_getaffinity_get_affinity;
866 50         139 my @mask;
867 50         3615 my $r = xs_sched_getaffinity_get_affinity($pid,\@mask,0);
868 50 100       228 if ($r) {
869 44         186 return _arrayToMask(@mask);
870             }
871 6         96 return;
872             }
873              
874             sub _getAffinity_with_xs_DEBUG_sched_getaffinity {
875             # to debug errors in xs_sched_getaffinity_get_affinity
876             # during t/11-exercise-all.t
877 0     0   0 my $pid = shift;
878 0 0       0 return 0 if !defined &xs_sched_getaffinity_get_affinity;
879 0         0 my @mask;
880 0         0 my $r = xs_sched_getaffinity_get_affinity($pid,\@mask,1);
881 0 0       0 if ($r) {
882 0         0 return _arrayToMask(@mask);
883             }
884 0         0 return;
885             }
886              
887             sub _getAffinity_with_pbind {
888 7     7   226 my ($pid) = @_;
889 7 50       138 return 0 if $^O !~ /solaris/i;
890 0 0       0 return 0 if !_configExternalProgram('pbind');
891 0         0 my $pbind = _configExternalProgram('pbind');
892 0         0 my $cmd = "$pbind -q $pid";
893 0         0 my $pbind_output = qx($cmd 2> /dev/null);
894 0 0 0     0 if ($pbind_output eq '' && $? == 0) {
895              
896             # pid is unbound or pid is invalid?
897 0 0       0 if (kill 'ZERO', $pid) {
898 0         0 $pbind_output = 'not bound';
899             } else {
900 0         0 warn "_getAffinity_with_pbind: could not signal unbound pid $pid";
901 0         0 return;
902             }
903             }
904              
905             # possible output:
906             # process id $pid: $index
907             # process id $pid: not bound
908             # pid \d+ \w+ bound to proccessor(s) \d+ \d+ \d+.
909              
910 0 0       0 if ($pbind_output =~ /not bound/) {
    0          
    0          
911 0         0 my $np = getNumCpus();
912 0 0       0 if ($np > 0) {
913 0         0 return (TWO ** $np) - 1;
914             } else {
915 0         0 carp '_getAffinity_with_pbind: ',
916             "process $pid unbound but can't count processors\n";
917 0         0 return TWO**32 - 1;
918             }
919             } elsif ($pbind_output =~ /: (\d+)/) {
920 0         0 my $bound_processor = $1;
921 0         0 return TWO ** $bound_processor;
922             } elsif ($pbind_output =~ / bound to proces\S+\s+(.+)\.$/) {
923 0         0 my $cpus = $1;
924 0 0       0 if (!defined($cpus)) {
925 0         0 return 0;
926             }
927 0         0 my @cpus = split /\s+/, $1;
928 0         0 return _arrayToMask(@cpus);
929             }
930 0         0 return 0;
931             }
932              
933             sub _getAffinity_with_psaix {
934 7     7   183 my ($pid) = @_;
935 7 50       149 return 0 if $^O !~ /aix/i;
936 0         0 my $pscmd = _configExternalProgram('ps');
937 0 0       0 return 0 if !$pscmd;
938 0         0 our $AIX_HINTS;
939 0 0       0 __set_aix_hints() unless $AIX_HINTS;
940              
941 0         0 my ($header,$data) = qx(ps -o THREAD -p $pid 2>/dev/null);
942 0 0       0 return 0 unless $data;
943 0         0 $header =~ s/^\s+//;
944 0         0 my @h = split /\s+/, $header;
945 0         0 my @d = split /\s+/, $data;
946 0         0 my ($ipid) = grep { $h[$_] eq 'PID' } 0 .. $#h;
  0         0  
947 0         0 my ($ibnd) = grep { $h[$_] eq 'BND' } 0 .. $#h;
  0         0  
948 0 0 0     0 if ($ipid ne '' && $ibnd) {
949 0         0 my $pidd = $d[$ipid];
950 0         0 my $bndd = $d[$ibnd];
951 0 0       0 if ($pidd == $pid) {
952 0         0 $bndd =~ s/^\s+//;
953 0         0 $bndd =~ s/\s+$//;
954 0 0       0 if ($bndd eq '-') { # not bound
955 0         0 return (TWO ** getNumCpus()) - 1;
956             }
957 0 0       0 if ($AIX_HINTS) {
958 0   0     0 $bndd = $AIX_HINTS->{PROC_MAP}{$bndd} || $bndd;
959             }
960 0         0 return TWO ** $bndd;
961             }
962             }
963 0         0 warn "ps\\aix: could not parse result:\n$header$data\n";
964 0         0 return 0;
965             }
966              
967             sub _getAffinity_with_xs_processor_affinity {
968 7     7   170 my ($pid) = @_;
969 7 50       115 return 0 if !defined &xs_getaffinity_processor_affinity;
970 0         0 my @mask = ();
971 0         0 my $ret = xs_getaffinity_processor_affinity($pid,\@mask);
972 0 0       0 if ($ret == 0) {
973 0         0 return 0;
974             }
975 0         0 _debug("affinity with getaffinity_xs_processor_affinity: @mask");
976 0         0 return _arrayToMask(@mask);
977             }
978              
979             sub _getAffinity_with_xs_processor_bind {
980 7     7   151 my ($pid) = @_;
981 7 50       56 return 0 if !defined &xs_getaffinity_processor_bind;
982 0 0       0 return 0 if $^O !~ /solaris/i;
983 0 0       0 return 0 if _is_solarisMultiCpuBinding();
984 0         0 my @mask = ();
985 0         0 my $ret = xs_getaffinity_processor_bind($pid,\@mask);
986 0 0       0 if ($ret == 0) {
987 0         0 return 0;
988             }
989 0         0 _debug("affinity with getaffinity_xs_processor_affinity: @mask");
990 0         0 return _arrayToMask(@mask);
991             }
992              
993             sub _getAffinity_with_BSD_Process_Affinity {
994 7     7   425 my ($pid) = @_;
995 7 50       222 return 0 if $^O !~ /bsd/i;
996 0 0       0 return 0 if !_configModule('BSD::Process::Affinity','0.04');
997              
998 0         0 my $mask;
999 0 0       0 if (! eval {
1000 0         0 my $affinity = BSD::Process::Affinity::get_process_mask($pid);
1001 0         0 $mask = $affinity->get;
1002 0         0 1 } ) {
1003             # $MODULE{'BSD::Process::Affinity'} = 0
1004 0         0 _debug("error in _setAffinity_with_BSD_Process_Affinity: $@");
1005 0         0 return 0;
1006             }
1007 0         0 return $mask;
1008             }
1009              
1010             sub _getAffinity_with_cpuset {
1011 7     7   198 my ($pid) = @_;
1012 7 50       174 return 0 if $^O !~ /bsd/i;
1013 0 0       0 return 0 if !_configExternalProgram('cpuset');
1014 0         0 my $cpuset = _configExternalProgram('cpuset');
1015 0         0 my $cmd = "$cpuset -g -p $pid";
1016 0         0 my $cpuset_output = qx($cmd 2> /dev/null);
1017              
1018             # output format:
1019             # pid nnnnn mask: i, j, k, ...
1020              
1021 0         0 $cpuset_output =~ s/.*:\s*//;
1022 0 0       0 if ($cpuset_output =~ /NaN/i) {
1023 0         0 return 0;
1024             }
1025 0         0 my @cpus = split /\s*,\s*/, $cpuset_output;
1026 0 0       0 if (@cpus > 0) {
1027 0         0 return _arrayToMask(@cpus);
1028             }
1029 0         0 return 0;
1030             }
1031              
1032             sub _getAffinity_with_xs_freebsd_getaffinity {
1033 7     7   233 my $pid = shift;
1034 7 50       60 return 0 if !defined &xs_getaffinity_freebsd;
1035 0         0 my @mask = ();
1036 0         0 my $ret = xs_getaffinity_freebsd($pid,\@mask,0);
1037 0 0       0 if ($ret == 0) {
1038 0         0 return 0;
1039             }
1040 0         0 return _arrayToMask(@mask);
1041             }
1042              
1043             sub _getAffinity_with_xs_freebsd_getaffinity_debug {
1044 0     0   0 my $pid = shift;
1045 0 0       0 if (!defined &xs_getaffinity_freebsd) {
1046 0 0       0 if ($^O =~ /bsd/) {
1047 0         0 warn "\$^O=$^O, xs_getaffinity_freebsd not defined";
1048             }
1049 0         0 return;
1050             }
1051 0         0 my @mask = ();
1052 0         0 my $ret = xs_getaffinity_freebsd($pid,\@mask,1);
1053 0         0 warn "return value from xs_getaffinity_freebsd: $ret";
1054 0 0       0 if ($ret == 0) {
1055 0         0 return 0;
1056             }
1057 0         0 return _arrayToMask(@mask);
1058             }
1059              
1060             sub _getAffinity_with_xs_win32 {
1061 7     7   894 my ($opid) = @_;
1062 7         16 my $pid = $opid;
1063 7 50       34 if ($^O =~ /cygwin/) {
1064 0         0 $pid = __pid_to_winpid($opid);
1065 0 0       0 return 0 if !defined $pid;
1066             }
1067              
1068 7 100       120 if ($pid < 0) {
    100          
    50          
1069 2 50       90 return 0 if !defined &xs_win32_getAffinity_thread;
1070 0         0 return xs_win32_getAffinity_thread(-$pid);
1071             } elsif ($opid == $$) {
1072 1 50       7 if (defined &xs_win32_getAffinity_proc) {
    50          
1073 0         0 return xs_win32_getAffinity_proc($pid);
1074             } elsif (defined &xs_win32_getAffinity_thread) {
1075 0         0 return xs_win32_getAffinity_thread(0);
1076             } else {
1077             }
1078 1         8 return 0;
1079             } elsif (defined &xs_win32_getAffinity_proc) {
1080 0         0 return xs_win32_getAffinity_proc($pid);
1081             }
1082 4         48 return 0;
1083             }
1084              
1085             sub _getAffinity_with_xs_pthread_self_getaffinity {
1086              
1087             # new in 1.00, may only work when run as root
1088              
1089 7     7   125 my ($pid) = @_;
1090 7 50       84 return 0 if $^O !~ /bsd/;
1091              
1092             # this function can only be used on the calling process.
1093 0 0       0 return 0 if $pid != $$;
1094 0 0       0 return 0 if !defined &xs_pthread_self_getaffinity;
1095 0         0 my $z = xs_pthread_self_getaffinity(0);
1096 0 0       0 if ($z == 0) {
1097              
1098             # does $z==0 mean that the current thread is not bound (i.e.,
1099             # bound to all processors)? Or does it mean that the
1100             # pthread_getaffinity_np() call didn't do anything (but still
1101             # returned 0/success?)
1102             # Does pthread_getaffinity_np() always return 0 for normal users
1103             # and return non-zero for the super-user?
1104              
1105             # must use $_NUM_CPUS_CACHED || ... to pass test t/12#2
1106 0   0     0 my $np = $_NUM_CPUS_CACHED || getNumCpus();
1107 0         0 my $maxmask = TWO ** $np - 1;
1108              
1109 0         0 my $y = _setAffinity_with_xs_pthread_self_setaffinity($pid, $maxmask);
1110 0 0       0 if ($y) {
1111 0         0 return $maxmask;
1112             } else {
1113 0         0 return 0;
1114             }
1115             }
1116 0         0 return $z;
1117             }
1118              
1119             sub _getAffinity_with_xs_irix_sysmp {
1120              
1121             # new in 1.00, not tested
1122              
1123 7     7   131 my ($pid) = @_;
1124 7 50       123 return 0 if $^O !~ /irix/i;
1125 0 0       0 return 0 if !defined &xs_irix_sysmp_getaffinity;
1126 0         0 my $result = xs_irix_sysmp_getaffinity($pid);
1127 0 0       0 if ($result < -1) { # error
    0          
1128 0         0 return 0;
1129             } elsif ($result == -1) { # unrestricted
1130 0         0 my $np = getNumCpus();
1131 0         0 return TWO ** $np - 1;
1132             } else { # restricted to a single processor.
1133 0         0 return TWO ** $result;
1134             }
1135             }
1136              
1137             ######################################################################
1138              
1139             # set affinity toolbox
1140              
1141             sub _setAffinity_with_Win32API {
1142 16     16   318 my ($pid, $mask) = @_;
1143 16 50 33     249 return 0 if $^O ne 'MSWin32' && $^O ne 'cygwin';
1144 0 0       0 return 0 if !_configModule('Win32::API');
1145              
1146             # if $^O is 'cygwin', make sure you are passing the Windows pid,
1147             # using Cygwin::pid_to_winpid if necessary!
1148              
1149 0 0       0 if ($^O eq 'cygwin') {
1150 0         0 $pid = __pid_to_winpid($pid);
1151 0 0       0 if ($DEBUG) {
1152 0         0 print STDERR "winpid is $pid ($_[0])\n";
1153             }
1154 0 0       0 return 0 if !defined $pid;
1155             }
1156              
1157 0 0       0 if ($pid > 0) {
1158 0         0 my $processHandle;
1159             # 0x0200 - PROCESS_SET_INFORMATION
1160 0         0 $processHandle = _win32api('OpenProcess', 0x0200,0,$pid);
1161 0 0       0 if ($DEBUG) {
1162 0         0 print STDERR "process handle: $processHandle\n";
1163             }
1164 0 0       0 return 0 if ! $processHandle;
1165 0         0 my $result = _win32api('SetProcessAffinityMask', $processHandle, $mask);
1166 0         0 _debug("set affinity with Win32::API: $result");
1167 0         0 return $result;
1168             } else {
1169             # negative pid indicates Windows "pseudo-process", which should
1170             # use the Thread functions.
1171             # Thread access rights definitions:
1172             # 0x0020: THREAD_QUERY_INFORMATION
1173             # 0x0400: THREAD_QUERY_LIMITED_INFORMATION
1174             # 0x0040: THREAD_SET_INFORMATION
1175             # 0x0200: THREAD_SET_LIMITED_INFORMATION
1176 0         0 my $threadHandle;
1177 0         0 local $! = undef;
1178 0         0 local $^E = 0;
1179 0   0     0 $threadHandle = _win32api('OpenThread', 0x0060, 0, -$pid)
1180             || _win32api('OpenThread', 0x0600, 0, -$pid)
1181             || _win32api('OpenThread', 0x0040, 0, -$pid)
1182             || _win32api('OpenThread', 0x0200, 0, -$pid);
1183 0 0       0 return 0 if ! $threadHandle;
1184 0         0 my $previous_affinity = _win32api('SetThreadAffinityMask',
1185             $threadHandle, $mask);
1186 0 0       0 if ($previous_affinity == 0) {
1187 0         0 carp 'Sys::CpuAffinity::_setAffinity_with_Win32API: ',
1188             "SetThreadAffinityMask call failed: $! / $^E\n";
1189             }
1190 0         0 return $previous_affinity;
1191             }
1192             }
1193              
1194             sub _setAffinity_with_Win32Process {
1195 16     16   265 my ($pid, $mask) = @_;
1196 16 50       161 return 0 if $^O ne 'MSWin32'; # cygwin? can't get it to work reliably
1197 0 0       0 return 0 if !_configModule('Win32::Process');
1198              
1199 0 0       0 if ($^O eq 'cygwin') {
1200 0         0 $pid = __pid_to_winpid($pid);
1201              
1202 0 0       0 if ($DEBUG) {
1203 0         0 print STDERR "cygwin pid $_[0] => winpid $pid\n";
1204             }
1205 0 0       0 return 0 if !defined $pid;
1206             }
1207              
1208 0         0 my $processHandle;
1209 0 0 0     0 if (! Win32::Process::Open($processHandle, $pid, 0)
1210             || ref($processHandle) ne 'Win32::Process') {
1211 0         0 return 0;
1212             }
1213              
1214             # Seg fault on Cygwin? We really prefer not to use it on Cygwin.
1215 0         0 local $SIG{SEGV} = 'IGNORE';
1216              
1217             # SetProcessAffinityMask: "only available on Windows NT"
1218 6     6   74 use Config;
  6         14  
  6         20098  
1219 0         0 my $v = $Config{osvers};
1220 0 0 0     0 if ($^O eq 'MSWin32' && ($v lt '3.51' || $v ge '6.0')) {
      0        
1221 0 0       0 if ($DEBUG) {
1222 0         0 print STDERR 'SetProcessAffinityMask ',
1223             "not available on MSWin32 osvers $v?\n";
1224             }
1225 0         0 return 0;
1226             }
1227             # Don't trust Strawberry Perl $Config{osvers}. Win32::GetOSVersion
1228             # is more reliable if it is available.
1229 0 0       0 if (_configModule('Win32')) {
1230 0 0       0 if (!Win32::IsWinNT()) {
1231 0 0       0 if ($DEBUG) {
1232 0         0 print STDERR 'SetProcessorAffinityMask ',
1233             "not available on MSWin32 OS Version $v\n";
1234             }
1235 0         0 return 0;
1236             }
1237             }
1238              
1239 0         0 my $result = $processHandle->SetProcessAffinityMask($mask);
1240 0         0 _debug("set affinity with Win32::Process: $result");
1241 0         0 return $result;
1242             }
1243              
1244             sub _setAffinity_with_taskset {
1245 5     5   270 my ($pid, $mask) = @_;
1246 5 50 33     189 return 0 if $^O ne 'linux' || !_configExternalProgram('taskset');
1247 5         17 my $cmd = sprintf '%s -p %x %d 2>&1',
1248             _configExternalProgram('taskset'), $mask, $pid;
1249              
1250 5         25473 my $taskset_output = qx($cmd 2> /dev/null);
1251 5         277 my $taskset_status = $?;
1252              
1253 5 100       107 if ($taskset_status) {
1254 4         136 _debug("taskset output: $taskset_output");
1255             }
1256              
1257 5         184 return $taskset_status == 0;
1258             }
1259              
1260             sub _setAffinity_with_xs_sched_setaffinity {
1261 16     16   257 my ($pid,$mask,$debug) = @_;
1262 16 50       61 return 0 if !defined &xs_sched_setaffinity_set_affinity;
1263 16         67 my @mask = _maskToArray($mask);
1264 16         1224 return xs_sched_setaffinity_set_affinity($pid,\@mask,0+!!$debug);
1265             }
1266              
1267             sub _setAffinity_with_BSD_Process_Affinity {
1268 5     5   254 my ($pid,$mask) = @_;
1269 5 50       196 return 0 if $^O !~ /bsd/i;
1270 0 0       0 return 0 if !_configModule('BSD::Process::Affinity','0.04');
1271              
1272 0 0       0 if (not eval {
1273 0         0 my $affinity = BSD::Process::Affinity::get_process_mask($pid);
1274 0         0 $affinity->set($mask)->update;
1275 0         0 1}) {
1276 0         0 _debug("error in _setAffinity_with_BSD_Process_Affinity: $@");
1277 0         0 return 0;
1278             }
1279             }
1280              
1281             sub _getNumCpus_from_BSD_Process_Affinity {
1282 1 50   1   42 return 0 if $^O !~ /bsd/i;
1283 0 0       0 return 0 if !_configModule('BSD::Process::Affinity','0.04');
1284 0         0 my $n = BSD::Process::Affinity::current_set()->get;
1285 0         0 $n = log( $n+1.01 ) / log(2);
1286 0         0 return int($n);
1287             }
1288              
1289             sub _setAffinity_with_bindprocessor {
1290 5     5   250 my ($pid,$mask) = @_;
1291 5 50       41 return 0 if $^O !~ /aix/i;
1292 0 0       0 return 0 if $pid < 0;
1293 0 0       0 return 0 if !_configExternalProgram('bindprocessor');
1294 0         0 my $cmd = _configExternalProgram('bindprocessor');
1295 0         0 our $AIX_HINTS;
1296 0 0       0 __set_aix_hints($cmd) unless $AIX_HINTS;
1297              
1298 0         0 my @mask = _maskToArray($mask);
1299 0         0 my @cores = map { $AIX_HINTS->{PROCESSORS}[$_] } @mask;
  0         0  
1300 0 0       0 if (@cores == $AIX_HINTS->{NUM_CORES}) {
    0          
1301 0         0 return system("'$cmd' -u $pid") == 0;
1302             } elsif (@cores > 1) {
1303 0         0 warn "_setAffinity_with_bindprocessor: will only set one core on aix";
1304             }
1305 0         0 return system("'$cmd' $pid $cores[0]") == 0;
1306             }
1307              
1308             sub _setAffinity_with_pbind {
1309 5     5   229 my ($pid,$mask) = @_;
1310 5 50       69 return 0 if $^O !~ /solaris/i;
1311 0 0       0 return 0 if !_configExternalProgram('pbind');
1312 0         0 my $pbind = _configExternalProgram('pbind');
1313 0         0 my @mask = _maskToArray($mask);
1314              
1315 0         0 my $cpus = join ",", @mask;
1316 0         0 my $np = getNumCpus();
1317 0         0 my $c1;
1318 0 0       0 if (@mask == $np) {
1319             # unbind
1320 0         0 $c1 = system("'$pbind' -u $pid > /dev/null 2>&1");
1321             } else {
1322             # second form doesn't work on i686-solaris?
1323 0   0     0 $c1 = system("'$pbind' -b $cpus $pid > /dev/null 2>&1")
1324             || system("'$pbind' -b -c $cpus -s $pid > /dev/null 2>&1");
1325             }
1326 0         0 return !$c1;
1327             }
1328              
1329             sub _setAffinity_with_xs_processor_affinity {
1330 5     5   219 my ($pid,$mask) = @_;
1331 5 50       75 return 0 if $^O !~ /solaris/i;
1332 0 0       0 return 0 if !defined &xs_setaffinity_processor_affinity;
1333 0         0 my @mask = _maskToArray($mask);
1334 0         0 my $ret = xs_setaffinity_processor_affinity($pid, \@mask);
1335 0 0       0 if ($ret == 0) {
1336 0         0 return 0;
1337             }
1338 0         0 return 1;
1339             }
1340              
1341             sub _setAffinity_with_xs_processor_bind {
1342 5     5   214 my ($pid,$mask) = @_;
1343 5 50       65 return 0 if $^O !~ /solaris/i;
1344 0 0       0 return 0 if !defined &xs_setaffinity_processor_bind;
1345 0 0       0 return 0 if _is_solarisMultiCpuBinding();
1346 0         0 my @mask = _maskToArray($mask);
1347 0         0 my $ret = xs_setaffinity_processor_bind($pid, \@mask);
1348 0 0       0 if ($ret == 0) {
1349 0         0 return 0;
1350             }
1351 0         0 return 1;
1352             }
1353              
1354             sub _setAffinity_with_cpuset {
1355 5     5   245 my ($pid, $mask) = @_;
1356 5 50       61 return 0 if $^O !~ /bsd/i;
1357 0 0       0 return 0 if !_configExternalProgram('cpuset');
1358              
1359 0         0 my $lmask = join ',' => _maskToArray($mask);
1360 0         0 my $cmd = _configExternalProgram('cpuset') . " -l $lmask -p $pid";
1361 0         0 my $c1 = system "$cmd 2> /dev/null";
1362 0         0 return !$c1;
1363             }
1364              
1365             sub _setAffinity_with_xs_freebsd_setaffinity {
1366 5     5   237 my ($pid,$mask) = @_;
1367 5 50       73 return 0 if !defined &xs_setaffinity_freebsd;
1368 0         0 my @mask = _maskToArray($mask);
1369 0         0 return xs_setaffinity_freebsd($pid,\@mask);
1370             }
1371              
1372             sub _setAffinity_with_xs_win32 {
1373 16     16   262 my ($opid, $mask) = @_;
1374              
1375 16         45 my $pid = $opid;
1376 16 50       56 if ($^O =~ /cygwin/) {
1377 0         0 $pid = __pid_to_winpid($opid);
1378 0 0       0 return 0 if !defined $pid;
1379             }
1380              
1381 16 100       158 if ($pid < 0) {
    100          
    50          
1382 2 50       38 if (defined &xs_win32_setAffinity_thread) {
1383 0         0 my $r = xs_win32_setAffinity_thread(-$pid,$mask);
1384 0         0 _debug("xs_win32_setAffinity_thread -$pid,$mask => $r");
1385 0 0       0 return $r if $r;
1386             }
1387 2         30 return 0;
1388             } elsif ($opid == $$) {
1389 12 50       44 if (defined &xs_win32_setAffinity_proc) {
1390 0         0 _debug('xs_win32_setAffinity_proc $$');
1391 0         0 return xs_win32_setAffinity_proc($pid,$mask);
1392             }
1393 12 50 33     43 if ($^O eq 'cygwin' && defined &xs_win32_setAffinity_thread) {
1394 0         0 my $r = xs_win32_setAffinity_thread(0, $mask);
1395 0 0       0 return $r if $r;
1396             }
1397 12         74 return 0;
1398             } elsif (defined &xs_win32_setAffinity_proc) {
1399 0         0 my $r = xs_win32_setAffinity_proc($pid, $mask);
1400 0         0 _debug("xs_win32_setAffinity_proc +$pid,$mask => $r");
1401 0         0 return $r;
1402             }
1403 2         36 return 0;
1404             }
1405              
1406             sub _setAffinity_with_xs_pthread_self_setaffinity {
1407              
1408             # new in 1.00, may only work when run as root
1409              
1410 5     5   236 my ($pid, $mask) = @_;
1411 5 50       105 return 0 if $^O !~ /bsd/i;
1412              
1413             # this function only works with the calling process
1414 0 0       0 return 0 if $$ != $pid;
1415 0 0       0 return 0 if !defined &xs_pthread_self_setaffinity;
1416 0         0 return &xs_pthread_self_setaffinity($mask);
1417             }
1418              
1419             sub _setAffinity_with_xs_irix_sysmp {
1420              
1421             # new in 1.00, not tested
1422              
1423 5     5   234 my ($pid, $mask) = @_;
1424              
1425 5 50       171 return 0 if $^O !~ /irix/i;
1426 0 0       0 return 0 if !defined &xs_irix_sysmp_setaffinity;
1427              
1428             # Like the pbind function in solaris, Irix's sysmp function can only
1429             # * bind a process to a single specific CPU, or
1430             # * bind a process to all CPUs
1431              
1432 0         0 my @mask = _maskToArray($mask);
1433              
1434 0         0 my $np = getNumCpus();
1435 0         0 my $c1;
1436 0 0 0     0 if ($np > 0 && $mask + 1 == TWO ** $np) {
1437 0         0 return xs_irix_sysmp_setaffinity($pid, -1);
1438             } else {
1439 0         0 my $element = 0;
1440 0         0 return xs_irix_sysmp_setaffinity($pid, $mask[$element]);
1441             }
1442             }
1443              
1444             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
1445              
1446             sub _maskToArray {
1447 16     16   45 my ($mask) = @_;
1448 16         38 my @mask = ();
1449 16         35 my $i = 0;
1450 16         52 while ($mask > 0) {
1451 219 100       68047 if ($mask & 1) {
1452 153         37662 push @mask, $i;
1453             }
1454 219         15919 $i++;
1455 219         574 $mask >>= 1;
1456             }
1457 16         4249 return @mask;
1458             }
1459              
1460             sub _arrayToMask {
1461 44     44   134 my @procs = @_;
1462 44         255 my $mask = Math::BigInt->new(0);
1463 44         5744 for my $proc (@procs) {
1464 441         148187 $mask |= TWO ** $proc;
1465             }
1466 44         17606 return $mask;
1467             }
1468              
1469             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
1470              
1471             sub __pid_to_winpid {
1472 0     0   0 my ($cygwinpid) = @_;
1473 0 0 0     0 if ($] >= 5.008 && defined &Cygwin::pid_to_winpid) {
1474 0         0 return Cygwin::pid_to_winpid($cygwinpid);
1475             } else {
1476 0         0 return __poor_mans_pid_to_winpid($cygwinpid);
1477             }
1478             }
1479              
1480             sub __poor_mans_pid_to_winpid {
1481 0     0   0 my ($cygwinpid) = @_;
1482 0         0 my @psw = qx(/usr/bin/ps -W 2> /dev/null);
1483 0         0 foreach my $psw (@psw) {
1484 0         0 $psw =~ s/^[A-Z\s]+//;
1485 0         0 my ($pid,$ppid,$pgid,$winpid) = split /\s+/, $psw;
1486 0 0       0 next if ! $pid;
1487 0 0       0 if ($pid == $cygwinpid) {
1488 0         0 return $winpid;
1489             }
1490             }
1491 0         0 warn "Could not resolve cygwin pid $cygwinpid into winpid.\n";
1492 0         0 return $cygwinpid;
1493             }
1494              
1495             ######################################################################
1496              
1497             # configuration code
1498              
1499             sub _debug {
1500 31     31   272 my @msg = @_;
1501 31 50       180 return if !$DEBUG;
1502 0         0 print STDERR 'Sys::CpuAffinity: ',@msg,"\n";
1503 0         0 return;
1504             }
1505              
1506             our %MODULE = ();
1507             our %PROGRAM = ();
1508             our %INLINE_CODE = ();
1509              
1510             sub _configModule {
1511 5     5   1245 my $module = shift;
1512 5   50     41 my $version = shift || "";
1513 5 50       19 return $MODULE{$module} if defined $MODULE{$module};
1514              
1515 5 100       396 if (eval "require $module") { ## no critic (StringyEval)
1516 1         72 my $v = eval "\$$module" . "::VERSION";
1517 1 50 33     11 if (!$@ && (!$version || $version <= $v)) {
      33        
1518 1         5 _debug("module $module is available.");
1519 1         5 return $MODULE{$module} = 1;
1520             } else {
1521 0         0 _debug("module $module $version not available ($v)");
1522 0         0 return $MODULE{$module} = 0;
1523             }
1524             } else {
1525 4         35 _debug("module $module $version not available: $@");
1526 4         37 return $MODULE{$module} = 0;
1527             }
1528             }
1529              
1530             our @PATH = ();
1531              
1532             sub _configExternalProgram {
1533 41     41   1346 my $program = shift;
1534 41 100       265 return $PROGRAM{$program} if defined $PROGRAM{$program};
1535 18 50       496 if (-x $program) {
1536 0         0 _debug("Program $program is available in $program");
1537 0         0 return $PROGRAM{$program} = $program;
1538             }
1539              
1540 18 50       286 if ($^O ne 'MSWin32') {
1541 18         106540 my $which = qx(which $program 2> /dev/null);
1542 18         744 $which =~ s/\s+$//;
1543              
1544 18 100 33     2127 if ($which =~ / not in / # negative output on irix
      33        
      66        
1545             || $which =~ /no \Q$program\E in / # negative output on solaris
1546             || $which =~ /Command not found/ # negative output on openbsd
1547             || ! -x $which # not executable, may be junk
1548             ) {
1549              
1550 11         58 $which = '';
1551             }
1552 18 100       180 if ($which) {
1553 7         256 _debug("Program $program is available in $which");
1554 7         273 return $PROGRAM{$program} = $which;
1555             }
1556             }
1557              
1558             # poor man's which
1559 11 100       113 if (@PATH == 0) {
1560 2         50 @PATH = split /:/, $ENV{PATH};
1561 2         12 push @PATH, split /;/, $ENV{PATH};
1562 2         22 push @PATH, '.';
1563 2         14 push @PATH, '/sbin', '/usr/sbin';
1564             }
1565 11         125 foreach my $dir (@PATH) {
1566 143 50       1810 if (-x "$dir/$program") {
1567 0         0 _debug("Program $program is available in $dir/$program");
1568 0         0 return $PROGRAM{$program} = "$dir/$program";
1569             }
1570             }
1571 11         556 return $PROGRAM{$program} = 0;
1572             }
1573              
1574             ######################################################################
1575              
1576             # some Win32::API specific code
1577              
1578             our %WIN32_API_SPECS
1579             = ('GetActiveProcessorCount' => [ 'kernel32',
1580             'DWORD GetActiveProcessorCount(WORD g)' ],
1581             'GetCurrentProcess' => [ 'kernel32',
1582             'HANDLE GetCurrentProcess()' ],
1583             'GetCurrentProcessId' => [ 'kernel32',
1584             'DWORD GetCurrentProcessId()' ],
1585             # 'GetCurrentThread' => [ 'kernel32',
1586             # 'HANDLE GetCurrentThread()' ],
1587             # 'GetCurrentThreadId' => [ 'kernel32',
1588             # 'int GetCurrentThreadId()' ],
1589             'GetLastError' => [ 'kernel32', 'DWORD GetLastError()' ],
1590             'GetModuleHandle' => [ 'kernel32',
1591             'HMODULE GetModuleHandle(LPCTSTR n)' ],
1592             # 'GetPriorityClass' => [ 'kernel32',
1593             # 'DWORD GetPriorityClass(HANDLE h)' ],
1594             'GetProcAddress' => [ 'kernel32',
1595             'DWORD GetProcAddress(HINSTANCE a,LPCTSTR b)' ],
1596             # 'DWORD GetProcAddress(HINSTANCE a,LPCWSTR b)' ],
1597             'GetProcessAffinityMask' => [ 'kernel32',
1598             'BOOL GetProcessAffinityMask(HANDLE h,PDWORD a,PDWORD b)' ],
1599             # 'GetThreadPriority' => [ 'kernel32','int GetThreadPriority(HANDLE h)' ],
1600             'IsWow64Process' => [ 'kernel32',
1601             'BOOL IsWow64Process(HANDLE h,PBOOL b)' ],
1602             'OpenProcess' => [ 'kernel32',
1603             'HANDLE OpenProcess(DWORD a,BOOL b,DWORD c)' ],
1604             'OpenThread' => [ 'kernel32',
1605             'HANDLE OpenThread(DWORD a,BOOL b,DWORD c)' ],
1606             'SetProcessAffinityMask' => [ 'kernel32',
1607             'BOOL SetProcessAffinityMask(HANDLE h,PDWORD m)' ],
1608             'SetThreadAffinityMask' => [ 'kernel32',
1609             'DWORD SetThreadAffinityMask(HANDLE h,DWORD d)' ],
1610             # 'SetThreadPriority' => [ 'kernel32',
1611             # 'BOOL SetThreadPriority(HANDLE h,int n)' ],
1612             # 'TerminateThread' => [ 'kernel32',
1613             # 'BOOL TerminateThread(HANDLE h,DWORD x)' ],
1614             );
1615             our %WIN32_API_SPECS_
1616             = map { $_ => $WIN32_API_SPECS{$_}[1] } keys %WIN32_API_SPECS;
1617              
1618             sub _win32api { ## no critic (RequireArgUnpacking)
1619             ## (we want spooky action-at-a-distance)
1620 0     0     my $function = shift;
1621 0 0         return if !_configModule('Win32::API');
1622 0 0         if (!defined $WIN32API{$function}) {
1623 0           __load_win32api_function($function);
1624             }
1625 0 0 0       return if !defined($WIN32API{$function}) || $WIN32API{$function} == 0;
1626              
1627 0           return $WIN32API{$function}->Call(@_);
1628             }
1629              
1630             sub __load_win32api_function {
1631 0     0     my $function = shift;
1632 0           my $spec = $WIN32_API_SPECS{$function};
1633 0 0         if (!defined $spec) {
1634 0           croak "Sys::CpuAffinity: bad Win32::API function request: $function\n";
1635             }
1636              
1637 0           local ($!, $^E) = (0, 0);
1638              
1639 0           my $spec_ = $WIN32_API_SPECS_{$function};
1640 0           $WIN32API{$function} = Win32::API->new('kernel32',$spec_);
1641              
1642 0 0         if ($!) {
1643 0           carp 'Sys::CpuAffinity: ',
1644             "error initializing Win32::API function $function: $! / $^E\n";
1645 0           $WIN32API{$function} = 0;
1646             }
1647 0           return;
1648             }
1649              
1650             ######################################################################
1651              
1652             1; # End of Sys::CpuAffinity
1653              
1654             __END__