/*** This file is part of PulseAudio. Copyright 2009 Lennart Poettering PulseAudio is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. PulseAudio is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with PulseAudio; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. ***/ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "aupdate.h" #define MSB (1U << (sizeof(unsigned)*8U-1)) #define WHICH(n) (!!((n) & MSB)) #define COUNTER(n) ((n) & ~MSB) struct pa_aupdate { pa_atomic_t read_lock; pa_mutex *write_lock; pa_semaphore *semaphore; pa_bool_t swapped; }; pa_aupdate *pa_aupdate_new(void) { pa_aupdate *a; a = pa_xnew(pa_aupdate, 1); pa_atomic_store(&a->read_lock, 0); a->write_lock = pa_mutex_new(FALSE, FALSE); a->semaphore = pa_semaphore_new(0); return a; } void pa_aupdate_free(pa_aupdate *a) { pa_assert(a); pa_mutex_free(a->write_lock); pa_semaphore_free(a->semaphore); pa_xfree(a); } unsigned pa_aupdate_read_begin(pa_aupdate *a) { unsigned n; pa_assert(a); /* Increase the lock counter */ n = (unsigned) pa_atomic_inc(&a->read_lock); /* When n is 0 we have about 2^31 threads running that all try to * access the data at the same time, oh my! */ pa_assert(COUNTER(n)+1 > 0); /* The uppermost bit tells us which data to look at */ return WHICH(n); } void pa_aupdate_read_end(pa_aupdate *a) { unsigned n; pa_assert(a); /* Decrease the lock counter */ n = (unsigned) pa_atomic_dec(&a->read_lock); /* Make sure the counter was valid */ pa_assert(COUNTER(n) > 0); /* Post the semaphore */ pa_semaphore_post(a->semaphore); } unsigned pa_aupdate_write_begin(pa_aupdate *a) { unsigned n; pa_assert(a); pa_mutex_lock(a->write_lock); n = (unsigned) pa_atomic_load(&a->read_lock); a->swapped = FALSE; return !WHICH(n); } unsigned pa_aupdate_write_swap(pa_aupdate *a) { unsigned n; pa_assert(a); for (;;) { n = (unsigned) pa_atomic_load(&a->read_lock); /* If the read counter is > 0 wait; if it is 0 try to swap the lists */ if (COUNTER(n) > 0) pa_semaphore_wait(a->semaphore); else if (pa_atomic_cmpxchg(&a->read_lock, (int) n, (int) (n ^ MSB))) break; } a->swapped = TRUE; return WHICH(n); } void pa_aupdate_write_end(pa_aupdate *a) { pa_assert(a); if (!a->swapped) pa_aupdate_write_swap(a); pa_mutex_unlock(a->write_lock); }