yum-mirror/slang

Making it easier to work with shaders

git clone https://git.yummers.dev/yum-mirror/slang

Ellie Hermaszewskaformatf65d756bf

master
12.9 KiB460 linesraw
1#include "d3d12-posix-synchapi.h"
2
3#include "slang.h"
4
5#if SLANG_LINUX_FAMILY
6
7#include "core/slang-common.h"
8
9#include <cerrno>
10#include <fcntl.h>
11#include <sys/epoll.h>
12#include <sys/eventfd.h>
13#include <sys/poll.h>
14#include <sys/timerfd.h>
15#include <unistd.h>
16
17// To keep aligned with the d3d12 API, we store file descriptors in the low 32
18// bits of HANDLEs.
19static int _handleToFD(HANDLE h)
20{
21    auto i = reinterpret_cast<std::intptr_t>(h);
22    int fd = static_cast<int>(i);
23    return fd;
24}
25
26static int _handleToFlags(HANDLE h)
27{
28    auto i = reinterpret_cast<std::intptr_t>(h) >> 32;
29    int flags = static_cast<int>(i);
30    return flags;
31}
32
33static HANDLE _fdToHandle(int fd, int flags)
34{
35    static_assert(sizeof(int) <= 4);
36    static_assert(sizeof(std::intptr_t) >= 8);
37    return reinterpret_cast<HANDLE>(static_cast<std::intptr_t>(flags) << 32 | fd);
38}
39
40
41HANDLE CreateEventEx(
42    LPSECURITY_ATTRIBUTES lpEventAttributes,
43    LPCSTR lpName,
44    DWORD dwFlags,
45    DWORD dwDesiredAccess)
46{
47    int fd = ::eventfd(dwFlags & CREATE_EVENT_INITIAL_SET ? 1 : 0, EFD_CLOEXEC | EFD_NONBLOCK);
48    // Make sure not to return a zero handle, duplicate the fd if necessary
49    if (fd == 0)
50    {
51        int nextFd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
52        if (fcntl(nextFd, F_SETFL, O_NONBLOCK) == -1)
53        {
54            close(nextFd);
55            nextFd = -1;
56        }
57        close(fd);
58        fd = nextFd;
59    }
60    return fd == -1 ? nullptr : _fdToHandle(fd, dwFlags);
61}
62
63BOOL CloseHandle(HANDLE h)
64{
65    if (h == 0)
66    {
67        return 1;
68    }
69    // TODO: Windows does reference counting, how to, dupfd?
70    return ::close(_handleToFD(h)) == 0;
71    return 1;
72}
73
74BOOL ResetEvent(HANDLE h)
75{
76    int fd = _handleToFD(h);
77    pollfd pfd{fd, POLLIN, 0};
78    uint64_t x;
79    int r = 0;
80    int nEvents = poll(&pfd, 1, 0);
81    if (pfd.revents != POLLIN)
82    {
83        // Nothing to read, already reset
84        return 1;
85    }
86    if (nEvents != 1)
87    {
88        return 0;
89    }
90    r = read(fd, &x, sizeof(x));
91    if (r == sizeof(x))
92    {
93        // We reset it
94        return 1;
95    }
96    if (r == -1 && errno == EAGAIN)
97    {
98        // Something else reset it
99        return 1;
100    }
101    return 0;
102}
103
104BOOL SetEvent(HANDLE h)
105{
106    int fd = _handleToFD(h);
107    pollfd pfd{fd, POLLOUT, 0};
108    for (;;)
109    {
110        int nEvents = poll(&pfd, 1, -1);
111        SLANG_ASSERT(nEvents != -1);
112        SLANG_ASSERT(nEvents != 0); // shouldn't have timed out
113        const uint64_t one = 1;
114        int w = ::write(fd, &one, sizeof(one));
115        if (w == sizeof(one))
116        {
117            return 1;
118        }
119        if (errno != EAGAIN)
120        {
121            return 0;
122        }
123    }
124}
125
126DWORD WaitForSingleObject(const HANDLE h, const DWORD ms)
127{
128    int fd = _handleToFD(h);
129    bool manualReset = _handleToFlags(h) & CREATE_EVENT_MANUAL_RESET;
130    pollfd pfd{fd, POLLIN, 0};
131    uint64_t x;
132    int r = 0;
133    // Implement unlimited waits as timing out with WAIT_FAILED after 5
134    // seconds. It's probably something fishy with d3dvk-proton or our synchapi
135    // implementation
136    const bool isInfinite = ms == INFINITE;
137    const DWORD fiveSeconds = 5000;
138    int nEvents = poll(&pfd, 1, isInfinite ? fiveSeconds : ms);
139    if (pfd.revents != POLLIN)
140    {
141        return WAIT_FAILED;
142    }
143    if (nEvents == -1)
144    {
145        return WAIT_FAILED;
146    }
147    if (nEvents == 0)
148    {
149        return isInfinite ? WAIT_FAILED : WAIT_TIMEOUT;
150    }
151    if (manualReset)
152    {
153        return WAIT_OBJECT_0;
154    }
155    r = read(fd, &x, sizeof(x));
156    if (r == sizeof(x))
157    {
158        return WAIT_OBJECT_0;
159    }
160    if (r == -1 && errno == EAGAIN)
161    {
162        return isInfinite ? WAIT_FAILED : WAIT_TIMEOUT;
163    }
164    return WAIT_FAILED;
165}
166
167DWORD WaitForMultipleObjects(DWORD n, const HANDLE* hs, BOOL bWaitAll, DWORD requestedMs)
168{
169    if (n == 0)
170    {
171        return bWaitAll ? WAIT_OBJECT_0 : WAIT_FAILED;
172    }
173
174    // Bail out of infinite waits after 5 seconds as it's probably a
175    // driver/vkd3d-proton/synchapi bug
176    const bool isInfinite = requestedMs == INFINITE;
177    const DWORD fiveSeconds = 5000;
178    const auto dwMilliseconds = isInfinite ? fiveSeconds : requestedMs;
179
180    DWORD res;
181    int fds[n];
182    int flagss[n];
183    epoll_event evs[n + 1]; // +1 for our timer
184    int ufd = -1;
185    int epfd = epoll_create1(EPOLL_CLOEXEC);
186    if (epfd == -1)
187    {
188        goto fail;
189    }
190
191    for (int i = 0; i < n; ++i)
192    {
193        fds[i] = _handleToFD(hs[i]);
194        flagss[i] = _handleToFlags(hs[i]);
195        epoll_event ev;
196        ev.data.fd = fds[i];
197        ev.events = EPOLLIN | EPOLLONESHOT;
198        if (epoll_ctl(epfd, EPOLL_CTL_ADD, fds[i], &ev) == -1)
199        {
200            goto fail;
201        }
202    }
203
204    // The wait all case can't be made correct on linux, as we can't atomically
205    // read from several fds and eventfd is the interface available from
206    // vkd3d-proton.
207    //
208    // As a best-effort we wait until they're all free, then grab them on one
209    // after the other, and put the values back if we can't claim them all, it
210    // sucks.
211    //
212    if (bWaitAll)
213    {
214        // Use a timer to easily know for sure when we've timed out
215        if (dwMilliseconds != INFINITE)
216        {
217            ufd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
218            if (ufd == -1)
219            {
220                goto fail;
221            }
222            itimerspec spec;
223            spec.it_interval.tv_sec = 0;
224            spec.it_interval.tv_nsec = 0;
225            spec.it_value.tv_sec = 0;
226            spec.it_value.tv_nsec = 1000000 * dwMilliseconds;
227            if (timerfd_settime(ufd, 0, &spec, nullptr) == -1)
228            {
229                goto fail;
230            }
231            evs[n].data.fd = ufd;
232            evs[n].events = EPOLLIN | EPOLLONESHOT;
233            if (epoll_ctl(epfd, EPOLL_CTL_ADD, ufd, &evs[n]) == -1)
234            {
235                goto fail;
236            }
237        }
238
239        bool timesUp = false;
240        int nSeenEvents = 0;
241        // Repeatedly call epoll_wait to eliminate read fds until the timer
242        // expires or we have elimininated all our fds
243        do
244        {
245            do
246            {
247                // Wait until epoll tells us they're all available, or the timer is
248                const int nEvents = epoll_wait(epfd, evs, n + 1, -1);
249                // We didn't specify a timeout, so 0 results is abnormal
250                if (nEvents < 1)
251                {
252                    goto fail;
253                }
254
255                // Process all the returned fds
256                for (int i = 0; i < nEvents; ++i)
257                {
258                    if (!(evs[i].events & EPOLLIN))
259                    {
260                        // Something exceptional happened on the fd
261                        // Possibly we could just continue and hope it doesn't
262                        // happen again?
263                        goto fail;
264                    }
265                    if (evs[i].data.fd == ufd)
266                    {
267                        // We're out of time, make this the last loop
268                        uint64_t x;
269                        int r = read(ufd, &x, sizeof(x));
270                        if (r == sizeof(x))
271                        {
272                            timesUp = true;
273                        }
274                        else
275                        {
276                            goto fail;
277                        }
278                    }
279                    else
280                    {
281                        // EPOLLONESHOT has removed this fd
282                        ++nSeenEvents;
283                    }
284                }
285            } while (!(timesUp || nSeenEvents == n));
286
287            // If we got here without seeing enough events, we must have timed out
288            if (nSeenEvents < n)
289            {
290                res = isInfinite ? WAIT_FAILED : WAIT_TIMEOUT;
291                goto end;
292            }
293
294            // See if all the events are readable.
295            // This isn't strictly necessary from a correctness point of view,
296            // but since we're not correct anything we can do helps, and it
297            // makes the code a bit cleaner.
298            // Put all the events back in our epoll instance and see if they're
299            // all readable.
300            for (int i = 0; i < n; ++i)
301            {
302                epoll_event modEv;
303                modEv.data.fd = fds[i];
304                modEv.events = EPOLLIN | EPOLLONESHOT;
305                if (epoll_ctl(epfd, EPOLL_CTL_MOD, fds[i], &modEv) == -1)
306                {
307                    goto fail;
308                }
309            }
310            // Remove the timer if we're using it
311            if (dwMilliseconds != INFINITE && epoll_ctl(epfd, EPOLL_CTL_DEL, ufd, nullptr) == -1)
312            {
313                goto fail;
314            }
315            int nEvents = epoll_wait(epfd, evs, n, 0);
316            if (nEvents < 0)
317            {
318                goto fail;
319            }
320            else if (nEvents < n)
321            {
322                // They're not all still available :(
323                // Put our timer back in and try again from the top
324                if (dwMilliseconds != INFINITE &&
325                    epoll_ctl(epfd, EPOLL_CTL_ADD, ufd, &evs[n]) == -1)
326                {
327                    goto fail;
328                }
329                // Put back the any fds which did trigger
330                for (int i = 0; i < nEvents; ++i)
331                {
332                    epoll_event modEv = evs[i];
333                    modEv.events = EPOLLIN | EPOLLONESHOT;
334                    if (epoll_ctl(epfd, EPOLL_CTL_MOD, modEv.data.fd, &modEv) == -1)
335                    {
336                        goto fail;
337                    }
338                }
339                continue;
340            }
341            else if (nEvents == n)
342            {
343                for (int i = 0; i < nEvents; ++i)
344                {
345                    if (!(evs->events & EPOLLIN))
346                    {
347                        goto fail;
348                    }
349                }
350            }
351
352            // Try to grab all the events
353            uint64_t vs[n];
354            int i;
355            bool failure = false;
356            for (i = 0; i < n; ++i)
357            {
358                if (flagss[i] & CREATE_EVENT_MANUAL_RESET)
359                {
360                    // We don't need to read this to unset it
361                    continue;
362                }
363                int r = read(fds[i], &vs[i], sizeof(vs[i]));
364                if (r == sizeof(vs[i]))
365                {
366                    continue;
367                }
368                else if (r == -1 && errno == EAGAIN)
369                {
370                    // contention, put things back and try again
371                    break;
372                }
373                else
374                {
375                    // failure, put things back and fail
376                    failure = true;
377                    break;
378                }
379            }
380            if (i < n)
381            {
382                // contention or failure
383                for (int j = 0; j < i; ++j)
384                {
385                    if (flagss[i] & CREATE_EVENT_MANUAL_RESET)
386                    {
387                        // We didn't read, so we shouldn't write
388                        continue;
389                    }
390                    // TODO: If this doesn't succeed then another thread has jumped
391                    // in between our non-atomic reads earlier, oops!
392                    //
393                    // This is just one case of failure we can detect,
394                    // arbitrarily many things may have happened between reads
395                    // and we just wouldn't know...
396                    int w = write(fds[j], &vs[j], sizeof(vs[j]));
397                    SLANG_ASSERT(w == sizeof(vs[j]));
398                }
399                if (failure)
400                {
401                    goto fail;
402                }
403            }
404            else
405            {
406                // success
407                res = WAIT_OBJECT_0;
408                goto end;
409            }
410
411            // If we get here then we've got some contention, go back to the top and try again (or
412            // timeout)
413        } while (!timesUp);
414    }
415    else
416    {
417        // Wait any
418        const int nEvents =
419            epoll_wait(epfd, evs, n, dwMilliseconds == INFINITE ? -1 : dwMilliseconds);
420        if (nEvents == -1)
421        {
422            goto fail;
423        }
424        if (nEvents == 0)
425        {
426            res = isInfinite ? WAIT_FAILED : WAIT_TIMEOUT;
427            goto end;
428        }
429        // Try reads until we get one
430        for (int i = 0; i < nEvents; ++i)
431        {
432            uint64_t x;
433            if (!evs[i].events & EPOLLIN)
434            {
435                continue;
436            }
437            const int r = ::read(evs[i].data.fd, &x, sizeof(x));
438            if (r == sizeof(x))
439            {
440                res = WAIT_OBJECT_0;
441                goto end;
442            }
443            if (errno != EAGAIN)
444            {
445                goto fail;
446            }
447            // Some other waiter got this one first
448        }
449    }
450
451    goto end;
452fail:
453    res = WAIT_FAILED;
454end:
455    close(ufd);
456    close(epfd);
457    return res;
458}
459
460#endif // SLANG_LINUX_FAMILY