1 /*** 2 This file is part of PulseAudio. 3 4 Copyright 2009 Lennart Poettering 5 6 PulseAudio is free software; you can redistribute it and/or modify 7 it under the terms of the GNU Lesser General Public License as 8 published by the Free Software Foundation; either version 2.1 of the 9 License, or (at your option) any later version. 10 11 PulseAudio is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU Lesser General Public 17 License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 18 ***/ 19 20 #ifdef HAVE_CONFIG_H 21 #include <config.h> 22 #endif 23 24 #include <pulse/xmalloc.h> 25 26 #include <pulsecore/semaphore.h> 27 #include <pulsecore/macro.h> 28 #include <pulsecore/mutex.h> 29 30 #include "aupdate.h" 31 32 #define MSB (1U << (sizeof(unsigned)*8U-1)) 33 #define WHICH(n) (!!((n) & MSB)) 34 #define COUNTER(n) ((n) & ~MSB) 35 36 struct pa_aupdate { 37 pa_atomic_t read_lock; 38 pa_mutex *write_lock; 39 pa_semaphore *semaphore; 40 bool swapped; 41 }; 42 pa_aupdate_new(void)43pa_aupdate *pa_aupdate_new(void) { 44 pa_aupdate *a; 45 46 a = pa_xnew(pa_aupdate, 1); 47 pa_atomic_store(&a->read_lock, 0); 48 a->write_lock = pa_mutex_new(false, false); 49 a->semaphore = pa_semaphore_new(0); 50 51 return a; 52 } 53 pa_aupdate_free(pa_aupdate * a)54void pa_aupdate_free(pa_aupdate *a) { 55 pa_assert(a); 56 57 pa_mutex_free(a->write_lock); 58 pa_semaphore_free(a->semaphore); 59 60 pa_xfree(a); 61 } 62 pa_aupdate_read_begin(pa_aupdate * a)63unsigned pa_aupdate_read_begin(pa_aupdate *a) { 64 unsigned n; 65 66 pa_assert(a); 67 68 /* Increase the lock counter */ 69 n = (unsigned) pa_atomic_inc(&a->read_lock); 70 71 /* When n is 0 we have about 2^31 threads running that all try to 72 * access the data at the same time, oh my! */ 73 pa_assert(COUNTER(n)+1 > 0); 74 75 /* The uppermost bit tells us which data to look at */ 76 return WHICH(n); 77 } 78 pa_aupdate_read_end(pa_aupdate * a)79void pa_aupdate_read_end(pa_aupdate *a) { 80 unsigned PA_UNUSED n; 81 82 pa_assert(a); 83 84 /* Decrease the lock counter */ 85 n = (unsigned) pa_atomic_dec(&a->read_lock); 86 87 /* Make sure the counter was valid */ 88 pa_assert(COUNTER(n) > 0); 89 90 /* Post the semaphore */ 91 pa_semaphore_post(a->semaphore); 92 } 93 pa_aupdate_write_begin(pa_aupdate * a)94unsigned pa_aupdate_write_begin(pa_aupdate *a) { 95 unsigned n; 96 97 pa_assert(a); 98 99 pa_mutex_lock(a->write_lock); 100 101 n = (unsigned) pa_atomic_load(&a->read_lock); 102 103 a->swapped = false; 104 105 return !WHICH(n); 106 } 107 pa_aupdate_write_swap(pa_aupdate * a)108unsigned pa_aupdate_write_swap(pa_aupdate *a) { 109 unsigned n; 110 111 pa_assert(a); 112 113 for (;;) { 114 n = (unsigned) pa_atomic_load(&a->read_lock); 115 116 /* If the read counter is > 0 wait; if it is 0 try to swap the lists */ 117 if (COUNTER(n) > 0) 118 pa_semaphore_wait(a->semaphore); 119 else if (pa_atomic_cmpxchg(&a->read_lock, (int) n, (int) (n ^ MSB))) 120 break; 121 } 122 123 a->swapped = true; 124 125 return WHICH(n); 126 } 127 pa_aupdate_write_end(pa_aupdate * a)128void pa_aupdate_write_end(pa_aupdate *a) { 129 pa_assert(a); 130 131 if (!a->swapped) 132 pa_aupdate_write_swap(a); 133 134 pa_mutex_unlock(a->write_lock); 135 } 136