[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..8e8d4231 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];