1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <new>
18#include <stdio.h>
19#include <sys/mman.h>
20#include <sys/types.h>
21#include <sys/wait.h>
22#include <unistd.h>
23
24#include <audio_utils/fifo.h>
25#include <cutils/ashmem.h>
26
27#define FRAME_COUNT 2048
28#define FRAME_SIZE sizeof(int16_t)
29#define BUFFER_SIZE (FRAME_COUNT * FRAME_SIZE)
30
31int main(int argc __unused, char **argv __unused)
32{
33    // TODO Add error checking for ashmem_create_region and mmap
34
35    const int frontFd = ashmem_create_region("front", sizeof(audio_utils_fifo_index));
36    printf("frontFd=%d\n", frontFd);
37
38    const int rearFd = ashmem_create_region("rear", sizeof(audio_utils_fifo_index));
39    printf("rearFd=%d\n", rearFd);
40
41    const int dataFd = ashmem_create_region("buffer", BUFFER_SIZE);
42    printf("dataFd=%d\n", dataFd);
43
44    // next two index constructors must execute exactly once, so we do it in the parent
45
46    audio_utils_fifo_index *frontIndex = (audio_utils_fifo_index *) mmap(NULL,
47            sizeof(audio_utils_fifo_index), PROT_READ | PROT_WRITE, MAP_SHARED, frontFd, (off_t) 0);
48    printf("parent frontIndex=%p\n", frontIndex);
49    (void) new(frontIndex) audio_utils_fifo_index();
50
51    audio_utils_fifo_index *rearIndex = (audio_utils_fifo_index *) mmap(NULL,
52            sizeof(audio_utils_fifo_index), PROT_READ | PROT_WRITE, MAP_SHARED, rearFd, (off_t) 0);
53    printf("parent rearIndex=%p\n", rearIndex);
54    (void) new(rearIndex) audio_utils_fifo_index();
55
56    int16_t *data = (int16_t *) mmap(NULL, sizeof(audio_utils_fifo_index), PROT_READ | PROT_WRITE,
57            MAP_SHARED, dataFd, (off_t) 0);
58    printf("parent data=%p\n", data);
59    memset(data, 0, BUFFER_SIZE);
60
61    const int pageSize = getpagesize();
62    printf("page size=%d\n", pageSize);
63
64    // create writer
65
66    printf("fork writer:\n");
67    const pid_t pidWriter = fork();
68    // TODO check if pidWriter < 0
69    if (!pidWriter) {
70
71        // Child inherits the parent's read/write mapping of front index.
72        // To confirm that there are no attempts to write to the front index,
73        // unmap it and then re-map it as read-only.
74        int ok = munmap(frontIndex, sizeof(audio_utils_fifo_index));
75        printf("writer unmap front ok=%d\n", ok);
76        ok = ashmem_set_prot_region(frontFd, PROT_READ);
77        printf("writer prot read front ok=%d\n", ok);
78        // The pagesize * 4 offset confirms that we don't assume identical mapping in both processes
79        frontIndex = (audio_utils_fifo_index *) mmap((char *) frontIndex + pageSize * 4,
80                sizeof(audio_utils_fifo_index), PROT_READ, MAP_SHARED | MAP_FIXED, frontFd,
81                (off_t) 0);
82        printf("writer frontIndex=%p\n", frontIndex);
83
84        // Retain our read/write mapping of rear index and data
85        audio_utils_fifo fifo(FRAME_COUNT, FRAME_SIZE, data, *rearIndex, frontIndex);
86        audio_utils_fifo_writer writer(fifo);
87
88        sleep(2);
89
90        for (int16_t value = 1; value <= 20; value++) {
91            printf("writing %d\n", value);
92            const ssize_t actual = writer.write(&value, 1);
93            if (actual != 1) {
94                printf("wrote unexpected actual = %zd\n", actual);
95                break;
96            }
97            // TODO needs a lot of work
98            switch (value) {
99            case 10:
100                sleep(2);
101                break;
102            case 14:
103                sleep(4);
104                break;
105            default:
106                usleep(500000);
107                break;
108            }
109        }
110
111        (void) close(frontFd);
112        (void) close(rearFd);
113        (void) close(dataFd);
114
115        return EXIT_SUCCESS;
116    }
117
118    // The sleep(2) above and sleep(1) here ensure that the order is:
119    //  a. writer initializes
120    //  b. reader initializes
121    //  c. reader starts the read loop
122    //  d. writer starts the write loop
123    // Actually, as long as (a) precedes (d) and (b) precedes (c), the order does not matter.
124    // TODO test all valid sequences.
125    sleep(1);
126
127    // create reader
128
129    printf("fork reader:\n");
130    const pid_t pidReader = fork();
131    // TODO check if pidReader < 0
132    if (!pidReader) {
133
134        // Child inherits the parent's read/write mapping of rear index.
135        // To confirm that there are no attempts to write to the rear index,
136        // unmap it and then re-map it as read-only.
137        int ok = munmap(rearIndex, sizeof(audio_utils_fifo_index));
138        printf("reader unmap rear ok=%d\n", ok);
139        ok = ashmem_set_prot_region(rearFd, PROT_READ);
140        printf("reader prot read rear ok=%d\n", ok);
141        // The pagesize * 4 offset confirms that we don't assume identical mapping in both processes
142        rearIndex = (audio_utils_fifo_index *) mmap((char *) rearIndex + pageSize * 4,
143                sizeof(audio_utils_fifo_index), PROT_READ, MAP_SHARED | MAP_FIXED, rearFd,
144                (off_t) 0);
145        printf("reader rearIndex=%p\n", rearIndex);
146
147        // Similarly for the data
148        ok = munmap(data, BUFFER_SIZE);
149        printf("reader unmap data ok=%d\n", ok);
150        ok = ashmem_set_prot_region(dataFd, PROT_READ);
151        printf("reader prot read data ok=%d\n", ok);
152        // The pagesize * 8 offset confirms that we don't assume identical mapping in both processes
153        data = (int16_t *) mmap((char *) data + pageSize * 8, BUFFER_SIZE, PROT_READ,
154                MAP_SHARED | MAP_FIXED, dataFd, (off_t) 0);
155        printf("reader data=%p\n", data);
156
157        // Retain our read/write mapping of front index
158        audio_utils_fifo fifo(FRAME_COUNT, FRAME_SIZE, data, *rearIndex, frontIndex);
159        audio_utils_fifo_reader reader(fifo);
160
161        for (;;) {
162            int16_t value;
163            struct timespec timeout = {
164                .tv_sec = 1,
165                .tv_nsec = 0
166            };
167            const ssize_t actual = reader.read(&value, 1, &timeout);
168            switch (actual) {
169            case 0:
170                break;
171            case 1:
172                printf("read %d\n", value);
173                if (value == 20) {
174                    goto out;
175                }
176                break;
177            case -ETIMEDOUT:
178                printf("read timed out\n");
179                break;
180            default:
181                printf("read unexpected actual = %zd\n", actual);
182                goto out;
183            }
184        }
185out:
186
187        (void) close(frontFd);
188        (void) close(rearFd);
189        (void) close(dataFd);
190
191        return EXIT_SUCCESS;
192    }
193
194    int status;
195    pid_t pid = waitpid(pidWriter, &status, 0);
196    if (pid == pidWriter) {
197        printf("writer exited with status %d\n", status);
198    } else {
199        printf("waitpid on writer = %d\n", pid);
200    }
201    pid = waitpid(pidReader, &status, 0);
202    if (pid == pidReader) {
203        printf("reader exited with status %d\n", status);
204    } else {
205        printf("waitpid on reader = %d\n", pid);
206    }
207
208    // next two index destructors must execute exactly once, so we do it in the parent
209    frontIndex->~audio_utils_fifo_index();
210    rearIndex->~audio_utils_fifo_index();
211
212    int ok = munmap(frontIndex, sizeof(audio_utils_fifo_index));
213    printf("parent unmap front ok=%d\n", ok);
214    ok = munmap(rearIndex, sizeof(audio_utils_fifo_index));
215    printf("parent unmap rear ok=%d\n", ok);
216    ok = munmap(data, BUFFER_SIZE);
217    printf("parent unmap data ok=%d\n", ok);
218
219    (void) close(frontFd);
220    (void) close(rearFd);
221    (void) close(dataFd);
222
223    return EXIT_SUCCESS;
224}
225