[kernel] Timestamps option for signaled objects

This patch enhances zx_object_wait_async() to optionally
tell the consumer the monotonic time when the object was
signaled by a new options flag ZX_WAIT_ASYNC_TIMESTAMP

The zx_packet_signal_t is therefore enhanced to contain
a timestamp member which replaces an existing reserved0
member.

TEST: new core-test added.

ZX-896 #comment progress

Change-Id: I480deba1a3edfb3deb25ff9754515319868e4f56
diff --git a/zircon/docs/syscalls/object_wait_async.md b/zircon/docs/syscalls/object_wait_async.md
index 6e9f61b..8e8d423 100644
--- a/zircon/docs/syscalls/object_wait_async.md
+++ b/zircon/docs/syscalls/object_wait_async.md
@@ -28,7 +28,8 @@
 
 *handle* points to the object that is to be watched for changes and must be a waitable object.
 
-The *options* argument must be set to **ZX_WAIT_ASYNC_ONCE**.
+The *options* argument can be 0 or it can be ZX_WAIT_ASYNC_TIMESTAMP which causes
+the system to capture a timestamp when the wait triggered.
 
 The *signals* argument indicates which signals on the object specified by *handle*
 will cause a packet to be enqueued, and if **any** of those signals are asserted when
@@ -53,12 +54,18 @@
     zx_signals_t trigger;
     zx_signals_t observed;
     uint64_t count;
+    zx_time_t timestamp;       // depends on ZX_WAIT_ASYNC_TIMESTAMP
+    uint64_t reserved1;
 } zx_packet_signal_t;
 ```
 
 *trigger* is the signals used in the call to `zx_object_wait_async()`, *observed* is the
-signals actually observed, and *count* is a per object defined count of pending operations. Use
-the `zx_port_packet_t`'s *key* member to track what object this packet corresponds to and
+signals actually observed, *count* is a per object defined count of pending operations
+and *timestamp* is clock-monotonic time when the object state transitioned to meet the
+trigger condition. If options does not include ZX_WAIT_ASYNC_TIMESTAMP the timestamp is
+reported as 0.
+
+Use the `zx_port_packet_t`'s *key* member to track what object this packet corresponds to and
 therefore match *count* with the operation.
 
 ## RIGHTS
@@ -75,7 +82,7 @@
 
 ## ERRORS
 
-**ZX_ERR_INVALID_ARGS**  *options* is not **ZX_WAIT_ASYNC_ONCE**.
+**ZX_ERR_INVALID_ARGS**  *options* is not 0 or **ZX_WAIT_ASYNC_TIMESTAMP**.
 
 **ZX_ERR_BAD_HANDLE**  *handle* is not a valid handle or *port* is not a valid handle.
 
diff --git a/zircon/kernel/object/include/object/port_dispatcher.h b/zircon/kernel/object/include/object/port_dispatcher.h
index 39e22a2..28b12dc 100644
--- a/zircon/kernel/object/include/object/port_dispatcher.h
+++ b/zircon/kernel/object/include/object/port_dispatcher.h
@@ -129,7 +129,7 @@
 
     using List = fbl::DoublyLinkedList<PortObserver*, PortObserver::ListTraits>;
 
-    PortObserver(uint32_t type, const Handle* handle, fbl::RefPtr<PortDispatcher> port,
+    PortObserver(uint32_t options, const Handle* handle, fbl::RefPtr<PortDispatcher> port,
                  Lock<fbl::Mutex>* port_lock, uint64_t key, zx_signals_t signals);
 
     ~PortObserver() = default;
@@ -155,7 +155,7 @@
     // OnInitialize(), OnStateChange() and OnCancel().
     Flags MaybeQueue(zx_signals_t new_state, uint64_t count);
 
-    const uint32_t type_;
+    const uint32_t options_;
     const zx_signals_t trigger_;
     PortPacket packet_;
 
diff --git a/zircon/kernel/object/port_dispatcher.cc b/zircon/kernel/object/port_dispatcher.cc
index f5c935e..8961246f 100644
--- a/zircon/kernel/object/port_dispatcher.cc
+++ b/zircon/kernel/object/port_dispatcher.cc
@@ -88,9 +88,9 @@
     // Note that packet is initialized to zeros.
 }
 
-PortObserver::PortObserver(uint32_t type, const Handle* handle, fbl::RefPtr<PortDispatcher> port,
+PortObserver::PortObserver(uint32_t options, const Handle* handle, fbl::RefPtr<PortDispatcher> port,
                            Lock<fbl::Mutex>* port_lock, uint64_t key, zx_signals_t signals)
-    : type_(type),
+    : options_(options),
       trigger_(signals),
       packet_(handle, nullptr),
       port_(ktl::move(port)),
@@ -104,7 +104,7 @@
     auto& packet = packet_.packet;
     packet.status = ZX_OK;
     packet.key = key;
-    packet.type = type_;
+    packet.type = ZX_PKT_TYPE_SIGNAL_ONE;
     packet.signal.trigger = trigger_;
 }
 
@@ -151,14 +151,14 @@
     if ((trigger_ & new_state) == 0u)
         return 0;
 
+    if (options_ & ZX_WAIT_ASYNC_TIMESTAMP) {
+        // Getting the current time can be somewhat expensive.
+        packet_.packet.signal.timestamp = current_time();
+    }
     // Queue cannot fail because the packet is not allocated in the packet arena,
     // and does not count against the per-port limit.
-    auto status = port_->Queue(&packet_, new_state, count);
-
-    if ((type_ == ZX_PKT_TYPE_SIGNAL_ONE) || (status != ZX_OK))
-        return kNeedRemoval;
-
-    return 0;
+    port_->Queue(&packet_, new_state, count);
+    return kNeedRemoval;
 }
 
 /////////////////////////////////////////////////////////////////////////////////////////
@@ -424,22 +424,9 @@
     if (!dispatcher->is_waitable())
         return ZX_ERR_NOT_SUPPORTED;
 
-    uint32_t type;
-    switch (options) {
-        case ZX_WAIT_ASYNC_ONCE:
-            type = ZX_PKT_TYPE_SIGNAL_ONE;
-            break;
-        case 1u:
-            printf("ZX_WAIT_ASYNC_REPEATING no longer supported. Use ZX_WAIT_ASYNC_ONCE.\n");
-            return ZX_ERR_INVALID_ARGS;
-            break;
-        default:
-            return ZX_ERR_INVALID_ARGS;
-    }
-
     fbl::AllocChecker ac;
     auto observer = new (&ac)
-        PortObserver(type, handle, fbl::RefPtr<PortDispatcher>(this), get_lock(), key, signals);
+        PortObserver(options, handle, fbl::RefPtr<PortDispatcher>(this), get_lock(), key, signals);
     if (!ac.check()) {
         return ZX_ERR_NO_MEMORY;
     }
diff --git a/zircon/kernel/syscalls/object_wait.cc b/zircon/kernel/syscalls/object_wait.cc
index 34b761f..dab03a4 100644
--- a/zircon/kernel/syscalls/object_wait.cc
+++ b/zircon/kernel/syscalls/object_wait.cc
@@ -182,6 +182,10 @@
                                   uint64_t key, zx_signals_t signals, uint32_t options) {
     LTRACEF("handle %x\n", handle_value);
 
+    if ((options != 0u) && (options != ZX_WAIT_ASYNC_TIMESTAMP)) {
+        return ZX_ERR_INVALID_ARGS;
+    }
+
     auto up = ProcessDispatcher::GetCurrent();
 
     {
diff --git a/zircon/system/public/zircon/syscalls/port.h b/zircon/system/public/zircon/syscalls/port.h
index a501fd8..58e431b 100644
--- a/zircon/system/public/zircon/syscalls/port.h
+++ b/zircon/system/public/zircon/syscalls/port.h
@@ -14,6 +14,7 @@
 
 // zx_object_wait_async() options
 #define ZX_WAIT_ASYNC_ONCE          ((uint32_t)0u)
+#define ZX_WAIT_ASYNC_TIMESTAMP     ((uint32_t)1u)
 
 // packet types.  zx_port_packet_t::type
 #define ZX_PKT_TYPE_USER            ((uint8_t)0x00u)
@@ -65,7 +66,7 @@
     zx_signals_t trigger;
     zx_signals_t observed;
     uint64_t count;
-    uint64_t reserved0;
+    uint64_t timestamp;
     uint64_t reserved1;
 } zx_packet_signal_t;
 
diff --git a/zircon/system/ulib/async/test/wait_tests.cc b/zircon/system/ulib/async/test/wait_tests.cc
index 399842e..9b4f9aeb 100644
--- a/zircon/system/ulib/async/test/wait_tests.cc
+++ b/zircon/system/ulib/async/test/wait_tests.cc
@@ -14,7 +14,7 @@
     .trigger = dummy_trigger,
     .observed = ZX_USER_SIGNAL_0 | ZX_USER_SIGNAL_1,
     .count = 0u,
-    .reserved0 = 0u,
+    .timestamp = 0u,
     .reserved1 = 0u};
 
 class MockDispatcher : public async::DispatcherStub {
diff --git a/zircon/system/utest/core/port/ports.cc b/zircon/system/utest/core/port/ports.cc
index eb69bb7..d00d58a 100644
--- a/zircon/system/utest/core/port/ports.cc
+++ b/zircon/system/utest/core/port/ports.cc
@@ -21,14 +21,14 @@
 
 TEST(PortTest, QueueNullPtrReturnsInvalidArgs) {
     zx::port port;
-    ASSERT_OK(zx::port::create(0, &port));
+    ASSERT_OK(zx::port::create(0u, &port));
 
     EXPECT_EQ(port.queue(nullptr), ZX_ERR_INVALID_ARGS);
 }
 
 TEST(PortTest, QueueWaitVerifyUserPacket) {
     zx::port port;
-    ASSERT_OK(zx::port::create(0, &port));
+    ASSERT_OK(zx::port::create(0u, &port));
 
     constexpr zx_port_packet_t kPortUserPacket = {
         12ull,
@@ -52,7 +52,7 @@
 
 TEST(PortTest, PortTimeout) {
     zx::port port;
-    ASSERT_OK(zx::port::create(0, &port));
+    ASSERT_OK(zx::port::create(0u, &port));
 
     zx_port_packet_t packet = {};
 
@@ -61,7 +61,7 @@
 
 TEST(PortTest, QueueAndClose) {
     zx::port port;
-    ASSERT_OK(zx::port::create(0, &port));
+    ASSERT_OK(zx::port::create(0u, &port));
 
     constexpr zx_port_packet_t kPortUserPacket = {
         1ull,
@@ -75,7 +75,7 @@
 
 TEST(PortTest, QueueTooMany) {
     zx::port port;
-    ASSERT_OK(zx::port::create(0, &port));
+    ASSERT_OK(zx::port::create(0u, &port));
     constexpr uint32_t kNumberPortQueueSize = 2049;
 
     constexpr zx_port_packet_t kPortUserPacket = {
@@ -101,7 +101,7 @@
     constexpr uint64_t kEventKey = 6567;
 
     zx::port port;
-    ASSERT_OK(zx::port::create(0, &port));
+    ASSERT_OK(zx::port::create(0u, &port));
 
     zx::channel ch[2];
     ASSERT_OK(zx::channel::create(0u, &ch[0], &ch[1]));
@@ -116,7 +116,7 @@
     constexpr uint64_t kEventKey = 6567;
 
     zx::port port;
-    ASSERT_OK(zx::port::create(0, &port));
+    ASSERT_OK(zx::port::create(0u, &port));
 
     zx::channel ch[2];
     ASSERT_OK(zx::channel::create(0u, &ch[0], &ch[1]));
@@ -192,7 +192,7 @@
 
 TEST(PortTest, EventAsyncSignalWaitSingle) {
     zx::port port;
-    ASSERT_OK(zx::port::create(0, &port));
+    ASSERT_OK(zx::port::create(0u, &port));
 
     zx::event event;
     ASSERT_OK(zx::event::create(0u, &event));
@@ -220,7 +220,7 @@
 
 TEST(PortTest, AsyncWaitEventRepeat) {
     zx::port port;
-    ASSERT_OK(zx::port::create(0, &port));
+    ASSERT_OK(zx::port::create(0u, &port));
 
     zx::event event;
     ASSERT_OK(zx::event::create(0u, &event));
@@ -261,7 +261,7 @@
     constexpr size_t kEventCount = 16 * 1024 + 1;
 
     zx::port port;
-    ASSERT_OK(zx::port::create(0, &port));
+    ASSERT_OK(zx::port::create(0u, &port));
 
     zx::event event[kEventCount];
     for (size_t i = 0; i < kEventCount; i++) {
@@ -292,7 +292,7 @@
 // invalid option.
 TEST(PortTest, AsyncWaitInvalidOption) {
     zx::port port;
-    ASSERT_OK(zx::port::create(0, &port));
+    ASSERT_OK(zx::port::create(0u, &port));
     zx::event event;
     ASSERT_OK(zx::event::create(0u, &event));
 
@@ -315,7 +315,7 @@
     ch[0].reset();
 
     zx::port port;
-    ASSERT_OK(zx::port::create(0, &port));
+    ASSERT_OK(zx::port::create(0u, &port));
 
     ASSERT_OK(ch[1].wait_async(port, kEventKey, ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED,
               ZX_WAIT_ASYNC_ONCE));
@@ -346,7 +346,7 @@
     zx::port port;
     zx::event event;
 
-    ASSERT_OK(zx::port::create(0, &port));
+    ASSERT_OK(zx::port::create(0u, &port));
     ASSERT_OK(zx::event::create(0u, &event));
 
     // Notice repeated key below.
@@ -391,7 +391,7 @@
 TEST(PortTest, CancelEventKeyAfter) {
     zx::port port;
 
-    ASSERT_OK(zx::port::create(0, &port));
+    ASSERT_OK(zx::port::create(0u, &port));
 
     const uint64_t keys[] = {128u, 3u, 3u};
     zx::event ev[fbl::count_of(keys)];
@@ -447,7 +447,7 @@
         } while (--count);
     };
 
-    ASSERT_OK(zx::port::create(0, &port));
+    ASSERT_OK(zx::port::create(0u, &port));
     ASSERT_OK(zx::event::create(0u, &event));
 
     std::thread port_waiters[kNumPortWaiterThreads];
@@ -470,6 +470,56 @@
     }
 }
 
+TEST(PortTest, Timestamp) {
+    // Test that the timestamp feature returns reasonable numbers. We use
+    // a single thread so the numbers should be nanosecond-grade reliable.
+    zx::port port;
+    zx::event event[2];
+    ASSERT_OK(zx::port::create(0u, &port));
+    ASSERT_OK(zx::event::create(0u, &event[0]));
+    ASSERT_OK(zx::event::create(0u, &event[1]));
+
+    ASSERT_OK(event[0].wait_async(port, 1, ZX_EVENT_SIGNALED, ZX_WAIT_ASYNC_TIMESTAMP));
+    ASSERT_OK(event[1].wait_async(port, 2, ZX_EVENT_SIGNALED, 0u));
+
+    auto before = zx::clock::get_monotonic();
+    ASSERT_OK(event[0].signal(0u, ZX_EVENT_SIGNALED));
+    auto after = zx::clock::get_monotonic();
+    ASSERT_OK(event[1].signal(0u, ZX_EVENT_SIGNALED));
+
+    zx_port_packet_t packet[2];
+    ASSERT_OK(port.wait(zx::time::infinite(), &packet[0]));
+    ASSERT_OK(port.wait(zx::time::infinite(), &packet[1]));
+
+    ASSERT_EQ(packet[0].signal.trigger, ZX_EVENT_SIGNALED);
+    ASSERT_EQ(packet[1].signal.trigger, ZX_EVENT_SIGNALED);
+
+    EXPECT_LE(before, zx::time(packet[0].signal.timestamp));
+    EXPECT_GE(after, zx::time(packet[0].signal.timestamp));
+
+    EXPECT_EQ(0u, packet[1].signal.timestamp);
+
+    // Now test the same using event[0] in place of event[1].
+    ASSERT_OK(event[0].signal(ZX_EVENT_SIGNALED, 0u));
+    ASSERT_OK(event[1].signal(ZX_EVENT_SIGNALED, 0u));
+
+    ASSERT_OK(event[1].wait_async(port, 1, ZX_EVENT_SIGNALED, ZX_WAIT_ASYNC_TIMESTAMP));
+    ASSERT_OK(event[0].wait_async(port, 2, ZX_EVENT_SIGNALED, 0u));
+
+    ASSERT_OK(event[0].signal(0u, ZX_EVENT_SIGNALED));
+    before = zx::clock::get_monotonic();
+    ASSERT_OK(event[1].signal(0u, ZX_EVENT_SIGNALED));
+    after = zx::clock::get_monotonic();
+
+    ASSERT_OK(port.wait(zx::time::infinite(), &packet[0]));
+    ASSERT_OK(port.wait(zx::time::infinite(), &packet[1]));
+
+    EXPECT_LE(before, zx::time(packet[1].signal.timestamp));
+    EXPECT_GE(after, zx::time(packet[1].signal.timestamp));
+
+    EXPECT_EQ(0u, packet[0].signal.timestamp);
+}
+
 constexpr uint32_t kStressCount = 20000;
 constexpr uint32_t kSleeps[] = { 0, 10, 2, 0, 15, 0};
 
@@ -530,7 +580,7 @@
 
     zx::port port;
     zx::event event;
-    ASSERT_OK(zx::port::create(0, &port));
+    ASSERT_OK(zx::port::create(0u, &port));
     ASSERT_OK(zx::event::create(0u, &event));
     zx_status_t waiter_status = ZX_ERR_INTERNAL;
     std::atomic<bool> keep_running(true);
@@ -614,7 +664,7 @@
     };
 
     zx::port port;
-    ASSERT_OK(zx::port::create(0, &port));
+    ASSERT_OK(zx::port::create(0u, &port));
 
     constexpr unsigned kNumSignalers = 4;
     std::thread signalers[kNumSignalers];