// Test code for reproducing http://bugs.python.org/issue15896 in C++ #include #include #include #include #include #include #include #include #include #define MIN_MULTIPLIER 1 // start of read-size multipliler range #define MAX_MULTIPLIER 1024 // end of read-size multipliler range #define NUM_ELEMENTS_IN_MULTIPLIER_RANGE ((MAX_MULTIPLIER - MIN_MULTIPLIER) + 1) #define NUM_FORK_ATTEMPTS_PER_MULTIPLIER 10 #define READ_SIZE_BASE 1024 #define WRITE_SIZE 512 int _execute_child(const int initialReadSize, const bool verbose) { int pipes[2]; if (pipe(pipes) != 0) { perror("pipe"); exit(EXIT_FAILURE); } const int errpipe_read = pipes[0]; const int errpipe_write = pipes[1]; // 0 in child process; pid of child in parent process pid_t const pid = fork(); if (pid == -1) { perror("fork"); exit(EXIT_FAILURE); } if (pid == 0) // CHILD PROCESS { //if (close(errpipe_read) != 0) //{ // perror("close(errpipe_read) in child"); // exit(EXIT_FAILURE); //} char out_data[WRITE_SIZE]; memset(out_data, 'a', sizeof(out_data)); const ssize_t num_written = write(errpipe_write, out_data, sizeof(out_data)); if (num_written == -1) { perror("write(errpipe_write, out_data, sizeof(out_data))"); exit(EXIT_FAILURE); } if (num_written != WRITE_SIZE) { // I know, write is not guaranteed to write out all of the requested // bytes, but in our simple test with small write amount it should. fprintf(stderr, "ERROR: child failed to write the expected number of bytes; " \ "expected=%d; num_written=%ld.\n", WRITE_SIZE, num_written); exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); // CHILD IS DONE! } // END OF CHILD else // PARENT PROCESS { // Don't need errpipe_write in parent if (close(errpipe_write) != 0) { perror("close(errpipe_write) in parent"); exit(EXIT_FAILURE); } // Make read pipe non-blocking const int old_flags = fcntl(errpipe_read, F_GETFL, 0); if (old_flags == -1) { perror("fcntl(errpipe_read, F_GETFL, 0) in parent"); exit(EXIT_FAILURE); } if (fcntl(errpipe_read, F_SETFL, old_flags | O_NONBLOCK) == -1) { perror("fcntl(errpipe_read, F_SETFL, old_flags | O_NONBLOCK) in parent"); exit(EXIT_FAILURE); } if (verbose) { fprintf(stderr, "New errpipe_read fcntl flags: %d\n", fcntl(errpipe_read, F_GETFL, 0)); } // Read data from child process size_t total_num_bytes_read = 0; //char* const outer_read_buf_ptr = (char*) malloc(initialReadSize); int result = -1; while (total_num_bytes_read < initialReadSize) { const size_t num_bytes_to_read = initialReadSize - total_num_bytes_read; char* const read_buf_ptr = (char*) malloc(num_bytes_to_read); if (verbose) { fprintf(stderr, "read(%d, ptr, %lu)\n", errpipe_read, num_bytes_to_read); } const ssize_t num_bytes_read = read( errpipe_read, read_buf_ptr, //outer_read_buf_ptr + total_num_bytes_read, num_bytes_to_read); free(read_buf_ptr); if (num_bytes_read == -1) { if (errno == EINTR || errno == EWOULDBLOCK) { if (verbose) { perror("read(errpipe_read,...) in parent, retrying..."); } continue; } else { result = errno; perror("read(errpipe_read,...) in parent."); break; } } else if (num_bytes_read == 0) { result = 0; if (verbose) { fprintf(stderr, "errpipe_read EOF in parent.\n"); } break; } else { total_num_bytes_read += num_bytes_read; } } // end read until EOF //free(outer_read_buf_ptr); if (close(errpipe_read) != 0) { perror("close(errpipe_read) in parent"); exit(EXIT_FAILURE); } if ((result == 0) && (total_num_bytes_read != WRITE_SIZE)) { fprintf(stderr, "ERROR: parent failed to read the expected number of bytes; " \ "expected=%d; total_num_bytes_read=%ld.\n", WRITE_SIZE, total_num_bytes_read); exit(EXIT_FAILURE); } if (result == -1) { fprintf(stderr, "ERROR: unexpected result=%d at end of parent; " \ "initialReadSize=%d; total_num_bytes_read=%lu.\n", result, initialReadSize, total_num_bytes_read); exit(EXIT_FAILURE); } // Wait for child to terminate int child_state; const pid_t wait_result = waitpid(pid, &child_state, 0); if (wait_result == -1) { perror("waitpid(pid, &child_stat, 0) in parent"); exit(EXIT_FAILURE); } if (WIFEXITED(child_state)) { const int child_exit_status = WEXITSTATUS(child_state); if (child_exit_status != EXIT_SUCCESS) { fprintf(stderr, "Child exited with RC=%d\n", child_exit_status); exit(EXIT_FAILURE); } } else if (WIFSIGNALED(child_state)) { fprintf(stderr, "ERROR: Child exited via signal %d\n", WTERMSIG(child_state)); exit(EXIT_FAILURE); } else { fprintf(stderr, "ERROR: Unexpected child exit state %d\n", child_state); } return result; } // END OF PARENT fprintf(stderr, "Control reached outside of parent/child block; child pid=%d\n", pid); exit(EXIT_FAILURE); } void test(bool decreasing) { const char * const order_string = ( decreasing ? "DECREASING" : "INCREASING"); fprintf(stderr, "Running test in %s order.\n", order_string); bool failed_multiplier_EINVAL_histogram[MAX_MULTIPLIER + 1]; // +1 for 0 multiplier memset(failed_multiplier_EINVAL_histogram, 0, sizeof(failed_multiplier_EINVAL_histogram)); int num_failed_multipliers = 0; for (int multiplier=decreasing ? MAX_MULTIPLIER : MIN_MULTIPLIER; decreasing ? multiplier >= MIN_MULTIPLIER : multiplier <= MAX_MULTIPLIER; decreasing ? --multiplier : ++multiplier) { bool multiplier_failed = false; for (int runIndex=0; runIndex < NUM_FORK_ATTEMPTS_PER_MULTIPLIER; ++runIndex) { fprintf(stderr, "run %d; multiplier %d\n", runIndex, multiplier); const int err = _execute_child(READ_SIZE_BASE*multiplier, false); if (err == EINVAL) { if (!multiplier_failed) { multiplier_failed = true; num_failed_multipliers++; failed_multiplier_EINVAL_histogram[multiplier] = true; } fprintf(stderr, "EINVAL\n"); } else if (err) { fprintf(stderr, "ERROR: unexpected error from _execute_child(): %d (%s)\n", err, strerror(err)); exit(EXIT_FAILURE); } } } // Print out the list of failed multipliers, if any if (num_failed_multipliers) { fprintf(stderr, "failed_multiplier_EINVAL_histogram hits (%d, %s):\n", num_failed_multipliers, order_string); const int num_elements = (sizeof(failed_multiplier_EINVAL_histogram) / sizeof(failed_multiplier_EINVAL_histogram[0])); for (int i=decreasing ? num_elements - 1 : 0; decreasing ? i > 0 : i < num_elements; decreasing ? --i : ++i) { if (failed_multiplier_EINVAL_histogram[i]) { fprintf(stderr, "%d, ", i); } } fprintf(stderr, "THE END.\n"); } else { fprintf(stderr, "0 EINVAL failures detected (%s).\n", order_string); } } int main(int argc, char **argv) { // Decreasing order test(true); // Increasing order test(false); exit(EXIT_SUCCESS); }