summaryrefslogtreecommitdiff
path: root/cras
diff options
context:
space:
mode:
authorJohn Muir <muirj@google.com>2016-07-01 12:35:07 -0700
committerchrome-bot <chrome-bot@chromium.org>2016-07-24 23:11:26 -0700
commit8494a53dede2d7bc0f74c7d68aa1c17427c1154f (patch)
treec888406b8b633d07ac9faf3d959af930472a18ba /cras
parentc073926a09e368ea14596deb25b3ee4c1b47990b (diff)
downloadadhd-8494a53dede2d7bc0f74c7d68aa1c17427c1154f.tar.gz
CRAS: Add cras_poll which updates the timeout parameter.
The ppoll function is used in a few places in CRAS, and in most cases we want to call ppoll again with the new timeout. This function automatically modifies the value of the timeout parameter with the time remaining following the poll operation. BUG=None TEST=New test added to util_unittest for this; not yet used. Change-Id: I4e439dd165665693abb1b0d962291dbd0a809ad5 Reviewed-on: https://chromium-review.googlesource.com/359936 Commit-Ready: John Muir <muirj@google.com> Tested-by: John Muir <muirj@google.com> Reviewed-by: Dylan Reid <dgreid@chromium.org>
Diffstat (limited to 'cras')
-rw-r--r--cras/src/common/cras_util.c49
-rw-r--r--cras/src/common/cras_util.h33
-rw-r--r--cras/src/tests/util_unittest.cc67
3 files changed, 141 insertions, 8 deletions
diff --git a/cras/src/common/cras_util.c b/cras/src/common/cras_util.c
index 147134f2..ee214ccc 100644
--- a/cras/src/common/cras_util.c
+++ b/cras/src/common/cras_util.c
@@ -3,20 +3,25 @@
* found in the LICENSE file.
*/
+#define _GNU_SOURCE /* For ppoll() */
+
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
+#include <poll.h>
#include <sched.h>
#include <stdlib.h>
#include <string.h>
+#include <syslog.h>
#include <sys/param.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <sys/syscall.h>
-#include <syslog.h>
#include <sys/types.h>
#include <unistd.h>
+#include "cras_util.h"
+
int cras_set_rt_scheduling(int rt_lim)
{
struct rlimit rl;
@@ -162,3 +167,45 @@ exit:
free(control);
return rc;
}
+
+int cras_poll(struct pollfd *fds, nfds_t nfds, struct timespec *timeout,
+ const sigset_t *sigmask)
+{
+ struct timespec now;
+ struct timespec future;
+ struct pollfd *fd = fds;
+ nfds_t i;
+ int rc = 0;
+
+ if (timeout) {
+ /* Treat a negative timeout as valid (but timed-out) since
+ * this function could update timeout to have negative tv_sec
+ * or tv_nsec. */
+ if (timeout->tv_sec < 0 || timeout->tv_nsec < 0)
+ return -ETIMEDOUT;
+ rc = clock_gettime(CLOCK_MONOTONIC_RAW, &future);
+ if (rc < 0)
+ return -errno;
+ add_timespecs(&future, timeout);
+ }
+
+ for (i = 0; i < nfds; i++) {
+ fd->revents = 0;
+ fd++;
+ }
+
+ rc = ppoll(fds, nfds, timeout, sigmask);
+ if (rc == 0 && timeout) {
+ rc = -ETIMEDOUT;
+ }
+ else if (rc < 0) {
+ rc = -errno;
+ }
+
+ if (timeout) {
+ clock_gettime(CLOCK_MONOTONIC_RAW, &now);
+ subtract_timespecs(&future, &now, timeout);
+ }
+
+ return rc;
+}
diff --git a/cras/src/common/cras_util.h b/cras/src/common/cras_util.h
index bcb60113..457721df 100644
--- a/cras/src/common/cras_util.h
+++ b/cras/src/common/cras_util.h
@@ -156,6 +156,13 @@ static inline unsigned int timespec_to_ms(const struct timespec *ts)
return ts->tv_sec * 1000 + (ts->tv_nsec + 999999) / 1000000;
}
+/* Convert milliseconds to timespec. */
+static inline void ms_to_timespec(time_t milliseconds, struct timespec *ts)
+{
+ ts->tv_sec = milliseconds / 1000;
+ ts->tv_nsec = (milliseconds % 1000) * 1000000;
+}
+
/* Calculates frames since time beg. */
static inline unsigned int cras_frames_since_time(const struct timespec *beg,
unsigned int rate)
@@ -170,6 +177,32 @@ static inline unsigned int cras_frames_since_time(const struct timespec *beg,
return cras_time_to_frames(&time_since, rate);
}
+/* Poll on the given file descriptors.
+ *
+ * See ppoll(). This implementation changes the value of timeout to the
+ * remaining time, and returns negative error codes on error.
+ *
+ * Args:
+ * fds - Array of pollfd structures.
+ * nfds - Number of pollfd structures.
+ * timeout - Timeout time updated upon return with remaining time. The
+ * timeout value may be updated to become invalid (negative
+ * tv_nsec or negative tv_sec). In that case, -tv_nsec is the
+ * number of nanoseconds by which the polling exceeded the
+ * supplied timeout. The function immediately returns with
+ * -ETIMEOUT if tv_nsec is negative, simplifying loops that
+ * rely on the returned remaining timeout.
+ * sigmask - Signal mask while in the poll.
+ *
+ * Returns:
+ * Positive when file decriptors are ready.
+ * Zero if no file descriptors are ready and timeout is NULL.
+ * -ETIMEDOUT when no file descriptors are ready and a timeout specified.
+ * Other negative error codes specified in the ppoll() man page.
+ */
+int cras_poll(struct pollfd *fds, nfds_t nfds, struct timespec *timeout,
+ const sigset_t *sigmask);
+
#ifdef __cplusplus
} /* extern "C" */
#endif
diff --git a/cras/src/tests/util_unittest.cc b/cras/src/tests/util_unittest.cc
index b0a7efee..3dbc026f 100644
--- a/cras/src/tests/util_unittest.cc
+++ b/cras/src/tests/util_unittest.cc
@@ -3,15 +3,17 @@
// found in the LICENSE file.
#include <gtest/gtest.h>
+#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
+#include <vector>
#include "cras_util.h"
namespace {
-static struct timespec time_now;
+static std::vector<struct timespec> time_now;
TEST(Util, SendRecvTwoFileDescriptors) {
int fd[2];
@@ -243,28 +245,79 @@ TEST(Util, TimespecToMs) {
}
TEST(Util, FramesSinceTime) {
- struct timespec t;
+ struct timespec t, tn;
unsigned int frames;
t.tv_sec = 0;
t.tv_nsec = 500000000;
- time_now.tv_sec = 2;
- time_now.tv_nsec = 0;
+ tn.tv_sec = 2;
+ tn.tv_nsec = 0;
+ time_now.push_back(tn);
frames = cras_frames_since_time(&t, 48000);
EXPECT_EQ(72000, frames);
- time_now.tv_sec = 0;
- time_now.tv_nsec = 0;
+ tn.tv_sec = 0;
+ time_now.push_back(tn);
frames = cras_frames_since_time(&t, 48000);
EXPECT_EQ(0, frames);
}
+// Test cras_poll().
+TEST(Util, CrasPoll) {
+ int pipe_fds[2];
+ struct pollfd poll_fd;
+ std::string output;
+ struct timespec timeout;
+ char buf[256];
+
+ ASSERT_EQ(0, pipe(pipe_fds));
+ poll_fd.fd = pipe_fds[0];
+ poll_fd.events = POLLIN;
+ ASSERT_NE(0, poll_fd.fd >= 0);
+
+ // Simple poll.
+ output = "Hello";
+ EXPECT_EQ(output.size() + 1,
+ write(pipe_fds[1], output.c_str(), output.size() + 1));
+ EXPECT_EQ(1, cras_poll(&poll_fd, 1, NULL, NULL));
+ ASSERT_EQ(static_cast<ssize_t>(output.size() + 1),
+ read(pipe_fds[0], buf, sizeof(buf)));
+ EXPECT_EQ(0, strcmp(output.c_str(), buf));
+
+ // Negative time.
+ timeout.tv_sec = 0;
+ timeout.tv_nsec = -10000000;
+ EXPECT_EQ(-ETIMEDOUT, cras_poll(&poll_fd, 1, &timeout, NULL));
+ timeout.tv_sec = -1;
+ timeout.tv_nsec = 10000000;
+ EXPECT_EQ(-ETIMEDOUT, cras_poll(&poll_fd, 1, &timeout, NULL));
+
+ // Timeout.
+ timeout.tv_sec = 0;
+ timeout.tv_nsec = 0;
+ time_now.push_back(timeout);
+ timeout.tv_nsec = 1100000;
+ time_now.push_back(timeout);
+ timeout.tv_nsec = 1000000;
+ EXPECT_EQ(-ETIMEDOUT, cras_poll(&poll_fd, 1, &timeout, NULL));
+ EXPECT_EQ(timeout.tv_nsec, -100000);
+
+ EXPECT_EQ(0, close(pipe_fds[0]));
+ EXPECT_EQ(0, close(pipe_fds[1]));
+}
+
/* Stubs */
extern "C" {
int clock_gettime(clockid_t clk_id, struct timespec *tp) {
- *tp = time_now;
+ std::vector<struct timespec>::iterator i = time_now.begin();
+ if (i != time_now.end()) {
+ *tp = *i;
+ time_now.erase(i);
+ }
+ else
+ memset(tp, 0, sizeof(*tp));
return 0;
}