public static unsafe SocketError Select(IList checkRead, IList checkWrite, IList checkError, int microseconds)
{
int checkReadInitialCount = checkRead != null ? checkRead.Count : 0;
int checkWriteInitialCount = checkWrite != null ? checkWrite.Count : 0;
int checkErrorInitialCount = checkError != null ? checkError.Count : 0;
int count = checked(checkReadInitialCount + checkWriteInitialCount + checkErrorInitialCount);
Debug.Assert(count > 0, $"Expected at least one entry.");
// Rather than using the select syscall, we use poll. While this has a mismatch in API from Select and
// requires some translation, it avoids the significant limitation of select only working with file descriptors
// less than FD_SETSIZE, and thus failing arbitrarily depending on the file descriptor value assigned
// by the system. Since poll then expects an array of entries, we try to allocate the array on the stack,
// only falling back to allocating it on the heap if it's deemed too big.
const int StackThreshold = 80; // arbitrary limit to avoid too much space on stack
if (count < StackThreshold)
{
Interop.Sys.PollEvent* eventsOnStack = stackalloc Interop.Sys.PollEvent[count];
return SelectViaPoll(
checkRead, checkReadInitialCount,
checkWrite, checkWriteInitialCount,
checkError, checkErrorInitialCount,
eventsOnStack, count, microseconds);
}
else
{
var eventsOnHeap = new Interop.Sys.PollEvent[count];
fixed (Interop.Sys.PollEvent* eventsOnHeapPtr = eventsOnHeap)
{
return SelectViaPoll(
checkRead, checkReadInitialCount,
checkWrite, checkWriteInitialCount,
checkError, checkErrorInitialCount,
eventsOnHeapPtr, count, microseconds);
}
}
}