/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <new> #include <stdio.h> #include <sys/mman.h> #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> #include <audio_utils/fifo.h> #include <cutils/ashmem.h> #define FRAME_COUNT 2048 #define FRAME_SIZE sizeof(int16_t) #define BUFFER_SIZE (FRAME_COUNT * FRAME_SIZE) int main(int argc __unused, char **argv __unused) { // TODO Add error checking for ashmem_create_region and mmap const int frontFd = ashmem_create_region("front", sizeof(audio_utils_fifo_index)); printf("frontFd=%d\n", frontFd); const int rearFd = ashmem_create_region("rear", sizeof(audio_utils_fifo_index)); printf("rearFd=%d\n", rearFd); const int dataFd = ashmem_create_region("buffer", BUFFER_SIZE); printf("dataFd=%d\n", dataFd); // next two index constructors must execute exactly once, so we do it in the parent audio_utils_fifo_index *frontIndex = (audio_utils_fifo_index *) mmap(NULL, sizeof(audio_utils_fifo_index), PROT_READ | PROT_WRITE, MAP_SHARED, frontFd, (off_t) 0); printf("parent frontIndex=%p\n", frontIndex); (void) new(frontIndex) audio_utils_fifo_index(); audio_utils_fifo_index *rearIndex = (audio_utils_fifo_index *) mmap(NULL, sizeof(audio_utils_fifo_index), PROT_READ | PROT_WRITE, MAP_SHARED, rearFd, (off_t) 0); printf("parent rearIndex=%p\n", rearIndex); (void) new(rearIndex) audio_utils_fifo_index(); int16_t *data = (int16_t *) mmap(NULL, sizeof(audio_utils_fifo_index), PROT_READ | PROT_WRITE, MAP_SHARED, dataFd, (off_t) 0); printf("parent data=%p\n", data); memset(data, 0, BUFFER_SIZE); const int pageSize = getpagesize(); printf("page size=%d\n", pageSize); // create writer printf("fork writer:\n"); const pid_t pidWriter = fork(); // TODO check if pidWriter < 0 if (!pidWriter) { // Child inherits the parent's read/write mapping of front index. // To confirm that there are no attempts to write to the front index, // unmap it and then re-map it as read-only. int ok = munmap(frontIndex, sizeof(audio_utils_fifo_index)); printf("writer unmap front ok=%d\n", ok); ok = ashmem_set_prot_region(frontFd, PROT_READ); printf("writer prot read front ok=%d\n", ok); // The pagesize * 4 offset confirms that we don't assume identical mapping in both processes frontIndex = (audio_utils_fifo_index *) mmap((char *) frontIndex + pageSize * 4, sizeof(audio_utils_fifo_index), PROT_READ, MAP_SHARED | MAP_FIXED, frontFd, (off_t) 0); printf("writer frontIndex=%p\n", frontIndex); // Retain our read/write mapping of rear index and data audio_utils_fifo fifo(FRAME_COUNT, FRAME_SIZE, data, *rearIndex, frontIndex); audio_utils_fifo_writer writer(fifo); sleep(2); for (int16_t value = 1; value <= 20; value++) { printf("writing %d\n", value); const ssize_t actual = writer.write(&value, 1); if (actual != 1) { printf("wrote unexpected actual = %zd\n", actual); break; } // TODO needs a lot of work switch (value) { case 10: sleep(2); break; case 14: sleep(4); break; default: usleep(500000); break; } } (void) close(frontFd); (void) close(rearFd); (void) close(dataFd); return EXIT_SUCCESS; } // The sleep(2) above and sleep(1) here ensure that the order is: // a. writer initializes // b. reader initializes // c. reader starts the read loop // d. writer starts the write loop // Actually, as long as (a) precedes (d) and (b) precedes (c), the order does not matter. // TODO test all valid sequences. sleep(1); // create reader printf("fork reader:\n"); const pid_t pidReader = fork(); // TODO check if pidReader < 0 if (!pidReader) { // Child inherits the parent's read/write mapping of rear index. // To confirm that there are no attempts to write to the rear index, // unmap it and then re-map it as read-only. int ok = munmap(rearIndex, sizeof(audio_utils_fifo_index)); printf("reader unmap rear ok=%d\n", ok); ok = ashmem_set_prot_region(rearFd, PROT_READ); printf("reader prot read rear ok=%d\n", ok); // The pagesize * 4 offset confirms that we don't assume identical mapping in both processes rearIndex = (audio_utils_fifo_index *) mmap((char *) rearIndex + pageSize * 4, sizeof(audio_utils_fifo_index), PROT_READ, MAP_SHARED | MAP_FIXED, rearFd, (off_t) 0); printf("reader rearIndex=%p\n", rearIndex); // Similarly for the data ok = munmap(data, BUFFER_SIZE); printf("reader unmap data ok=%d\n", ok); ok = ashmem_set_prot_region(dataFd, PROT_READ); printf("reader prot read data ok=%d\n", ok); // The pagesize * 8 offset confirms that we don't assume identical mapping in both processes data = (int16_t *) mmap((char *) data + pageSize * 8, BUFFER_SIZE, PROT_READ, MAP_SHARED | MAP_FIXED, dataFd, (off_t) 0); printf("reader data=%p\n", data); // Retain our read/write mapping of front index audio_utils_fifo fifo(FRAME_COUNT, FRAME_SIZE, data, *rearIndex, frontIndex); audio_utils_fifo_reader reader(fifo); for (;;) { int16_t value; struct timespec timeout = { .tv_sec = 1, .tv_nsec = 0 }; const ssize_t actual = reader.read(&value, 1, &timeout); switch (actual) { case 0: break; case 1: printf("read %d\n", value); if (value == 20) { goto out; } break; case -ETIMEDOUT: printf("read timed out\n"); break; default: printf("read unexpected actual = %zd\n", actual); goto out; } } out: (void) close(frontFd); (void) close(rearFd); (void) close(dataFd); return EXIT_SUCCESS; } int status; pid_t pid = waitpid(pidWriter, &status, 0); if (pid == pidWriter) { printf("writer exited with status %d\n", status); } else { printf("waitpid on writer = %d\n", pid); } pid = waitpid(pidReader, &status, 0); if (pid == pidReader) { printf("reader exited with status %d\n", status); } else { printf("waitpid on reader = %d\n", pid); } // next two index destructors must execute exactly once, so we do it in the parent frontIndex->~audio_utils_fifo_index(); rearIndex->~audio_utils_fifo_index(); int ok = munmap(frontIndex, sizeof(audio_utils_fifo_index)); printf("parent unmap front ok=%d\n", ok); ok = munmap(rearIndex, sizeof(audio_utils_fifo_index)); printf("parent unmap rear ok=%d\n", ok); ok = munmap(data, BUFFER_SIZE); printf("parent unmap data ok=%d\n", ok); (void) close(frontFd); (void) close(rearFd); (void) close(dataFd); return EXIT_SUCCESS; }