1

I need to come up with the P/Invoke signature for the Linux sched_setaffinity function, to be called from C# code running on Mono.

int sched_setaffinity(pid_t pid, size_t cpusetsize,
                      cpu_set_t *mask);

The ThreadAffinity class in the "open-hardware-monitor" project defines it as:

[DllImport(LIBC)]
public static extern int sched_setaffinity(int pid, IntPtr maskSize,
                                           ref ulong mask);  

However, the Mono.LibRt project uses a different signature:

[DllImport (LIBC, CallingConvention=CallingConvention.Cdecl,
                  SetLastError=true, EntryPoint="sched_setaffinity")]
protected static extern int sched_setaffinity (long pid, int num_bytes, 
                                               byte[] affinity);

Are both signatures correct, or is one erroneous? Are they portable across 32- and 64-bit architectures?

I assume that the first argument should be int, not long, since pid_t is a signed 32-bit integer on both 32- and 64-bit platforms (per Joe Shaw). Similarly, the second should be UIntPtr or IntPtr per Patrick McDonald. However, I don't know how to handle the third parameter.

Edit: Building on Simon Mourier's comment: I gather, from the cpu_set_t * type, that the third parameter should be a pointer. Does ref ulong qualify? I'm tempted to use that type because it makes the macros easier to implement; for example, a mask for a single processor id can be computed as:

ulong mask = 1UL << id;

I've found a definition for cpu_set_t in this sched.h, where it's implemented as an array of unsigned long int.

Community
  • 1
  • 1
Douglas
  • 53,759
  • 13
  • 140
  • 188
  • I agree on `int pid` and `IntPtr maskSize`. For the cpu_set_t I suppose it defaults to C `int` since it's not defined explicitly in sched.h. What's important is it has to be a pointer, and this pointer must be manipulated by the internal CPU_* macros (CPU_ZERO, etc.). So you can use byte[] or int[], but you need to expose the CPU_* macros to the .NET world as well. – Simon Mourier Jun 15 '13 at 07:12

2 Answers2

2

As you have found, cpu_set_t is simply an array of unsigned long int. So I would declare the function like this:

[DllImport("libc.so.6", SetLastError=true)]
private static extern int sched_setaffinity(
    int pid, 
    IntPtr cpusetsize, 
    ulong[] cpuset
);
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Thanks David! I assume `cpusetsize` should be the size of the entire array (e.g. `4 * sizeof(ulong)` if it contains four elements)? – Douglas Jan 18 '14 at 16:41
  • (It'll take me a couple of weeks to test the answer; I don't currently have access to the Linux machine.) – Douglas Jan 18 '14 at 16:43
0

Per my other answer, I went with the former option (which worked fine on Linux).

[DllImport("libc.so.6", SetLastError=true)]
private static extern int sched_setaffinity(int pid, IntPtr cpusetsize, 
                                            ref ulong cpuset);
Community
  • 1
  • 1
Douglas
  • 53,759
  • 13
  • 140
  • 188
  • The problem is that `cpu_set_t` could be larger than a `ulong`. – David Heffernan Jan 18 '14 at 16:18
  • @DavidHeffernan: That would require a machine larger than a 64-core, no? – Douglas Jan 18 '14 at 16:20
  • @DavidHeffernan: Granted, but most programmers are not likely to encounter them in the next few years. Even the .NET Framework's [`ProcessorAffinity`](http://msdn.microsoft.com/en-us/library/system.diagnostics.processthread.processoraffinity(v=vs.110).aspx) would break on such machines. – Douglas Jan 18 '14 at 16:35