From cd2571b0fcd4ba6b73ec23576e4197fb6552bbe8 Mon Sep 17 00:00:00 2001 From: Joe Orton Date: Fri, 10 May 2024 11:22:11 +0100 Subject: [PATCH] Add support for pipe2() on Linux. * configure.in: Test for working pipe2(). * file_io/unix/pipe.c (file_pipe_create): Use pipe2(,O_NONBLOCK) if APR_FULL_NONBLOCK is requested. * test/testpipe.c (nonblock_pipe): New test. --- configure.in | 16 ++++++++++++++++ file_io/unix/pipe.c | 30 +++++++++++++++++++++++------- test/testpipe.c | 39 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 7 deletions(-) diff --git a/configure.in b/configure.in index 1b8332c271..f7d7eb518b 100644 --- a/configure.in +++ b/configure.in @@ -1210,6 +1210,22 @@ if test "$apr_cv_dup3" = "yes"; then AC_DEFINE([HAVE_DUP3], 1, [Define if dup3 function is supported]) fi +# test for pipe2 +AC_CACHE_CHECK([for pipe2 support], [apr_cv_pipe2], +[AC_TRY_RUN([ +#include +#include + +int main(int argc, const char **argv) +{ + int fds[2]; + return pipe2(fds, O_NONBLOCK) == -1; +}], [apr_cv_pipe2=yes], [apr_cv_pipe2=no], [apr_cv_pipe2=no])]) + +if test "$apr_cv_pipe2" = "yes"; then + AC_DEFINE([HAVE_PIPE2], 1, [Define if pipe2 function is supported]) +fi + # Test for accept4(). Create a non-blocking socket, bind it to # an unspecified port & address (kernel picks), and attempt to # call accept4() on it. If the syscall is wired up (i.e. the diff --git a/file_io/unix/pipe.c b/file_io/unix/pipe.c index 6c603ec9a3..68c1b8951b 100644 --- a/file_io/unix/pipe.c +++ b/file_io/unix/pipe.c @@ -182,10 +182,25 @@ static apr_status_t file_pipe_create(apr_file_t **in, apr_pool_t *pool_in, apr_pool_t *pool_out) { - int filedes[2]; + int filedes[2], fd_blocking; + apr_interval_time_t fd_timeout; - if (pipe(filedes) == -1) { - return errno; +#ifdef HAVE_PIPE2 + if (blocking == APR_FULL_NONBLOCK) { + if (pipe2(filedes, O_NONBLOCK) == -1) { + return errno; + } + fd_blocking = BLK_OFF; + fd_timeout = 0; + } + else +#endif + { + if (pipe(filedes) == -1) { + return errno; + } + fd_blocking = BLK_ON; + fd_timeout = -1; } (*in) = (apr_file_t *)apr_pcalloc(pool_in, sizeof(apr_file_t)); @@ -194,8 +209,8 @@ static apr_status_t file_pipe_create(apr_file_t **in, (*in)->is_pipe = 1; (*in)->fname = NULL; (*in)->buffered = 0; - (*in)->blocking = BLK_ON; - (*in)->timeout = -1; + (*in)->blocking = fd_blocking; + (*in)->timeout = fd_timeout; (*in)->ungetchar = -1; (*in)->flags = APR_INHERIT; #if APR_HAS_THREADS @@ -210,9 +225,9 @@ static apr_status_t file_pipe_create(apr_file_t **in, (*out)->is_pipe = 1; (*out)->fname = NULL; (*out)->buffered = 0; - (*out)->blocking = BLK_ON; + (*out)->blocking = fd_blocking; (*out)->flags = APR_INHERIT; - (*out)->timeout = -1; + (*out)->timeout = fd_timeout; #if APR_HAS_THREADS (*out)->thlock = NULL; #endif @@ -234,6 +249,7 @@ static apr_status_t file_pipe_create(apr_file_t **in, apr_file_pipe_timeout_set(*in, 0); break; default: + /* These are noops for the pipe2() case */ apr_file_pipe_timeout_set(*out, 0); apr_file_pipe_timeout_set(*in, 0); break; diff --git a/test/testpipe.c b/test/testpipe.c index 2f9dafbfa5..0e95a0f13c 100644 --- a/test/testpipe.c +++ b/test/testpipe.c @@ -220,6 +220,42 @@ static void wait_pipe(abts_case *tc, void *data) APR_ASSERT_SUCCESS(tc, "Wait for pipe failed", rv); } +static void nonblock_pipe(abts_case *tc, void *data) +{ + apr_status_t rv; + apr_interval_time_t delay; + char buf[8192]; + apr_size_t nbytes; + unsigned iter; + + rv = apr_file_pipe_create_ex(&readp, &writep, APR_FULL_NONBLOCK, p); + APR_ASSERT_SUCCESS(tc, "Couldn't create pipe", rv); + + rv = apr_file_pipe_timeout_get(readp, &delay); + APR_ASSERT_SUCCESS(tc, "Couldn't set pipe timeout", rv); + ABTS_INT_EQUAL(tc, delay, 0); + + nbytes = sizeof buf; + rv = apr_file_read(readp, buf, &nbytes); + ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_EAGAIN(rv)); + + /* Fill the pipe buffer, although the pipe buffer size is + * platform-specific, write up to 8MiB. */ + nbytes = sizeof buf; + memset(buf, 'A', sizeof buf); + rv = APR_SUCCESS; + + for (iter = 0; iter < 1024 && rv == APR_SUCCESS; iter++) + rv = apr_file_write(writep, buf, &nbytes); + + ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_EAGAIN(rv)); + + nbytes = sizeof buf; + /* Reading should now work. */ + rv = apr_file_read(readp, buf, &nbytes); + APR_ASSERT_SUCCESS(tc, "Reading from pipe", rv); +} + abts_suite *testpipe(abts_suite *suite) { suite = ADD_SUITE(suite) @@ -234,6 +270,9 @@ abts_suite *testpipe(abts_suite *suite) abts_run_test(suite, test_pipe_writefull, NULL); abts_run_test(suite, close_pipe, NULL); abts_run_test(suite, wait_pipe, NULL); + abts_run_test(suite, close_pipe, NULL); + abts_run_test(suite, nonblock_pipe, NULL); + abts_run_test(suite, close_pipe, NULL); return suite; }