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 *, size_t, const char *); 37 38#define COND_WAIT_W() do {} while (sem_wait(f->cond_w) != 0) 39#define COND_SIGNAL_W() do { sem_post(f->cond_w); } while (0) 40#define COND_WAIT_T() do {} while (sem_wait(f->cond_t) != 0) 41#define COND_SIGNAL_T() do { sem_post(f->cond_t); } 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 v = __sync_bool_compare_and_swap(&f->triggered, 0, 1); 56 if (v == 0) { 57 /* already triggered */ 58 return 0; 59 } 60 61 while ((waiting = __sync_fetch_and_add(&f->waiting, 0)) > 0) { 62 if (__sync_bool_compare_and_swap(&f->waiting, waiting, 0)) { 63 __sync_fetch_and_add(&f->wakeups, waiting); 64 while (waiting--) { 65 COND_SIGNAL_W(); 66 } 67 COND_WAIT_T(); 68 return 0; 69 } 70 } 71 72 return 0; 73} 74 75/** 76 * xshmfence_await: 77 * @f: An X fence 78 * 79 * Wait for @f to be triggered. If @f is already triggered, this 80 * function returns immediately. 81 * 82 * Return value: 0 on success and -1 on error (in which case, errno 83 * will be set as appropriate). 84 **/ 85int 86xshmfence_await(struct xshmfence *f) { 87 88 if (__sync_fetch_and_add(&f->triggered, 0) == 1) { 89 return 0; 90 } 91 do { 92 __sync_fetch_and_add(&f->waiting, 1); 93 COND_WAIT_W(); 94 } while (__sync_fetch_and_add(&f->triggered, 0) == 0); 95 96 if (__sync_sub_and_fetch(&f->wakeups, 1) == 0) { 97 COND_SIGNAL_T(); 98 } 99 100 return 0; 101} 102 103/** 104 * xshmfence_query: 105 * @f: An X fence 106 * 107 * Return value: 1 if @f is triggered, else returns 0. 108 **/ 109int 110xshmfence_query(struct xshmfence *f) { 111 return __sync_fetch_and_add(&f->triggered, 0); 112} 113 114/** 115 * xshmfence_reset: 116 * @f: An X fence 117 * 118 * Reset @f to untriggered. If @f is already untriggered, 119 * this function has no effect. 120 **/ 121void 122xshmfence_reset(struct xshmfence *f) { 123 __sync_bool_compare_and_swap(&f->triggered, 1, 0); 124} 125 126/** 127 * xshmfence_init: 128 * @fd: An fd for an X fence 129 * 130 * Initialize the fence when first allocated 131 **/ 132void 133xshmfence_init(int fd) 134{ 135 sem_t *cond_w, *cond_t; 136 struct xshmfence f; 137 138 __sync_fetch_and_and(&f.refcnt, 0); 139 __sync_fetch_and_and(&f.triggered, 0); 140 __sync_fetch_and_and(&f.waiting, 0); 141 __sync_fetch_and_and(&f.wakeups, 0); 142 143 cond_w = mksemtemp(f.condname_w, sizeof(f.condname_w), "w"); 144 if (cond_w == SEM_FAILED) { 145 err(EXIT_FAILURE, "xshmfence_init: sem_open"); 146 } 147 cond_t = mksemtemp(f.condname_t, sizeof(f.condname_t), "t"); 148 if (cond_t == SEM_FAILED) { 149 err(EXIT_FAILURE, "xshmfence_init: sem_open"); 150 } 151 152 sem_close(cond_w); 153 sem_close(cond_t); 154 155 pwrite(fd, &f, sizeof(f), 0); 156} 157 158/** 159 * xshmfence_open_semaphore: 160 * @f: An X fence 161 * 162 * Open the semaphore after mapping the fence 163 **/ 164void 165xshmfence_open_semaphore(struct xshmfence *f) 166{ 167 /* 168 * map process local memory to page 2 169 */ 170 if (mmap ((void*)&f->cond_w, 171 LIBXSHM_PAGESIZE, 172 PROT_READ|PROT_WRITE, 173 MAP_FIXED|MAP_ANON, 174 -1, 0) == MAP_FAILED) { 175 errx(EXIT_FAILURE, "xshmfence_open_semaphore: mmap failed"); 176 } 177 178 if ((f->cond_w = sem_open(f->condname_w, 0)) == SEM_FAILED) { 179 errx(EXIT_FAILURE, "xshmfence_open_semaphore: sem_open failed"); 180 } 181 182 if ((f->cond_t = sem_open(f->condname_t, 0)) == SEM_FAILED) { 183 errx(EXIT_FAILURE, "xshmfence_open_semaphore: sem_open failed"); 184 } 185 __sync_fetch_and_add(&f->refcnt, 1); 186} 187 188/** 189 * xshmfence_close_semaphore: 190 * @f: An X fence 191 * 192 * Close the semaphore before unmapping the fence 193 **/ 194void 195xshmfence_close_semaphore(struct xshmfence *f) 196{ 197 sem_close(f->cond_w); 198 sem_close(f->cond_t); 199 if (__sync_sub_and_fetch(&f->refcnt, 1) == 0) { 200 sem_unlink(f->condname_w); 201 sem_unlink(f->condname_t); 202 } 203} 204 205static sem_t * 206mksemtemp(char *name, size_t namelen, const char *suffix) 207{ 208 sem_t *ret; 209 pid_t p; 210 p = getpid(); 211 for(;;) { 212 if (snprintf(name, namelen, "/xshmf%s-%d", suffix, p) >= namelen) 213 return SEM_FAILED; 214 ret = sem_open(name, O_CREAT|O_EXCL, 0600, 0); 215 if (ret == SEM_FAILED) { 216 if (errno == EEXIST) { 217 p++; 218 continue; 219 } 220 } 221 return ret; 222 } 223} 224