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