[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];