(* Set the processor affinity. The parameter is normally a +ve integer
representing a bitmap with CPU 0 as the LSB, but if -ve it will instead fill
in bits from the highest numbered available (i.e. -1 is the highest available,
-2 is the second-highest available, -3 is the two highest available and so on).
The number of bits set is returned, or -1 on error.
See discussion at https://lists.freepascal.org/fpc-pascal/2011-January/028026.html
*)
function setProcessorAffinity(affinity: Int64): integer;
const
CPU_SETSIZE_BITS= 1024;
CPU_SETSIZE_QWORDS= CPU_SETSIZE_BITS DIV 64;
var
cpu_set: array[0..CPU_SETSIZE_QWORDS - 1] of qword;
r, i: integer;
bit, unBit: qword;
function bitsSet(q: qword): integer;
var
i: integer;
begin
result := 0;
for i := 0 to 63 do begin
if Odd(q) then result += 1;
q := q >> 1
end
end { bitsSet } ;
begin
FillByte(cpu_set, SizeOf(cpu_set), 0);
{$push }{$R- }{ Needed for i386 but not x86_64 }
r := do_Syscall(syscall_nr_sched_getaffinity, fpgetpid(), SizeOf(cpu_set), ptruint(@cpu_set));
{$pop }
Assert(r >= 0, 'sched_getaffinity() -> error ' + IntToStr(fpGetErrNo) + ', "' + StrError(fpGetErrNo) + '"');
(* The behaviour of the syscalls and C library routines differ, this describes *)
(* the former. A +ve return value from sched_getaffinity() indicates the number *)
(* of bytes set to a known state in the cpu_set bitmap, which will be at least *)
(* as many as are required to enumerate the available CPUs (i.e. cores and/or *)
(* threads as determined by the CPU design). *)
(* *)
(* This implementation is adequate for no more than 64 CPUs, since it only *)
(* looks at a single qword. *)
case Sign(r) of
-1: exit(-1); (* Syscall error *)
0: exit(1) (* No error, but no CPU count *)
otherwise
case Sign(affinity) of
-1: begin
FillByte(cpu_set, SizeOf(cpu_set), 0);
(* Get the bitmap representing the entire population of available CPUs, up to a *)
(* maximum of 64. *)
cpu_set[0] := High(qword);
{$push }{$R- }{ Possibly needed for i386 but not x86_64 }
r := do_Syscall(syscall_nr_sched_setaffinity, fpgetpid(), SizeOf(cpu_set), ptruint(@cpu_set));
{$pop }
Assert(r = 0, 'sched_setaffinity() -> error ' + IntToStr(fpGetErrNo) + ', "' + StrError(fpGetErrNo) + '"');
if r <> 0 then
exit(-1); (* Syscall error *)
{$push }{$R- }{ Possibly needed for i386 but not x86_64 }
r := do_Syscall(syscall_nr_sched_getaffinity, fpgetpid(), SizeOf(cpu_set), ptruint(@cpu_set));
{$pop }
Assert(r >= 0, 'sched_getaffinity() -> error ' + IntToStr(fpGetErrNo) + ', "' + StrError(fpGetErrNo) + '"');
if r < 0 then
exit(-1); (* Syscall error *)
(* Working from the top down, find the highest available CPU. Don't assume that *)
(* shift distances > 31 are reliable. *)
i := 63;
bit := qword($8000000000000000);
unBit := qword($7fffffffffffffff);
while ((cpu_set[0] and bit) = 0) and (i >= 0) do begin
bit := bit div 2;
unBit := (unBit div 2) + qword($8000000000000000);
i -= 1
end;
(* Mark the CPUs we want to use. This doesn't handle CPUs which aren't already *)
(* marked as active specially, since this is how the +ve case (below) works. *)
affinity := Abs(affinity);
while i >= 0 do begin
if Odd(affinity) then
cpu_set[0] := cpu_set[0] or bit
else
cpu_set[0] := cpu_set[0] and unBit;
affinity := affinity >> 1;
bit := bit div 2;
unBit := (unBit div 2) + qword($8000000000000000);
i -= 1
end
end;
0: exit(bitsSet(cpu_set[0])) (* No change requested *)
otherwise
FillByte(cpu_set, SizeOf(cpu_set), 0);
cpu_set[0] := affinity
end;
(* The pattern of bits in the set indicates the CPUs we want to use, starting *)
(* either at zero or at the highest available CPU depending on the sign of the *)
(* affinity parameter. *)
{$push }{$R- }{ Possibly needed for i386 but not x86_64 }
r := do_Syscall(syscall_nr_sched_setaffinity, fpgetpid(), SizeOf(cpu_set), ptruint(@cpu_set));
{$pop }
Assert(r = 0, 'sched_setaffinity() -> error ' + IntToStr(fpGetErrNo) + ', "' + StrError(fpGetErrNo) + '"');
FillByte(cpu_set, SizeOf(cpu_set), 0);
{$push }{$R- }{ Possibly needed for i386 but not x86_64 }
r := do_Syscall(syscall_nr_sched_getaffinity, fpgetpid(), SizeOf(cpu_set), ptruint(@cpu_set));
{$pop }
case Sign(r) of
-1: result := -1; (* Syscall error *)
0: result := 1 (* No error, but no CPU count *)
otherwise
result := bitsSet(cpu_set[0])
end
end
end { setProcessorAffinity } ;