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