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 }