Signed-off-by: rean <[email protected]>
---
 core/semaphore.cc           | 23 +++++++++++++++++++++
 modules/tests/Makefile      |  2 +-
 tests/tst-sem-timed-wait.cc | 50 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 74 insertions(+), 1 deletion(-)
 create mode 100644 tests/tst-sem-timed-wait.cc

diff --git a/core/semaphore.cc b/core/semaphore.cc
index 73732b8..6081a87 100644
--- a/core/semaphore.cc
+++ b/core/semaphore.cc
@@ -46,6 +46,29 @@ bool semaphore::wait(unsigned units, sched::timer* tmr)
     sched::thread::wait_until(_mtx,
             [&] { return (tmr && tmr->expired()) || !wr.owner; });
 
+    // If wr.owner is not nullptr then there was a timeout (post() did not
+    // wake us). In that case, just remove the wait_record (local variable)
+    // that we just pushed onto _waiters (via push_back)
+    if (wr.owner) {
+       auto i = _waiters.begin();
+       // _val can be 0 right now, so we can't use the same condition that
+       // post_unlocked uses "_val > 0 && i != _waiters.end()"
+       while (i != _waiters.end()) {
+          auto wait_rec = i++;
+          if (wait_rec->owner == sched::thread::current()) {
+             // We found our wait record so remove it from the list.
+             // There is no need to wake ourselves or set wr.owner = nullptr
+             // we want to remember that we timed out (if post happened then
+             // wr.owner would have been set to nullptr)
+             if (wait_rec->units <= _val) {
+                _val -= wait_rec->units;
+             }
+             // Remove our wait_record
+             _waiters.erase(wait_rec);
+          }
+       }
+    }
+
     // if wr.owner, it's a timeout - post() didn't wake us and didn't decrease
     // the semaphore's value for us. Note we are holding the mutex, so there
     // can be no race with post().
diff --git a/modules/tests/Makefile b/modules/tests/Makefile
index fb8d001..6e3aea7 100644
--- a/modules/tests/Makefile
+++ b/modules/tests/Makefile
@@ -84,7 +84,7 @@ tests := tst-pthread.so misc-ramdisk.so tst-vblk.so 
tst-bsd-evh.so \
        tst-namespace.so tst-without-namespace.so payload-env.so \
        payload-merge-env.so misc-execve.so misc-execve-payload.so 
misc-mutex2.so \
        tst-pthread-setcancelstate.so tst-syscall.so tst-pin.so tst-run.so \
-       tst-ifaddrs.so tst-pthread-affinity-inherit.so
+       tst-ifaddrs.so tst-pthread-affinity-inherit.so tst-sem-timed-wait.so
 
 #      libstatic-thread-variable.so tst-static-thread-variable.so \
 
diff --git a/tests/tst-sem-timed-wait.cc b/tests/tst-sem-timed-wait.cc
new file mode 100644
index 0000000..f401ed0
--- /dev/null
+++ b/tests/tst-sem-timed-wait.cc
@@ -0,0 +1,50 @@
+#include <osv/semaphore.hh>
+#include <stdio.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <errno.h>
+
+unsigned int tests_total = 0, tests_failed = 0;
+
+void report(const char* name, bool passed)
+{
+   static const char* status[] = {"FAIL", "PASS"};
+   printf("%s: %s\n", status[passed], name);
+   tests_total += 1;
+   tests_failed += !passed;
+}
+
+int main(void)
+{
+   printf("Starting sem_timed_wait test\n");
+
+   // Basic flow for test
+   // 1) Create a semaphore (initialized to 0)
+   // 2) Do a timed-wait on it
+   // 3) We're never signaled/woken so our stack-allocated wait_record remains
+   // on the semaphore's waiters list
+   //
+   // In the failure case the end result is a stacktrace that looks like:
+   // Assertion failed: !hook.is_linked()
+   //(/usr/include/boost/intrusive/detail/generic_hook.hpp: destructor_impl: 
47)
+
+   //[backtrace]
+   //0x0000000000225a48 <__assert_fail+24>
+   //0x00000000003c40e9 <???+3948777>
+   //0x00000000003c4242 <semaphore::wait(unsigned int, sched::timer*)+98>
+   //0x0000100000c01057 <???+12587095>
+
+   semaphore sem(0);
+   timespec ts;
+   ts.tv_sec = 0;
+   ts.tv_nsec = 0;
+
+   sched::timer tmr(*sched::thread::current());
+   osv::clock::wall::time_point time(std::chrono::seconds(ts.tv_sec) +
+                                      std::chrono::nanoseconds(ts.tv_nsec));
+   tmr.set(time);
+   bool ret_val = sem.wait(1, &tmr);
+   report("sem_timedwait\0", ret_val == false);
+   printf("SUMMARY: %u tests / %u failures\n", tests_total, tests_failed);
+   return tests_failed == 0 ? 0 : 1;
+}
-- 
2.7.4

-- 
You received this message because you are subscribed to the Google Groups "OSv 
Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to