• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***
2   This file is part of PulseAudio.
3 
4   Copyright 2006-2008 Lennart Poettering
5   Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
6   Copyright (C) 2012 Canonical Ltd.
7 
8   Contact: Jyri Sarha <Jyri.Sarha@nokia.com>
9 
10   PulseAudio is free software; you can redistribute it and/or modify
11   it under the terms of the GNU Lesser General Public License as
12   published by the Free Software Foundation; either version 2.1 of the
13   License, or (at your option) any later version.
14 
15   PulseAudio is distributed in the hope that it will be useful, but
16   WITHOUT ANY WARRANTY; without even the implied warranty of
17   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18   Lesser General Public License for more details.
19 
20   You should have received a copy of the GNU Lesser General Public
21   License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
22 ***/
23 
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27 
28 #include <pulse/xmalloc.h>
29 
30 #include <pulsecore/atomic.h>
31 #include <pulsecore/log.h>
32 #include <pulsecore/macro.h>
33 #include <pulsecore/core-util.h>
34 #include <pulsecore/macro.h>
35 
36 #include "flist.h"
37 
38 #define FLIST_SIZE 256
39 
40 /* Atomic table indices contain
41    sign bit = if set, indicates empty/NULL value
42    tag bits (to avoid the ABA problem)
43    actual index bits
44 */
45 
46 /* Lock free single linked list element. */
47 struct pa_flist_elem {
48     pa_atomic_t next;
49     pa_atomic_ptr_t ptr;
50 };
51 
52 typedef struct pa_flist_elem pa_flist_elem;
53 
54 struct pa_flist {
55     char *name;
56     unsigned size;
57 
58     pa_atomic_t current_tag;
59     int index_mask;
60     int tag_shift;
61     int tag_mask;
62 
63     /* Stack that contains pointers stored into free list */
64     pa_atomic_t stored;
65     /* Stack that contains empty list elements */
66     pa_atomic_t empty;
67     pa_flist_elem table[];
68 };
69 
70 /* Lock free pop from linked list stack */
stack_pop(pa_flist * flist,pa_atomic_t * list)71 static pa_flist_elem *stack_pop(pa_flist *flist, pa_atomic_t *list) {
72     pa_flist_elem *popped;
73     int idx;
74     pa_assert(list);
75 
76     do {
77         idx = pa_atomic_load(list);
78         if (idx < 0)
79             return NULL;
80         popped = &flist->table[idx & flist->index_mask];
81     } while (!pa_atomic_cmpxchg(list, idx, pa_atomic_load(&popped->next)));
82 
83     return popped;
84 }
85 
86 /* Lock free push to linked list stack */
stack_push(pa_flist * flist,pa_atomic_t * list,pa_flist_elem * new_elem)87 static void stack_push(pa_flist *flist, pa_atomic_t *list, pa_flist_elem *new_elem) {
88     int tag, newindex, next;
89     pa_assert(list);
90 
91     tag = pa_atomic_inc(&flist->current_tag);
92     newindex = new_elem - flist->table;
93     pa_assert(newindex >= 0 && newindex < (int) flist->size);
94     newindex |= (tag << flist->tag_shift) & flist->tag_mask;
95 
96     do {
97         next = pa_atomic_load(list);
98         pa_atomic_store(&new_elem->next, next);
99     } while (!pa_atomic_cmpxchg(list, next, newindex));
100 }
101 
pa_flist_new_with_name(unsigned size,const char * name)102 pa_flist *pa_flist_new_with_name(unsigned size, const char *name) {
103     pa_flist *l;
104     unsigned i;
105     pa_assert(name);
106 
107     if (!size)
108         size = FLIST_SIZE;
109 
110     l = pa_xmalloc0(sizeof(pa_flist) + sizeof(pa_flist_elem) * size);
111 
112     l->name = pa_xstrdup(name);
113     l->size = size;
114 
115     while (1 << l->tag_shift < (int) size)
116         l->tag_shift++;
117     l->index_mask = (1 << l->tag_shift) - 1;
118     l->tag_mask = INT_MAX - l->index_mask;
119 
120     pa_atomic_store(&l->stored, -1);
121     pa_atomic_store(&l->empty, -1);
122     for (i=0; i < size; i++) {
123         stack_push(l, &l->empty, &l->table[i]);
124     }
125     return l;
126 }
127 
pa_flist_new(unsigned size)128 pa_flist *pa_flist_new(unsigned size) {
129     return pa_flist_new_with_name(size, "unknown");
130 }
131 
pa_flist_free(pa_flist * l,pa_free_cb_t free_cb)132 void pa_flist_free(pa_flist *l, pa_free_cb_t free_cb) {
133     pa_assert(l);
134     pa_assert(l->name);
135 
136     if (free_cb) {
137         pa_flist_elem *elem;
138         while((elem = stack_pop(l, &l->stored)))
139             free_cb(pa_atomic_ptr_load(&elem->ptr));
140     }
141 
142     pa_xfree(l->name);
143     pa_xfree(l);
144 }
145 
pa_flist_push(pa_flist * l,void * p)146 int pa_flist_push(pa_flist *l, void *p) {
147     pa_flist_elem *elem;
148     pa_assert(l);
149     pa_assert(p);
150 
151     elem = stack_pop(l, &l->empty);
152     if (elem == NULL) {
153         if (pa_log_ratelimit(PA_LOG_DEBUG))
154             pa_log_debug("%s flist is full (don't worry)", l->name);
155         return -1;
156     }
157     pa_atomic_ptr_store(&elem->ptr, p);
158     stack_push(l, &l->stored, elem);
159 
160     return 0;
161 }
162 
pa_flist_pop(pa_flist * l)163 void* pa_flist_pop(pa_flist *l) {
164     pa_flist_elem *elem;
165     void *ptr;
166     pa_assert(l);
167 
168     elem = stack_pop(l, &l->stored);
169     if (elem == NULL)
170         return NULL;
171 
172     ptr = pa_atomic_ptr_load(&elem->ptr);
173 
174     stack_push(l, &l->empty, elem);
175 
176     return ptr;
177 }
178