/ src / Ryujinx.HLE / HOS / Services / Sockets / Bsd / Impl / ManagedSocketPollManager.cs
ManagedSocketPollManager.cs
  1  using Ryujinx.Common.Logging;
  2  using Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types;
  3  using System.Collections.Generic;
  4  using System.Net.Sockets;
  5  
  6  namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
  7  {
  8      class ManagedSocketPollManager : IPollManager
  9      {
 10          private static ManagedSocketPollManager _instance;
 11  
 12          public static ManagedSocketPollManager Instance
 13          {
 14              get
 15              {
 16                  _instance ??= new ManagedSocketPollManager();
 17  
 18                  return _instance;
 19              }
 20          }
 21  
 22          public bool IsCompatible(PollEvent evnt)
 23          {
 24              return evnt.FileDescriptor is ManagedSocket;
 25          }
 26  
 27          public LinuxError Poll(List<PollEvent> events, int timeoutMilliseconds, out int updatedCount)
 28          {
 29              List<Socket> readEvents = new();
 30              List<Socket> writeEvents = new();
 31              List<Socket> errorEvents = new();
 32  
 33              updatedCount = 0;
 34  
 35              foreach (PollEvent evnt in events)
 36              {
 37                  ManagedSocket socket = (ManagedSocket)evnt.FileDescriptor;
 38  
 39                  bool isValidEvent = evnt.Data.InputEvents == 0;
 40  
 41                  errorEvents.Add(socket.Socket);
 42  
 43                  if ((evnt.Data.InputEvents & PollEventTypeMask.Input) != 0)
 44                  {
 45                      readEvents.Add(socket.Socket);
 46  
 47                      isValidEvent = true;
 48                  }
 49  
 50                  if ((evnt.Data.InputEvents & PollEventTypeMask.UrgentInput) != 0)
 51                  {
 52                      readEvents.Add(socket.Socket);
 53  
 54                      isValidEvent = true;
 55                  }
 56  
 57                  if ((evnt.Data.InputEvents & PollEventTypeMask.Output) != 0)
 58                  {
 59                      writeEvents.Add(socket.Socket);
 60  
 61                      isValidEvent = true;
 62                  }
 63  
 64                  if (!isValidEvent)
 65                  {
 66                      Logger.Warning?.Print(LogClass.ServiceBsd, $"Unsupported Poll input event type: {evnt.Data.InputEvents}");
 67                      return LinuxError.EINVAL;
 68                  }
 69              }
 70  
 71              try
 72              {
 73                  int actualTimeoutMicroseconds = timeoutMilliseconds == -1 ? -1 : timeoutMilliseconds * 1000;
 74  
 75                  Socket.Select(readEvents, writeEvents, errorEvents, actualTimeoutMicroseconds);
 76              }
 77              catch (SocketException exception)
 78              {
 79                  return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
 80              }
 81  
 82              foreach (PollEvent evnt in events)
 83              {
 84                  Socket socket = ((ManagedSocket)evnt.FileDescriptor).Socket;
 85  
 86                  PollEventTypeMask outputEvents = evnt.Data.OutputEvents & ~evnt.Data.InputEvents;
 87  
 88                  if (errorEvents.Contains(socket))
 89                  {
 90                      outputEvents |= PollEventTypeMask.Error;
 91  
 92                      if (!socket.Connected || !socket.IsBound)
 93                      {
 94                          outputEvents |= PollEventTypeMask.Disconnected;
 95                      }
 96                  }
 97  
 98                  if (readEvents.Contains(socket))
 99                  {
100                      if ((evnt.Data.InputEvents & PollEventTypeMask.Input) != 0)
101                      {
102                          outputEvents |= PollEventTypeMask.Input;
103                      }
104                  }
105  
106                  if (writeEvents.Contains(socket))
107                  {
108                      outputEvents |= PollEventTypeMask.Output;
109                  }
110  
111                  evnt.Data.OutputEvents = outputEvents;
112              }
113  
114              updatedCount = readEvents.Count + writeEvents.Count + errorEvents.Count;
115  
116              return LinuxError.SUCCESS;
117          }
118  
119          public LinuxError Select(List<PollEvent> events, int timeout, out int updatedCount)
120          {
121              List<Socket> readEvents = new();
122              List<Socket> writeEvents = new();
123              List<Socket> errorEvents = new();
124  
125              updatedCount = 0;
126  
127              foreach (PollEvent pollEvent in events)
128              {
129                  ManagedSocket socket = (ManagedSocket)pollEvent.FileDescriptor;
130  
131                  if (pollEvent.Data.InputEvents.HasFlag(PollEventTypeMask.Input))
132                  {
133                      readEvents.Add(socket.Socket);
134                  }
135  
136                  if (pollEvent.Data.InputEvents.HasFlag(PollEventTypeMask.Output))
137                  {
138                      writeEvents.Add(socket.Socket);
139                  }
140  
141                  if (pollEvent.Data.InputEvents.HasFlag(PollEventTypeMask.Error))
142                  {
143                      errorEvents.Add(socket.Socket);
144                  }
145              }
146  
147              Socket.Select(readEvents, writeEvents, errorEvents, timeout);
148  
149              updatedCount = readEvents.Count + writeEvents.Count + errorEvents.Count;
150  
151              foreach (PollEvent pollEvent in events)
152              {
153                  ManagedSocket socket = (ManagedSocket)pollEvent.FileDescriptor;
154  
155                  if (readEvents.Contains(socket.Socket))
156                  {
157                      pollEvent.Data.OutputEvents |= PollEventTypeMask.Input;
158                  }
159  
160                  if (writeEvents.Contains(socket.Socket))
161                  {
162                      pollEvent.Data.OutputEvents |= PollEventTypeMask.Output;
163                  }
164  
165                  if (errorEvents.Contains(socket.Socket))
166                  {
167                      pollEvent.Data.OutputEvents |= PollEventTypeMask.Error;
168                  }
169              }
170  
171              return LinuxError.SUCCESS;
172          }
173      }
174  }