* tests/test-nonblocking-pipe.sh: New file.
* tests/test-nonblocking-pipe-main.c: New file.
* tests/test-nonblocking-pipe-child.c: New file.
* tests/test-nonblocking-pipe.h: New file.
* tests/test-nonblocking-writer.h: New file.
* tests/test-nonblocking-reader.h: New file.
* tests/test-nonblocking-misc.h: New file.
* modules/nonblocking-pipe-tests: New file.
* modules/nonblocking-tests (Depends-on): Add nonblocking-pipe-tests.
+2011-04-17 Bruno Haible <bruno@clisp.org>
+
+ nonblocking: Add tests for pipes.
+ * tests/test-nonblocking-pipe.sh: New file.
+ * tests/test-nonblocking-pipe-main.c: New file.
+ * tests/test-nonblocking-pipe-child.c: New file.
+ * tests/test-nonblocking-pipe.h: New file.
+ * tests/test-nonblocking-writer.h: New file.
+ * tests/test-nonblocking-reader.h: New file.
+ * tests/test-nonblocking-misc.h: New file.
+ * modules/nonblocking-pipe-tests: New file.
+ * modules/nonblocking-tests (Depends-on): Add nonblocking-pipe-tests.
+
2011-04-16 Bruno Haible <bruno@clisp.org>
gettext: Clarify the needed programmer actions.
--- /dev/null
+Files:
+tests/test-nonblocking-pipe.sh
+tests/test-nonblocking-pipe-main.c
+tests/test-nonblocking-pipe-child.c
+tests/test-nonblocking-pipe.h
+tests/test-nonblocking-writer.h
+tests/test-nonblocking-reader.h
+tests/test-nonblocking-misc.h
+tests/macros.h
+
+Depends-on:
+stdbool
+unistd
+nonblocking
+wait-process
+pipe-posix
+dup2
+environ
+posix_spawnp
+binary-io
+gettimeofday
+snprintf
+vsnprintf
+strerror
+ssize_t
+usleep
+read
+write
+
+configure.ac:
+
+Makefile.am:
+TESTS += test-nonblocking-pipe.sh
+check_PROGRAMS += test-nonblocking-pipe-main test-nonblocking-pipe-child
Depends-on:
close
pipe-posix
+nonblocking-pipe-tests
configure.ac:
--- /dev/null
+/* Test for nonblocking read and write.
+
+ Copyright (C) 2011 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* Whether to print debugging messages. */
+#define ENABLE_DEBUGGING 0
+
+/* Delay (in microseconds) to sleep when write() or read() returned -1 with
+ errno = EAGAIN. */
+#define SMALL_DELAY 10000
+
+/* Return a memory area, filled with the data to be transferred. */
+static unsigned char *
+init_data (size_t data_block_size)
+{
+ unsigned char *data;
+ unsigned int i;
+
+ data = (unsigned char *) malloc (2 * data_block_size);
+ ASSERT (data != NULL);
+
+ for (i = 0; i < 2 * data_block_size; i++)
+ data[i] = (unsigned char) (i * i + (7 * i) % 61 + 4);
+
+ return data;
+}
+
+#if ENABLE_DEBUGGING
+# include <stdarg.h>
+static int dbgfprintf (FILE *fp, const char *format, ...)
+ _GL_ATTRIBUTE_FORMAT_PRINTF (2, 3);
+static int
+dbgfprintf (FILE *fp, const char *format, ...)
+{
+ /* Accumulate the entire line in a buffer, so that the output on fp
+ is done atomically. */
+ char line[1024];
+ size_t line_len;
+ struct timeval current_time;
+ va_list args;
+ int ret;
+
+ line_len = 0;
+ gettimeofday (¤t_time, NULL);
+ ret = snprintf (line, sizeof (line), "%.6f ",
+ current_time.tv_sec + (double) current_time.tv_usec * 1e-6);
+ if (ret < 0)
+ return -1;
+ line_len = strlen (line);
+
+ va_start (args, format);
+ ret = vsnprintf (line + line_len, sizeof (line) - line_len, format, args);
+ va_end (args);
+ if (ret < 0)
+ return -1;
+ line_len += strlen (line + line_len);
+
+ ret = fwrite (line, 1, line_len, fp);
+
+ /* Make sure the debugging information is output, so that the order of the
+ messages reflects the timeline of events, and so that the output is not
+ lost if the program crashes afterwards (relevant on mingw). */
+ fflush (fp);
+ return ret;
+}
+#else
+# define dbgfprintf if (1) ; else fprintf
+#endif
+
+/* Return a textual description of the error code ERR, if FAILED is true.
+ Return an empty string if FAILED is false. */
+static const char *
+dbgstrerror (bool failed, int err)
+{
+ static char buf[256];
+ if (failed)
+ {
+ sprintf (buf, " %d %s", err, strerror (err));
+ return buf;
+ }
+ else
+ return "";
+}
+
+#define TIMING_DECLS \
+ struct timeval before_time; \
+ struct timeval after_time; \
+ double spent_time;
+#define START_TIMING \
+ gettimeofday (&before_time, NULL);
+#define END_TIMING \
+ gettimeofday (&after_time, NULL); \
+ spent_time = \
+ (after_time.tv_sec - before_time.tv_sec) \
+ + ((double) after_time.tv_usec - (double) before_time.tv_usec) * 1e-6;
--- /dev/null
+/* Child program invoked by test-nonblocking-pipe-main.
+
+ Copyright (C) 2011 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+#include "binary-io.h"
+
+#include "macros.h"
+#include "test-nonblocking-pipe.h"
+#define PROG_ROLE "child"
+#include "test-nonblocking-reader.h"
+
+int
+main (int argc, char *argv[])
+{
+ int test = atoi (argv[1]);
+
+ /* Close unused file descriptors. */
+ close (STDOUT_FILENO);
+
+ /* STDIN_FILENO was created as binary in the parent process. But since an
+ fd's mode is stored in the process, not in the kernel, on native Windows
+ we need to set it as binary in the child process again. */
+ SET_BINARY (STDIN_FILENO);
+
+ main_reader_loop (test, PIPE_DATA_BLOCK_SIZE, STDIN_FILENO);
+
+ return 0;
+}
--- /dev/null
+/* Test for nonblocking read and write on pipes.
+
+ Copyright (C) 2011 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+# include <process.h>
+#else
+# include <spawn.h>
+#endif
+
+#include "nonblocking.h"
+#include "wait-process.h"
+
+#include "macros.h"
+#include "test-nonblocking-pipe.h"
+#define PROG_ROLE "main"
+#include "test-nonblocking-writer.h"
+
+int
+main (int argc, char *argv[])
+{
+ const char *child_path = argv[1];
+ int test = atoi (argv[2]);
+ int fd[2];
+ int child;
+ int exitcode;
+
+ /* Create a pipe. */
+ ASSERT (pipe (fd) >= 0);
+
+ /* Map fd[0] to STDIN_FILENO and fd[1] to STDOUT_FILENO, because on Windows,
+ the only three file descriptors that are inherited by child processes are
+ STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO. */
+ if (fd[0] != STDIN_FILENO)
+ {
+ ASSERT (dup2 (fd[0], STDIN_FILENO) >= 0);
+ close (fd[0]);
+ }
+ if (fd[1] != STDOUT_FILENO)
+ {
+ ASSERT (dup2 (fd[1], STDOUT_FILENO) >= 0);
+ close (fd[1]);
+ }
+
+ /* Prepare the file descriptors. */
+ if (test & 1)
+ ASSERT (set_nonblocking_flag (STDOUT_FILENO, true) >= 0);
+ if (test & 2)
+ ASSERT (set_nonblocking_flag (STDIN_FILENO, true) >= 0);
+
+ /* Spawn the child process. */
+ {
+ const char *child_argv[3];
+
+ child_argv[0] = child_path;
+ child_argv[1] = argv[2];
+ child_argv[2] = NULL;
+
+#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+ child = spawnvpe (P_NOWAIT, child_path, child_argv,
+ (const char **) environ);
+ ASSERT (child >= 0);
+#else
+ {
+ pid_t child_pid;
+ int err =
+ posix_spawnp (&child_pid, child_path, NULL, NULL, (char **) child_argv,
+ environ);
+ ASSERT (err == 0);
+ child = child_pid;
+ }
+#endif
+ }
+
+ /* Close unused file descriptors. */
+ close (STDIN_FILENO);
+
+ exitcode =
+ main_writer_loop (test, PIPE_DATA_BLOCK_SIZE, STDOUT_FILENO, false);
+
+ {
+ int err =
+ wait_subprocess (child, child_path, false, false, false, false, NULL);
+ ASSERT (err == 0);
+ }
+
+ return exitcode;
+}
--- /dev/null
+/* Test for nonblocking read and write.
+
+ Copyright (C) 2011 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* A data block ought to be larger than the size of the in-kernel buffer.
+ Working values of PIPE_DATA_BLOCK_SIZE, depending on kernel:
+
+ Platform PIPE_DATA_BLOCK_SIZE
+
+ Linux >= 63489
+ FreeBSD, OpenBSD, MacOS X >= 65537
+ AIX >= 32769
+ HP-UX >= 8193
+ IRIX >= 10241
+ OSF/1 >= 262145
+ Solaris <= 7 >= 10241
+ Solaris >= 8 >= 20481
+ Cygwin >= 65537
+ native Win32 >= 4097 (depends on the _pipe argument)
+ */
+#if defined __osf__
+# define PIPE_DATA_BLOCK_SIZE 270000
+#else
+# define PIPE_DATA_BLOCK_SIZE 70000
+#endif
--- /dev/null
+#!/bin/sh
+
+# Note: This test fails on Cygwin 1.5.x, because the non-blocking flag has
+# apparently no effect on STDOUT_FILENO. It is fixed in Cygwin 1.7.
+
+# Test blocking write() with blocking read().
+# Commented out because this test succeeds on all platforms anyway.
+#./test-nonblocking-pipe-main${EXEEXT} ./test-nonblocking-pipe-child${EXEEXT} 0 || exit 1
+
+# Test non-blocking write() with blocking read().
+./test-nonblocking-pipe-main${EXEEXT} ./test-nonblocking-pipe-child${EXEEXT} 1 || exit 1
+
+# Test blocking write() with non-blocking read().
+./test-nonblocking-pipe-main${EXEEXT} ./test-nonblocking-pipe-child${EXEEXT} 2 || exit 1
+
+# Test non-blocking write() with non-blocking read().
+./test-nonblocking-pipe-main${EXEEXT} ./test-nonblocking-pipe-child${EXEEXT} 3 || exit 1
--- /dev/null
+/* The reader part of a test program for non-blocking communication.
+
+ Copyright (C) 2011 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* This program implements 4 tests:
+
+ test == 0:
+ Test blocking write() with blocking read().
+
+ Timeline Main process Child process
+ 0 s Start Start, read(10000)
+ 1 s write(20000) Return from read(10000)
+ 2 s Next read(10000)
+ 2 s Return from write(20000) Return from read(10000)
+
+ test == 1:
+ Test non-blocking write() with blocking read().
+
+ Timeline Main process Child process
+ 0 s Start Start, read(10000)
+ 1 s write(20000) Return from read(10000)
+ Return with at least 10000,
+ Repeatedly continue
+ write() of the rest
+ 2 s Next read(10000)
+ 2 s Return from write(10000) Return from read(10000)
+
+ test == 2:
+ Test blocking write() with non-blocking read().
+
+ Timeline Main process Child process
+ 0 s Start Start, read(10000)
+ repeatedly polling
+ 1 s write(20000) Return from read(10000)
+ 2 s Next read(10000)
+ 2 s Return from write(20000) Return from read(10000)
+
+ test == 3:
+ Test non-blocking write() with non-blocking read().
+ */
+
+#include "test-nonblocking-misc.h"
+
+static ssize_t
+full_read (size_t fd, void *buf, size_t count)
+{
+ size_t bytes_read;
+
+ bytes_read = 0;
+ while (bytes_read < count)
+ {
+ TIMING_DECLS
+ ssize_t ret;
+ int saved_errno;
+
+ dbgfprintf (stderr, "%s: >> read (%lu)\n", PROG_ROLE,
+ (unsigned long) (count - bytes_read));
+ START_TIMING
+ ret = read (fd, (char *) buf + bytes_read, count - bytes_read);
+ saved_errno = errno;
+ END_TIMING
+ dbgfprintf (stderr, "%s: << read -> %ld%s\n", PROG_ROLE,
+ (long) ret, dbgstrerror (ret < 0, saved_errno));
+ if (ret < 0)
+ return -1;
+ else
+ {
+ ASSERT (ret > 0);
+ bytes_read += ret;
+ }
+ }
+ return bytes_read;
+}
+
+static ssize_t
+full_read_from_nonblocking_fd (size_t fd, void *buf, size_t count)
+{
+ size_t bytes_read;
+
+ bytes_read = 0;
+ while (bytes_read < count)
+ {
+ TIMING_DECLS
+ ssize_t ret;
+ int saved_errno;
+
+ dbgfprintf (stderr, "%s: >> read (%lu)\n", PROG_ROLE,
+ (unsigned long) (count - bytes_read));
+ START_TIMING
+ ret = read (fd, (char *) buf + bytes_read, count - bytes_read);
+ saved_errno = errno;
+ END_TIMING
+ dbgfprintf (stderr, "%s: << read -> %ld%s\n", PROG_ROLE,
+ (long) ret, dbgstrerror (ret < 0, saved_errno));
+ /* This assertion fails if the non-blocking flag is effectively not set
+ on fd = STDIN_FILENO. */
+ ASSERT (spent_time < 0.5);
+ if (ret < 0)
+ {
+ ASSERT (saved_errno == EAGAIN);
+ usleep (SMALL_DELAY);
+ }
+ else
+ {
+ ASSERT (ret > 0);
+ bytes_read += ret;
+ }
+ }
+ return bytes_read;
+}
+
+/* Execute the reader loop. */
+static void
+main_reader_loop (int test, size_t data_block_size, int fd)
+{
+ unsigned char *expected;
+ unsigned char *data;
+
+ /* Set up the expected data. */
+ expected = init_data (data_block_size);
+
+ data = (unsigned char *) malloc (2 * data_block_size);
+ ASSERT (data != NULL);
+
+ switch (test)
+ {
+ TIMING_DECLS
+ ssize_t ret;
+
+ case 0: /* Test blocking write() with blocking read(). */
+ case 1: /* Test non-blocking write() with blocking read(). */
+ START_TIMING
+ ret = full_read (fd, data, data_block_size);
+ END_TIMING
+ ASSERT (ret == data_block_size);
+ ASSERT (memcmp (data, expected, data_block_size) == 0);
+ ASSERT (spent_time > 0.5);
+ /* This assertion fails if data_block_size is very large and
+ ENABLE_DEBUGGING is 1. */
+ ASSERT (spent_time < 1.5);
+
+ usleep (1000000);
+
+ START_TIMING
+ ret = full_read (fd, data, data_block_size);
+ END_TIMING
+ ASSERT (ret == data_block_size);
+ ASSERT (memcmp (data, expected + data_block_size, data_block_size) == 0);
+ /* This assertion fails if data_block_size is much larger than needed
+ and SMALL_DELAY is too large. */
+ ASSERT (spent_time < 0.5);
+
+ break;
+
+ case 2: /* Test blocking write() with non-blocking read(). */
+ case 3: /* Test non-blocking write() with non-blocking read(). */
+ START_TIMING
+ ret = full_read_from_nonblocking_fd (fd, data, data_block_size);
+ END_TIMING
+ ASSERT (ret == data_block_size);
+ ASSERT (memcmp (data, expected, data_block_size) == 0);
+ ASSERT (spent_time > 0.5);
+ /* This assertion fails if data_block_size is much larger than needed
+ and SMALL_DELAY is too large, or if data_block_size is very large and
+ ENABLE_DEBUGGING is 1. */
+ ASSERT (spent_time < 1.5);
+
+ usleep (1000000);
+
+ START_TIMING
+ ret = full_read_from_nonblocking_fd (fd, data, data_block_size);
+ END_TIMING
+ ASSERT (ret == data_block_size);
+ ASSERT (memcmp (data, expected + data_block_size, data_block_size) == 0);
+ /* This assertion fails if data_block_size is much larger than needed
+ and SMALL_DELAY is too large. */
+ ASSERT (spent_time < 0.5);
+
+ break;
+
+ default:
+ abort ();
+ }
+
+ free (data);
+ free (expected);
+}
--- /dev/null
+/* The writer part of a test program for non-blocking communication.
+
+ Copyright (C) 2011 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* This program implements 4 tests:
+
+ test == 0:
+ Test blocking write() with blocking read().
+
+ Timeline Main process Child process
+ 0 s Start Start, read(10000)
+ 1 s write(20000) Return from read(10000)
+ 2 s Next read(10000)
+ 2 s Return from write(20000) Return from read(10000)
+
+ test == 1:
+ Test non-blocking write() with blocking read().
+
+ Timeline Main process Child process
+ 0 s Start Start, read(10000)
+ 1 s write(20000) Return from read(10000)
+ Return with at least 10000,
+ Repeatedly continue
+ write() of the rest
+ 2 s Next read(10000)
+ 2 s Return from write(10000) Return from read(10000)
+
+ test == 2:
+ Test blocking write() with non-blocking read().
+
+ Timeline Main process Child process
+ 0 s Start Start, read(10000)
+ repeatedly polling
+ 1 s write(20000) Return from read(10000)
+ 2 s Next read(10000)
+ 2 s Return from write(20000) Return from read(10000)
+
+ test == 3:
+ Test non-blocking write() with non-blocking read().
+ */
+
+#include "test-nonblocking-misc.h"
+
+/* Execute the writer loop.
+ Returns 0 if successful, 1 if data_block_size is too small. */
+static int
+main_writer_loop (int test, size_t data_block_size, int fd,
+ bool has_large_buffer)
+{
+ int too_small = 0;
+ unsigned char *data;
+
+ /* Set up the data to transfer. */
+ data = init_data (data_block_size);
+
+ switch (test)
+ {
+ TIMING_DECLS
+ ssize_t ret;
+
+ case 0: /* Test blocking write() with blocking read(). */
+ case 2: /* Test blocking write() with non-blocking read(). */
+ {
+ int saved_errno;
+
+ usleep (1000000);
+
+ dbgfprintf (stderr, "%s:1: >> write (%lu)\n", PROG_ROLE,
+ (unsigned long) 2 * data_block_size);
+ START_TIMING
+ ret = write (fd, data, 2 * data_block_size);
+ saved_errno = errno;
+ END_TIMING
+ dbgfprintf (stderr, "%s:1: << write -> %ld%s\n", PROG_ROLE,
+ (long) ret, dbgstrerror (ret < 0, saved_errno));
+ ASSERT (ret == 2 * data_block_size);
+ if (!has_large_buffer)
+ {
+ /* This assertion fails if data_block_size is too small. */
+ if (!(spent_time > 0.5))
+ {
+ fprintf (stderr,
+ "%s:1: spent_time = %g, data_block_size too small\n",
+ PROG_ROLE, spent_time);
+ too_small = 1;
+ }
+ }
+ ASSERT (spent_time < 1.5);
+ }
+ break;
+
+ case 1: /* Test non-blocking write() with blocking read(). */
+ case 3: /* Test non-blocking write() with non-blocking read(). */
+ {
+ size_t bytes_written;
+ int saved_errno;
+
+ usleep (1000000);
+
+ bytes_written = 0;
+ while (bytes_written < 2 * data_block_size)
+ {
+ dbgfprintf (stderr, "%s:2: >> write (%lu)\n", PROG_ROLE,
+ (unsigned long) (2 * data_block_size - bytes_written));
+ START_TIMING
+ ret = write (fd, data + bytes_written,
+ 2 * data_block_size - bytes_written);
+ saved_errno = errno;
+ END_TIMING
+ dbgfprintf (stderr, "%s:2: << write -> %ld%s\n", PROG_ROLE,
+ (long) ret, dbgstrerror (ret < 0, saved_errno));
+ if (ret < 0 && bytes_written >= data_block_size)
+ {
+ ASSERT (saved_errno == EAGAIN);
+ ASSERT (spent_time < 0.5);
+ break;
+ }
+ /* This assertion fails if the non-blocking flag is effectively not
+ set on fd. */
+ ASSERT (spent_time < 0.5);
+ if (ret < 0)
+ {
+ ASSERT (saved_errno == EAGAIN);
+ usleep (SMALL_DELAY);
+ }
+ else
+ {
+ /* This assertion fails if data_block_size is too small. */
+ if (!(ret > 0))
+ {
+ fprintf (stderr,
+ "%s:1: spent_time = %g, data_block_size too small\n",
+ PROG_ROLE, spent_time);
+ too_small = 1;
+ }
+ bytes_written += ret;
+ }
+ }
+ ASSERT (bytes_written >= data_block_size);
+
+ while (bytes_written < 2 * data_block_size)
+ {
+ dbgfprintf (stderr, "%s:3: >> write (%lu)\n", PROG_ROLE,
+ (unsigned long) (2 * data_block_size - bytes_written));
+ START_TIMING
+ ret = write (fd, data + bytes_written,
+ 2 * data_block_size - bytes_written);
+ saved_errno = errno;
+ END_TIMING
+ dbgfprintf (stderr, "%s:3: << write -> %ld%s\n", PROG_ROLE,
+ (long) ret, dbgstrerror (ret < 0, saved_errno));
+ ASSERT (spent_time < 0.5);
+ if (ret < 0)
+ {
+ ASSERT (saved_errno == EAGAIN);
+ usleep (SMALL_DELAY);
+ }
+ else
+ {
+ ASSERT (ret > 0);
+ bytes_written += ret;
+ }
+ }
+ }
+ break;
+
+ default:
+ abort ();
+ }
+
+ free (data);
+ return too_small;
+}