xshmfence_semaphore.c revision 875bea1a
1/*
2 * Copyright © 2015 Tobias Nygren
3 * Copyright © 2013 Keith Packard
4 *
5 * Permission to use, copy, modify, distribute, and sell this software and its
6 * documentation for any purpose is hereby granted without fee, provided that
7 * the above copyright notice appear in all copies and that both that copyright
8 * notice and this permission notice appear in supporting documentation, and
9 * that the name of the copyright holders not be used in advertising or
10 * publicity pertaining to distribution of the software without specific,
11 * written prior permission.  The copyright holders make no representations
12 * about the suitability of this software for any purpose.  It is provided "as
13 * is" without express or implied warranty.
14 *
15 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
17 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
19 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
20 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
21 * OF THIS SOFTWARE.
22 */
23
24#if HAVE_CONFIG_H
25#include "config.h"
26#endif
27
28#include <err.h>
29#include <errno.h>
30#include <fcntl.h>
31#include <stdio.h>
32#include <unistd.h>
33
34#include "xshmfenceint.h"
35
36static sem_t *mksemtemp(char *, const char *);
37
38#define LOCK() do {} while (sem_wait(f->lock) != 0)
39#define UNLOCK() do { sem_post(f->lock); } while (0)
40#define COND_WAIT() do {} while (sem_wait(f->cond) != 0)
41#define COND_SIGNAL() do { sem_post(f->cond); } while (0)
42
43/**
44 * xshmfence_trigger:
45 * @f: An X fence
46 *
47 * Set @f to triggered, waking all waiters.
48 *
49 * Return value: 0 on success and -1 on error (in which case, errno
50 * will be set as appropriate).
51 **/
52int
53xshmfence_trigger(struct xshmfence *f) {
54	int v, waiting;
55	LOCK();
56	v = __sync_bool_compare_and_swap(&f->triggered, 0, 1);
57	if (v == 0) {
58		/* already triggered */
59		UNLOCK();
60		return 0;
61	}
62
63	waiting = __sync_fetch_and_add(&f->waiting, 0);
64
65	while (waiting > 0) {
66		COND_SIGNAL();
67		waiting--;
68	}
69
70	while (__sync_fetch_and_add(&f->waiting, 0) > 0) {
71		/*
72		 * Busy wait until they all woke up.
73		 * No new sleepers should arrive since
74		 * the lock is still held.
75		 */
76		/* yield(); */
77	}
78	UNLOCK();
79	return 0;
80}
81
82/**
83 * xshmfence_await:
84 * @f: An X fence
85 *
86 * Wait for @f to be triggered. If @f is already triggered, this
87 * function returns immediately.
88 *
89 * Return value: 0 on success and -1 on error (in which case, errno
90 * will be set as appropriate).
91 **/
92int
93xshmfence_await(struct xshmfence *f) {
94
95	LOCK();
96	if (__sync_fetch_and_add(&f->triggered, 0) == 1) {
97		UNLOCK();
98		return 0;
99	}
100	do {
101		__sync_fetch_and_add(&f->waiting, 1);
102		/*
103		 * These next operations are not atomic.
104		 * But we busy-wait in xshmfence_trigger, so that's ok.
105		 */
106		UNLOCK();
107		COND_WAIT();
108		__sync_fetch_and_sub(&f->waiting, 1);
109		LOCK();
110	}
111	while (__sync_fetch_and_add(&f->triggered, 0) == 0);
112	UNLOCK();
113	return 0;
114}
115
116/**
117 * xshmfence_query:
118 * @f: An X fence
119 *
120 * Return value: 1 if @f is triggered, else returns 0.
121 **/
122int
123xshmfence_query(struct xshmfence *f) {
124	int ret;
125	LOCK();
126	ret = __sync_fetch_and_add(&f->triggered, 0);
127	UNLOCK();
128	return ret;
129}
130
131/**
132 * xshmfence_reset:
133 * @f: An X fence
134 *
135 * Reset @f to untriggered. If @f is already untriggered,
136 * this function has no effect.
137 **/
138void
139xshmfence_reset(struct xshmfence *f) {
140	LOCK();
141	__sync_bool_compare_and_swap(&f->triggered, 1, 0);
142	UNLOCK();
143}
144
145/**
146 * xshmfence_init:
147 * @fd: An fd for an X fence
148 *
149 * Initialize the fence when first allocated
150 **/
151void
152xshmfence_init(int fd)
153{
154	sem_t *lock;
155	sem_t *cond;
156	struct xshmfence f;
157
158	__sync_fetch_and_and(&f.refcnt, 0);
159	__sync_fetch_and_and(&f.triggered, 0);
160	__sync_fetch_and_and(&f.waiting, 0);
161
162	lock = mksemtemp(f.lockname, "/xshmfl-%i");
163	if (lock == SEM_FAILED) {
164		err(EXIT_FAILURE, "xshmfence_init: sem_open");
165	}
166
167	cond = mksemtemp(f.condname, "/xshmfc-%i");
168	if (cond == SEM_FAILED) {
169		err(EXIT_FAILURE, "xshmfence_init: sem_open");
170	}
171
172	sem_close(lock);
173	sem_close(cond);
174
175	pwrite(fd, &f, sizeof(f), 0);
176}
177
178/**
179 * xshmfence_open_semaphore:
180 * @f: An X fence
181 *
182 * Open the semaphore after mapping the fence
183 **/
184void
185xshmfence_open_semaphore(struct xshmfence *f)
186{
187	/*
188	 * map process local memory to page 2
189	 */
190	if (mmap ((void*)&f->lock,
191		  LIBXSHM_PAGESIZE,
192		  PROT_READ|PROT_WRITE,
193		  MAP_FIXED|MAP_ANON,
194		  -1, 0) == MAP_FAILED) {
195		errx(EXIT_FAILURE, "xshmfence_open_semaphore: mmap failed");
196	}
197
198	if ((f->lock = sem_open(f->lockname, 0 , 0)) == SEM_FAILED) {
199		errx(EXIT_FAILURE, "xshmfence_open_semaphore: sem_open failed");
200	}
201
202	if ((f->cond = sem_open(f->condname, 0 , 0)) == SEM_FAILED) {
203		errx(EXIT_FAILURE, "xshmfence_open_semaphore: sem_open failed");
204	}
205	__sync_fetch_and_add(&f->refcnt, 1);
206}
207
208/**
209 * xshmfence_close_semaphore:
210 * @f: An X fence
211 *
212 * Close the semaphore before unmapping the fence
213 **/
214void
215xshmfence_close_semaphore(struct xshmfence *f)
216{
217	sem_close(f->lock);
218	sem_close(f->cond);
219	if (__sync_sub_and_fetch(&f->refcnt, 1) == 0) {
220		sem_unlink(f->lockname);
221		sem_unlink(f->condname);
222	}
223}
224
225static sem_t *
226mksemtemp(char *name, const char *template)
227{
228	sem_t *ret;
229	pid_t p;
230	p = getpid();
231	for(;;) {
232		sprintf(name, template, p);
233		ret = sem_open(name, O_CREAT|O_EXCL, 0600, 1);
234		if (ret == SEM_FAILED) {
235			if (errno == EEXIST) {
236				p++;
237				continue;
238			}
239		}
240		return ret;
241	}
242}
243