Index: fusl/src/aio/aio_suspend.c |
diff --git a/fusl/src/aio/aio_suspend.c b/fusl/src/aio/aio_suspend.c |
new file mode 100644 |
index 0000000000000000000000000000000000000000..08fb5ddcf1a7b1450fb7446d9e8be202eaa1f42a |
--- /dev/null |
+++ b/fusl/src/aio/aio_suspend.c |
@@ -0,0 +1,79 @@ |
+#include <aio.h> |
+#include <errno.h> |
+#include <time.h> |
+#include "atomic.h" |
+#include "libc.h" |
+#include "pthread_impl.h" |
+ |
+extern volatile int __aio_fut; |
+ |
+int aio_suspend(const struct aiocb *const cbs[], int cnt, const struct timespec *ts) |
+{ |
+ int i, tid = 0, ret, expect = 0; |
+ struct timespec at; |
+ volatile int dummy_fut, *pfut; |
+ int nzcnt = 0; |
+ const struct aiocb *cb = 0; |
+ |
+ pthread_testcancel(); |
+ |
+ if (cnt<0) { |
+ errno = EINVAL; |
+ return -1; |
+ } |
+ |
+ for (i=0; i<cnt; i++) if (cbs[i]) { |
+ if (aio_error(cbs[i]) != EINPROGRESS) return 0; |
+ nzcnt++; |
+ cb = cbs[i]; |
+ } |
+ |
+ if (ts) { |
+ clock_gettime(CLOCK_MONOTONIC, &at); |
+ at.tv_sec += ts->tv_sec; |
+ if ((at.tv_nsec += ts->tv_nsec) >= 1000000000) { |
+ at.tv_nsec -= 1000000000; |
+ at.tv_sec++; |
+ } |
+ } |
+ |
+ for (;;) { |
+ for (i=0; i<cnt; i++) |
+ if (cbs[i] && aio_error(cbs[i]) != EINPROGRESS) |
+ return 0; |
+ |
+ switch (nzcnt) { |
+ case 0: |
+ pfut = &dummy_fut; |
+ break; |
+ case 1: |
+ pfut = (void *)&cb->__err; |
+ expect = EINPROGRESS | 0x80000000; |
+ a_cas(pfut, EINPROGRESS, expect); |
+ break; |
+ default: |
+ pfut = &__aio_fut; |
+ if (!tid) tid = __pthread_self()->tid; |
+ expect = a_cas(pfut, 0, tid); |
+ if (!expect) expect = tid; |
+ /* Need to recheck the predicate before waiting. */ |
+ for (i=0; i<cnt; i++) |
+ if (cbs[i] && aio_error(cbs[i]) != EINPROGRESS) |
+ return 0; |
+ break; |
+ } |
+ |
+ ret = __timedwait_cp(pfut, expect, CLOCK_MONOTONIC, ts?&at:0, 1); |
+ |
+ switch (ret) { |
+ case ETIMEDOUT: |
+ ret = EAGAIN; |
+ case ECANCELED: |
+ case EINTR: |
+ errno = ret; |
+ return -1; |
+ } |
+ } |
+} |
+ |
+LFS64(aio_suspend); |