[LTP] [PATCH v4] aio_cancel_7-1: Rewrite test

Li Wang liwang@redhat.com
Wed Sep 17 11:26:09 CEST 2025


On Mon, Sep 15, 2025 at 10:48 PM Martin Doucha <mdoucha@suse.cz> wrote:

> The test schedules multiple async writes into a file and then hopes that
> at least one will block long enough that it can be canceled
> before completion. Use a socket pair instead of a file to force async
> writes to block indefinitely and make sure at least one can be canceled.
> Then wait for one of the writes to actually block on full socket buffer.
> This fixes a race condition where aio_cancel() could be called while
> no write is actually in progress (before the first write, after the last
> one or between two writes) and the test would fail.
>
> Also rewrite result checks to verify that all writes before the blocked
> one actually completed and all writes after the blocked one were cancelled.
>
> Add cleanup helper function that will flush socket buffers, free allocated
> memory and close the sockets. Also make setup and cleanup simpler
> by statically allocating the aiocb structure array.
>
> Signed-off-by: Martin Doucha <mdoucha@suse.cz>
>

Reviewed-by: Li Wang <liwang@redhat.com>

---
>
> Changes since v1: Rebased to current master.
>
> Changes since v2:
> - Changed argsize from unsigned int to socklen_t
> - Improved test cleanup using a helper function
>
> Changes since v3:
> - Update test description and copyright header
> - Statically allocate aiocb array to avoid unnecessary calloc()
> - Wait for write #2 to actually block
> - Rewrite result checks to eliminate weird edge cases
> - Flush socket buffers during cleanup
>
>  .../conformance/interfaces/aio_cancel/7-1.c   | 209 ++++++++++--------
>  1 file changed, 121 insertions(+), 88 deletions(-)
>
> diff --git
> a/testcases/open_posix_testsuite/conformance/interfaces/aio_cancel/7-1.c
> b/testcases/open_posix_testsuite/conformance/interfaces/aio_cancel/7-1.c
> index 34b263245..a8a6e4d9e 100644
> ---
> a/testcases/open_posix_testsuite/conformance/interfaces/aio_cancel/7-1.c
> +++
> b/testcases/open_posix_testsuite/conformance/interfaces/aio_cancel/7-1.c
> @@ -1,5 +1,6 @@
>  /*
>   * Copyright (c) 2004, Bull SA. All rights reserved.
> + * Copyright (c) 2025 SUSE LLC
>   * Created by:  Laurent.Vivier@bull.net
>   * This file is licensed under the GPL license.  For the full content
>   * of this license, see the COPYING file at the top level of this
> @@ -14,145 +15,177 @@
>   *
>   * method:
>   *
> - *     queue a lot of aio_write() to a given file descriptor
> - *     then cancel all operation belonging to this file descriptor
> + *     queue multiple aio_write()s to a given socket
> + *     wait for a specific task to start and block on full socket buffer
> + *     then cancel all operations belonging to this socket
>   *     check result of each operation:
> - *     - if aio_error() is EINPROGRESS and aio_cancel() is not
> AIO_NOTCANCELED
> - *       result is failed
> - *     - if aio_error() is succes (0) and aio_cancel() is AIO_NOTCANCELED
> - *       result is susccess
> - *     - otherwise result is unresolved
> - *
> + *     - aio_cancel() must return AIO_NOTCANCELED
> + *     - aio_error() must return 0 for writes before the blocked task
> + *     - aio_error() must return EINPROGRESS for the blocked task
> + *     - aio_error() must return ECANCELED for writes after the blocked
> task
> + *     If any of the above conditions is not met, fail the test.
> + *     Otherwise pass.
>   */
>
>  #include <stdio.h>
>  #include <sys/types.h>
>  #include <unistd.h>
>  #include <sys/stat.h>
> -#include <fcntl.h>
>  #include <string.h>
>  #include <errno.h>
>  #include <stdlib.h>
> +#include <time.h>
>  #include <aio.h>
> +#include <sys/socket.h>
>
>  #include "posixtest.h"
> -#include "tempfile.h"
>
>  #define TNAME "aio_cancel/7-1.c"
>
> -#define BUF_NB         128
> -#define BUF_SIZE       1024
> +#define BUF_NB         8
> +#define BLOCKED_TASK   2
> +
> +static int fds[2];
> +static struct aiocb aiocb[BUF_NB];
> +
> +static void cleanup(void)
> +{
> +       int i, ret;
> +
> +       for (i = 0; i < BUF_NB; i++) {
> +               if (!aiocb[i].aio_buf)
> +                       break;
> +
> +               ret = aio_error(&aiocb[i]);
> +
> +               /* flush written data from the socket */
> +               if (ret == 0 || ret == EINPROGRESS) {
> +                       read(fds[1], (void *)aiocb[i].aio_buf,
> +                               aiocb[i].aio_nbytes);
> +               }
> +
> +               free((void *)aiocb[i].aio_buf);
> +       }
> +
> +       close(fds[0]);
> +       close(fds[1]);
> +}
>
>  int main(void)
>  {
> -       char tmpfname[PATH_MAX];
> -       int fd;
> -       struct aiocb *aiocb[BUF_NB];
>         int i;
> -       int in_progress;
>         int gret;
> +       int bufsize;
> +       int exp_ret;
> +       socklen_t argsize = sizeof(bufsize);
> +       const struct timespec sleep_ts = { .tv_sec = 0, .tv_nsec =
> 10000000 };
>
>         if (sysconf(_SC_ASYNCHRONOUS_IO) < 200112L)
>                 return PTS_UNSUPPORTED;
>
> -       PTS_GET_TMP_FILENAME(tmpfname, "pts_aio_cancel_7_1");
> -       unlink(tmpfname);
> -       fd = open(tmpfname, O_CREAT | O_RDWR | O_EXCL, S_IRUSR | S_IWUSR);
> -       if (fd == -1) {
> -               printf(TNAME " Error at open(): %s\n", strerror(errno));
> +       gret = socketpair(AF_UNIX, SOCK_DGRAM, 0, fds);
> +       if (gret == -1) {
> +               printf(TNAME " Error creating sockets(): %s\n",
> +                       strerror(errno));
> +               return PTS_UNRESOLVED;
> +       }
> +
> +       gret = getsockopt(fds[0], SOL_SOCKET, SO_SNDBUF, &bufsize,
> &argsize);
> +       if (gret == -1) {
> +               printf(TNAME " Error reading socket buffer size: %s\n",
> +                       strerror(errno));
> +               cleanup();
>                 return PTS_UNRESOLVED;
>         }
>
> -       unlink(tmpfname);
> +       /* Socket buffer size is twice the maximum message size */
> +       bufsize /= 2;
>
>         /* create AIO req */
>         for (i = 0; i < BUF_NB; i++) {
> -               aiocb[i] = calloc(1, sizeof(struct aiocb));
> +               aiocb[i].aio_fildes = fds[0];
> +               aiocb[i].aio_buf = malloc(bufsize);
>
> -               if (aiocb[i] == NULL) {
> +               if (aiocb[i].aio_buf == NULL) {
>                         printf(TNAME " Error at malloc(): %s\n",
>                                strerror(errno));
> -                       close(fd);
> +                       cleanup();
>                         return PTS_UNRESOLVED;
>                 }
>
> -               aiocb[i]->aio_fildes = fd;
> -               aiocb[i]->aio_buf = malloc(BUF_SIZE);
> +               aiocb[i].aio_nbytes = bufsize;
> +               aiocb[i].aio_offset = 0;
> +               aiocb[i].aio_sigevent.sigev_notify = SIGEV_NONE;
>
> -               if (aiocb[i]->aio_buf == NULL) {
> -                       printf(TNAME " Error at malloc(): %s\n",
> -                              strerror(errno));
> -                       close(fd);
> -                       return PTS_UNRESOLVED;
> -               }
> -
> -               aiocb[i]->aio_nbytes = BUF_SIZE;
> -               aiocb[i]->aio_offset = 0;
> -               aiocb[i]->aio_sigevent.sigev_notify = SIGEV_NONE;
> -
> -               if (aio_write(aiocb[i]) == -1) {
> +               if (aio_write(&aiocb[i]) == -1) {
>                         printf(TNAME " loop %d: Error at aio_write():
> %s\n",
>                                i, strerror(errno));
> -                       close(fd);
> +                       cleanup();
>                         return PTS_FAIL;
>                 }
>         }
>
> -       /* try to cancel all
> -        * we hope to have enough time to cancel at least one
> -        */
> -       gret = aio_cancel(fd, NULL);
> +       /* wait for write #2 to start and get blocked by full socket
> buffer */
> +       for (i = 0; i < 1000; i++) {
> +               gret = aio_error(&aiocb[BLOCKED_TASK - 1]);
> +               nanosleep(&sleep_ts, NULL);
> +
> +               if (gret <= 0)
> +                       break;
> +       }
> +
> +       if (gret) {
> +               printf(TNAME " Task #%d failed to complete: %s\n",
> +                       BLOCKED_TASK - 1, strerror(gret == -1 ? errno :
> gret));
> +               cleanup();
> +               return PTS_FAIL;
> +       }
> +
> +       /* try to cancel all */
> +       gret = aio_cancel(fds[0], NULL);
>         if (gret == -1) {
>                 printf(TNAME " Error at aio_cancel(): %s\n",
> strerror(errno));
> -               close(fd);
> +               cleanup();
>                 return PTS_FAIL;
>         }
>
> -       do {
> -               in_progress = 0;
> -               for (i = 0; i < BUF_NB; i++) {
> -                       int ret = aio_error(aiocb[i]);
> -
> -                       switch (ret) {
> -                       case -1:
> -                               printf(TNAME " Error at aio_error(): %s\n",
> -                                      strerror(errno));
> -                               close(fd);
> -                               return PTS_FAIL;
> -                               break;
> -                       case EINPROGRESS:
> -                               /* at this point, all operations should be:
> -                                *    canceled
> -                                * or in progress
> -                                *    with aio_cancel() == AIO_NOTCANCELED
> -                                */
> -                               if (gret != AIO_NOTCANCELED) {
> -                                       printf(TNAME
> -                                              " Error at aio_error():
> %s\n",
> -                                              strerror(errno));
> -                                       close(fd);
> -                                       return PTS_FAIL;
> -                               }
> -
> -                               in_progress = 1;
> -                               break;
> -                       case 0:
> -                               /* we seek one not canceled and check why.
> -                                * (perhaps) it has not been canceled
> -                                * because it was in progress
> -                                * during the cancel operation
> -                                */
> -                               if (gret == AIO_NOTCANCELED) {
> -                                       printf("Test PASSED\n");
> -                                       close(fd);
> -                                       return PTS_PASS;
> -                               }
> -                               break;
> +       if (gret != AIO_NOTCANCELED) {
> +               printf(TNAME " Unexpected aio_cancel() return value: %s\n",
> +                       strerror(gret));
> +               cleanup();
> +               return PTS_FAIL;
> +       }
> +
> +       /*
> +        * check write results, expected values:
> +        * - 0 for the first two writes
> +        * - EINPROGRESS for the third
> +        * - ECANCELED for the rest
> +        */
> +       for (i = 0, exp_ret = 0; i < BUF_NB; i++) {
> +               int ret = aio_error(&aiocb[i]);
> +
> +               if (i == BLOCKED_TASK) {
> +                       if (ret == EINPROGRESS) {
> +                               exp_ret = ECANCELED;
> +                               continue;
>                         }
> +
> +                       printf(TNAME " Bad task #%d result: %s "
> +                               "(expected EINPROGRESS)\n",
> +                               i, strerror(ret));
> +                       cleanup();
> +                       return PTS_FAIL;
>                 }
> -       } while (in_progress);
>
> -       close(fd);
> +               if (ret != exp_ret) {
> +                       printf(TNAME " Bad task #%d result: %s (expected
> %s)\n",
> +                               i, strerror(ret), strerror(exp_ret));
> +                       cleanup();
> +                       return PTS_FAIL;
> +               }
> +       }
>
> -       return PTS_UNRESOLVED;
> +       cleanup();
>

An explicit PASS message is required here, otherwise, nothing will be
printed
when the test succeeds.

    printf("Test PASSED\n");

+       return PTS_PASS;
>  }
> --
> 2.51.0
>
>
> --
> Mailing list info: https://lists.linux.it/listinfo/ltp
>
>

-- 
Regards,
Li Wang


More information about the ltp mailing list