/ src / Ryujinx.HLE / HOS / Services / Sockets / Bsd / Impl / EventFileDescriptor.cs
EventFileDescriptor.cs
  1  using Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types;
  2  using System;
  3  using System.Runtime.InteropServices;
  4  using System.Threading;
  5  
  6  namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
  7  {
  8      class EventFileDescriptor : IFileDescriptor
  9      {
 10          private ulong _value;
 11          private readonly EventFdFlags _flags;
 12  
 13          private readonly object _lock = new();
 14  
 15          public bool Blocking { get => !_flags.HasFlag(EventFdFlags.NonBlocking); set => throw new NotSupportedException(); }
 16  
 17          public ManualResetEvent WriteEvent { get; }
 18          public ManualResetEvent ReadEvent { get; }
 19  
 20          public EventFileDescriptor(ulong value, EventFdFlags flags)
 21          {
 22              // FIXME: We should support blocking operations.
 23              // Right now they can't be supported because it would cause the
 24              // service to lock up as we only have one thread processing requests.
 25              flags |= EventFdFlags.NonBlocking;
 26  
 27              _value = value;
 28              _flags = flags;
 29  
 30              WriteEvent = new ManualResetEvent(false);
 31              ReadEvent = new ManualResetEvent(false);
 32              UpdateEventStates();
 33          }
 34  
 35          public int Refcount { get; set; }
 36  
 37          public void Dispose()
 38          {
 39              WriteEvent.Dispose();
 40              ReadEvent.Dispose();
 41          }
 42  
 43          private void ResetEventStates()
 44          {
 45              WriteEvent.Reset();
 46              ReadEvent.Reset();
 47          }
 48  
 49          private void UpdateEventStates()
 50          {
 51              if (_value > 0)
 52              {
 53                  ReadEvent.Set();
 54              }
 55  
 56              if (_value != uint.MaxValue - 1)
 57              {
 58                  WriteEvent.Set();
 59              }
 60          }
 61  
 62          public LinuxError Read(out int readSize, Span<byte> buffer)
 63          {
 64              if (buffer.Length < sizeof(ulong))
 65              {
 66                  readSize = 0;
 67  
 68                  return LinuxError.EINVAL;
 69              }
 70  
 71              lock (_lock)
 72              {
 73                  ResetEventStates();
 74  
 75                  ref ulong count = ref MemoryMarshal.Cast<byte, ulong>(buffer)[0];
 76  
 77                  if (_value == 0)
 78                  {
 79                      if (Blocking)
 80                      {
 81                          while (_value == 0)
 82                          {
 83                              Monitor.Wait(_lock);
 84                          }
 85                      }
 86                      else
 87                      {
 88                          readSize = 0;
 89  
 90                          UpdateEventStates();
 91                          return LinuxError.EAGAIN;
 92                      }
 93                  }
 94  
 95                  readSize = sizeof(ulong);
 96  
 97                  if (_flags.HasFlag(EventFdFlags.Semaphore))
 98                  {
 99                      --_value;
100  
101                      count = 1;
102                  }
103                  else
104                  {
105                      count = _value;
106  
107                      _value = 0;
108                  }
109  
110                  UpdateEventStates();
111                  return LinuxError.SUCCESS;
112              }
113          }
114  
115          public LinuxError Write(out int writeSize, ReadOnlySpan<byte> buffer)
116          {
117              if (!MemoryMarshal.TryRead(buffer, out ulong count) || count == ulong.MaxValue)
118              {
119                  writeSize = 0;
120  
121                  return LinuxError.EINVAL;
122              }
123  
124              lock (_lock)
125              {
126                  ResetEventStates();
127  
128                  if (_value > _value + count)
129                  {
130                      if (Blocking)
131                      {
132                          Monitor.Wait(_lock);
133                      }
134                      else
135                      {
136                          writeSize = 0;
137  
138                          UpdateEventStates();
139                          return LinuxError.EAGAIN;
140                      }
141                  }
142  
143                  writeSize = sizeof(ulong);
144  
145                  _value += count;
146                  Monitor.Pulse(_lock);
147  
148                  UpdateEventStates();
149                  return LinuxError.SUCCESS;
150              }
151          }
152      }
153  }