1/*
2 * Copyright © 2013 Keith Packard
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that copyright
7 * notice and this permission notice appear in supporting documentation, and
8 * that the name of the copyright holders not be used in advertising or
9 * publicity pertaining to distribution of the software without specific,
10 * written prior permission.  The copyright holders make no representations
11 * about the suitability of this software for any purpose.  It is provided "as
12 * is" without express or implied warranty.
13 *
14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20 * OF THIS SOFTWARE.
21 */
22
23#include <stdint.h>
24#include <stdio.h>
25#include <stddef.h>
26#include <stdlib.h>
27#include <xshmfence.h>
28#include <unistd.h>
29#include <signal.h>
30#include <sys/types.h>
31#include <sys/wait.h>
32
33#define NCHILD  5               /* number of child processes to fork */
34#define NCHECK  10              /* number of times to signal the fence */
35
36/* Catch an alarm and bail
37 */
38static void
39sigalrm(int sig)
40{
41    write(2, "caught alarm\n", 13);
42    exit(1);
43}
44
45int
46main(int argc, char **argv)
47{
48    int		        fd;
49    struct xshmfence    *x;
50    int		        i;
51    int		        c;
52    int		        pid;
53    int                 status;
54    int                 failed = 0;
55
56    /* Allocate a fence
57     */
58    fd = xshmfence_alloc_shm();
59    if (fd < 0) {
60        perror("xshmfence_alloc_shm");
61        exit(1);
62    }
63
64    /* fork NCHILD processes to wait for the fence
65     */
66    for (c = 0; c < NCHILD; c++) {
67        switch (fork()) {
68        case -1:
69            perror("fork");
70            exit(1);
71        case 0:
72
73            /* Set an alarm to limit how long
74             * to wait
75             */
76            signal(SIGALRM, sigalrm);
77            alarm(10);
78
79            /* Map the fence
80             */
81            x = xshmfence_map_shm(fd);
82            if (!x) {
83                fprintf(stderr, "%6d: ", c);
84                perror("xshmfence_map_shm");
85                exit(1);
86            }
87
88            for (i = 0; i < NCHECK; i++) {
89
90                /* Verify that the fence is currently reset
91                 */
92                if (xshmfence_query(x) != 0) {
93                    fprintf(stderr, "%6d: query reset failed\n", c);
94                    exit(1);
95                }
96
97                /* Wait for the fence
98                 */
99                fprintf(stderr, "%6d: waiting\n", c);
100                if (xshmfence_await(x) < 0) {
101                    fprintf(stderr, "%6d: ", c);
102                    perror("xshmfence_await");
103                    exit(1);
104                }
105
106                fprintf(stderr, "%6d: awoken\n", c);
107
108                /* Verify that the fence is currently triggered
109                 */
110                if (xshmfence_query(x) == 0) {
111                    fprintf(stderr, "%6d: query triggered failed\n", c);
112                    exit(1);
113                }
114
115                usleep(10 * 1000);
116
117                /* Reset the fence
118                 */
119                if (c == 0)
120                    xshmfence_reset(x);
121
122                usleep(10 * 1000);
123            }
124            fprintf(stderr, "%6d: done\n", c);
125            exit(0);
126        }
127    }
128
129    /* Map the fence into the parent process
130     */
131    x = xshmfence_map_shm(fd);
132    if (!x) {
133        perror("xshmfence_map_shm");
134        exit(1);
135    }
136
137    for (i = 0; i < NCHECK; i++) {
138        usleep(100 * 1000);
139        fprintf(stderr, "trigger\n");
140
141        /* Verify that the fence is reset
142         */
143        if (xshmfence_query(x) != 0) {
144            fprintf(stderr, "query reset failed\n");
145            exit(1);
146        }
147
148        /* Trigger the fence
149         */
150        if (xshmfence_trigger(x) < 0) {
151            perror("xshmfence_trigger");
152            exit(1);
153        }
154
155        /* Verify that the fence is triggered
156         */
157        if (xshmfence_query(x) == 0) {
158            fprintf (stderr, "query triggered failed\n");
159            exit(1);
160        }
161
162        fprintf(stderr, "trigger done\n");
163    }
164
165    /* Reap all of the child processes
166     */
167    for (c = 0; c < NCHILD; c++) {
168        pid = wait(&status);
169        if (pid < 0) {
170            perror("wait");
171            exit(1);
172        }
173        fprintf(stderr, "child %d done %d\n", pid, status);
174        if (status)
175            failed++;
176    }
177    exit(failed);
178}
179