// Poll(u32 nfds, u32 timeout, buffer<unknown, 0x21, 0> fds) -> (i32 ret, u32 bsd_errno, buffer<unknown, 0x22, 0>) public ResultCode Poll(ServiceCtx context) { int fdsCount = context.RequestData.ReadInt32(); int timeout = context.RequestData.ReadInt32(); (ulong bufferPosition, ulong bufferSize) = context.Request.GetBufferType0x21(); if (timeout < -1 || fdsCount < 0 || (ulong)(fdsCount * 8) > bufferSize) { return(WriteBsdResult(context, -1, LinuxError.EINVAL)); } PollEvent[] events = new PollEvent[fdsCount]; for (int i = 0; i < fdsCount; i++) { PollEventData pollEventData = context.Memory.Read <PollEventData>(bufferPosition + (ulong)(i * Unsafe.SizeOf <PollEventData>())); IFileDescriptor fileDescriptor = _context.RetrieveFileDescriptor(pollEventData.SocketFd); if (fileDescriptor == null) { return(WriteBsdResult(context, -1, LinuxError.EBADF)); } events[i] = new PollEvent(pollEventData, fileDescriptor); } List <PollEvent> discoveredEvents = new List <PollEvent>(); List <PollEvent>[] eventsByPollManager = new List <PollEvent> [_pollManagers.Count]; for (int i = 0; i < eventsByPollManager.Length; i++) { eventsByPollManager[i] = new List <PollEvent>(); foreach (PollEvent evnt in events) { if (_pollManagers[i].IsCompatible(evnt)) { eventsByPollManager[i].Add(evnt); discoveredEvents.Add(evnt); } } } foreach (PollEvent evnt in events) { if (!discoveredEvents.Contains(evnt)) { Logger.Error?.Print(LogClass.ServiceBsd, $"Poll operation is not supported for {evnt.FileDescriptor.GetType().Name}!"); return(WriteBsdResult(context, -1, LinuxError.EBADF)); } } int updateCount = 0; LinuxError errno = LinuxError.SUCCESS; if (fdsCount != 0) { bool IsUnexpectedLinuxError(LinuxError error) { return(errno != LinuxError.SUCCESS && errno != LinuxError.ETIMEDOUT); } // Hybrid approach long budgetLeftMilliseconds; if (timeout == -1) { budgetLeftMilliseconds = PerformanceCounter.ElapsedMilliseconds + uint.MaxValue; } else { budgetLeftMilliseconds = PerformanceCounter.ElapsedMilliseconds + timeout; } do { for (int i = 0; i < eventsByPollManager.Length; i++) { if (eventsByPollManager[i].Count == 0) { continue; } errno = _pollManagers[i].Poll(eventsByPollManager[i], 0, out updateCount); if (IsUnexpectedLinuxError(errno)) { break; } if (updateCount > 0) { break; } } // If we are here, that mean nothing was availaible, sleep for 50ms context.Device.System.KernelContext.Syscall.SleepThread(50 * 1000000); }while (PerformanceCounter.ElapsedMilliseconds < budgetLeftMilliseconds); } else if (timeout == -1) { // FIXME: If we get a timeout of -1 and there is no fds to wait on, this should kill the KProces. (need to check that with re) throw new InvalidOperationException(); } else { context.Device.System.KernelContext.Syscall.SleepThread(timeout); } // TODO: Spanify for (int i = 0; i < fdsCount; i++) { context.Memory.Write(bufferPosition + (ulong)(i * Unsafe.SizeOf <PollEventData>()), events[i].Data); } return(WriteBsdResult(context, updateCount, errno)); }
// Poll(u32 nfds, u32 timeout, buffer<unknown, 0x21, 0> fds) -> (i32 ret, u32 bsd_errno, buffer<unknown, 0x22, 0>) public ResultCode Poll(ServiceCtx context) { int fdsCount = context.RequestData.ReadInt32(); int timeout = context.RequestData.ReadInt32(); (long bufferPosition, long bufferSize) = context.Request.GetBufferType0x21(); if (timeout < -1 || fdsCount < 0 || (fdsCount * 8) > bufferSize) { return(WriteBsdResult(context, -1, LinuxError.EINVAL)); } PollEvent[] events = new PollEvent[fdsCount]; for (int i = 0; i < fdsCount; i++) { int socketFd = context.Memory.ReadInt32(bufferPosition + i * 8); BsdSocket socket = RetrieveSocket(socketFd); if (socket == null) { return(WriteBsdResult(context, -1, LinuxError.EBADF)); } PollEvent.EventTypeMask inputEvents = (PollEvent.EventTypeMask)context.Memory.ReadInt16(bufferPosition + i * 8 + 4); PollEvent.EventTypeMask outputEvents = (PollEvent.EventTypeMask)context.Memory.ReadInt16(bufferPosition + i * 8 + 6); events[i] = new PollEvent(socketFd, socket, inputEvents, outputEvents); } List <Socket> readEvents = new List <Socket>(); List <Socket> writeEvents = new List <Socket>(); List <Socket> errorEvents = new List <Socket>(); foreach (PollEvent Event in events) { bool isValidEvent = false; if ((Event.InputEvents & PollEvent.EventTypeMask.Input) != 0) { readEvents.Add(Event.Socket.Handle); errorEvents.Add(Event.Socket.Handle); isValidEvent = true; } if ((Event.InputEvents & PollEvent.EventTypeMask.UrgentInput) != 0) { readEvents.Add(Event.Socket.Handle); errorEvents.Add(Event.Socket.Handle); isValidEvent = true; } if ((Event.InputEvents & PollEvent.EventTypeMask.Output) != 0) { writeEvents.Add(Event.Socket.Handle); errorEvents.Add(Event.Socket.Handle); isValidEvent = true; } if ((Event.InputEvents & PollEvent.EventTypeMask.Error) != 0) { errorEvents.Add(Event.Socket.Handle); isValidEvent = true; } if (!isValidEvent) { Logger.PrintWarning(LogClass.ServiceBsd, $"Unsupported Poll input event type: {Event.InputEvents}"); return(WriteBsdResult(context, -1, LinuxError.EINVAL)); } } try { System.Net.Sockets.Socket.Select(readEvents, writeEvents, errorEvents, timeout); } catch (SocketException exception) { return(WriteWinSock2Error(context, (WsaError)exception.ErrorCode)); } for (int i = 0; i < fdsCount; i++) { PollEvent Event = events[i]; context.Memory.WriteInt32(bufferPosition + i * 8, Event.SocketFd); context.Memory.WriteInt16(bufferPosition + i * 8 + 4, (short)Event.InputEvents); PollEvent.EventTypeMask outputEvents = 0; Socket socket = Event.Socket.Handle; if (errorEvents.Contains(socket)) { outputEvents |= PollEvent.EventTypeMask.Error; if (!socket.Connected || !socket.IsBound) { outputEvents |= PollEvent.EventTypeMask.Disconnected; } } if (readEvents.Contains(socket)) { if ((Event.InputEvents & PollEvent.EventTypeMask.Input) != 0) { outputEvents |= PollEvent.EventTypeMask.Input; } } if (writeEvents.Contains(socket)) { outputEvents |= PollEvent.EventTypeMask.Output; } context.Memory.WriteInt16(bufferPosition + i * 8 + 6, (short)outputEvents); } return(WriteBsdResult(context, readEvents.Count + writeEvents.Count + errorEvents.Count, LinuxError.SUCCESS)); }
// Poll(u32 nfds, u32 timeout, buffer<unknown, 0x21, 0> fds) -> (i32 ret, u32 bsd_errno, buffer<unknown, 0x22, 0>) public ResultCode Poll(ServiceCtx context) { int fdsCount = context.RequestData.ReadInt32(); int timeout = context.RequestData.ReadInt32(); (ulong bufferPosition, ulong bufferSize) = context.Request.GetBufferType0x21(); if (timeout < -1 || fdsCount < 0 || (ulong)(fdsCount * 8) > bufferSize) { return(WriteBsdResult(context, -1, LinuxError.EINVAL)); } PollEvent[] events = new PollEvent[fdsCount]; for (int i = 0; i < fdsCount; i++) { int socketFd = context.Memory.Read <int>(bufferPosition + (ulong)i * 8); BsdSocket socket = RetrieveSocket(socketFd); if (socket == null) { return(WriteBsdResult(context, -1, LinuxError.EBADF)); } PollEvent.EventTypeMask inputEvents = (PollEvent.EventTypeMask)context.Memory.Read <short>(bufferPosition + (ulong)i * 8 + 4); PollEvent.EventTypeMask outputEvents = (PollEvent.EventTypeMask)context.Memory.Read <short>(bufferPosition + (ulong)i * 8 + 6); events[i] = new PollEvent(socketFd, socket, inputEvents, outputEvents); } List <Socket> readEvents = new List <Socket>(); List <Socket> writeEvents = new List <Socket>(); List <Socket> errorEvents = new List <Socket>(); foreach (PollEvent Event in events) { bool isValidEvent = false; if ((Event.InputEvents & PollEvent.EventTypeMask.Input) != 0) { readEvents.Add(Event.Socket.Handle); errorEvents.Add(Event.Socket.Handle); isValidEvent = true; } if ((Event.InputEvents & PollEvent.EventTypeMask.UrgentInput) != 0) { readEvents.Add(Event.Socket.Handle); errorEvents.Add(Event.Socket.Handle); isValidEvent = true; } if ((Event.InputEvents & PollEvent.EventTypeMask.Output) != 0) { writeEvents.Add(Event.Socket.Handle); errorEvents.Add(Event.Socket.Handle); isValidEvent = true; } if ((Event.InputEvents & PollEvent.EventTypeMask.Error) != 0) { errorEvents.Add(Event.Socket.Handle); isValidEvent = true; } if (!isValidEvent) { Logger.Warning?.Print(LogClass.ServiceBsd, $"Unsupported Poll input event type: {Event.InputEvents}"); return(WriteBsdResult(context, -1, LinuxError.EINVAL)); } } if (fdsCount != 0) { try { System.Net.Sockets.Socket.Select(readEvents, writeEvents, errorEvents, timeout); } catch (SocketException exception) { return(WriteWinSock2Error(context, (WsaError)exception.ErrorCode)); } } else if (timeout == -1) { // FIXME: If we get a timeout of -1 and there is no fds to wait on, this should kill the KProces. (need to check that with re) throw new InvalidOperationException(); } else { // FIXME: We should make the KThread sleep but we can't do much about it yet. Thread.Sleep(timeout); } for (int i = 0; i < fdsCount; i++) { PollEvent Event = events[i]; context.Memory.Write(bufferPosition + (ulong)i * 8, Event.SocketFd); context.Memory.Write(bufferPosition + (ulong)i * 8 + 4, (short)Event.InputEvents); PollEvent.EventTypeMask outputEvents = 0; Socket socket = Event.Socket.Handle; if (errorEvents.Contains(socket)) { outputEvents |= PollEvent.EventTypeMask.Error; if (!socket.Connected || !socket.IsBound) { outputEvents |= PollEvent.EventTypeMask.Disconnected; } } if (readEvents.Contains(socket)) { if ((Event.InputEvents & PollEvent.EventTypeMask.Input) != 0) { outputEvents |= PollEvent.EventTypeMask.Input; } } if (writeEvents.Contains(socket)) { outputEvents |= PollEvent.EventTypeMask.Output; } context.Memory.Write(bufferPosition + (ulong)i * 8 + 6, (short)outputEvents); } return(WriteBsdResult(context, readEvents.Count + writeEvents.Count + errorEvents.Count, LinuxError.SUCCESS)); }
public LinuxError Poll(List <PollEvent> events, int timeoutMilliseconds, out int updatedCount) { updatedCount = 0; List <ManualResetEvent> waiters = new List <ManualResetEvent>(); for (int i = 0; i < events.Count; i++) { PollEvent evnt = events[i]; EventFileDescriptor socket = (EventFileDescriptor)evnt.FileDescriptor; bool isValidEvent = false; if (evnt.Data.InputEvents.HasFlag(PollEventTypeMask.Input) || evnt.Data.InputEvents.HasFlag(PollEventTypeMask.UrgentInput)) { waiters.Add(socket.ReadEvent); isValidEvent = true; } if (evnt.Data.InputEvents.HasFlag(PollEventTypeMask.Output)) { waiters.Add(socket.WriteEvent); isValidEvent = true; } if (!isValidEvent) { Logger.Warning?.Print(LogClass.ServiceBsd, $"Unsupported Poll input event type: {evnt.Data.InputEvents}"); return(LinuxError.EINVAL); } } int index = WaitHandle.WaitAny(waiters.ToArray(), timeoutMilliseconds); if (index != WaitHandle.WaitTimeout) { for (int i = 0; i < events.Count; i++) { PollEvent evnt = events[i]; EventFileDescriptor socket = (EventFileDescriptor)evnt.FileDescriptor; if ((evnt.Data.InputEvents.HasFlag(PollEventTypeMask.Input) || evnt.Data.InputEvents.HasFlag(PollEventTypeMask.UrgentInput)) && socket.ReadEvent.WaitOne(0)) { waiters.Add(socket.ReadEvent); } if ((evnt.Data.InputEvents.HasFlag(PollEventTypeMask.Output)) && socket.WriteEvent.WaitOne(0)) { waiters.Add(socket.WriteEvent); } } } else { return(LinuxError.ETIMEDOUT); } return(LinuxError.SUCCESS); }
public bool IsCompatible(PollEvent evnt) { return(evnt.FileDescriptor is EventFileDescriptor); }
public bool IsCompatible(PollEvent evnt) { return(evnt.FileDescriptor is ManagedSocket); }