shl: add new register helper
[platform/upstream/kmscon.git] / src / shl_register.h
1 /*
2  * shl - Named-Objects Registers
3  *
4  * Copyright (c) 2012 David Herrmann <dh.herrmann@googlemail.com>
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining
7  * a copy of this software and associated documentation files
8  * (the "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sublicense, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included
15  * in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
21  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  */
25
26 /*
27  * Named-Objects Registers
28  * We often have an interface and several different backends that implement the
29  * interface. To allow dynamic loading/unloading of backends, we give each
30  * backend a name and vtable/data-ptr.
31  * Once registered, the backend can be searched for and then used. The
32  * shl_register object manages the backend loading/unloading in a thread-safe
33  * way so we can share the backends between threads. It also manages access to
34  * still used backends while they have been unregistered. Only after the last
35  * record of a backend has been dropped, the backend is fully unused.
36  */
37
38 #ifndef SHL_REGISTER_H
39 #define SHL_REGISTER_H
40
41 #include <errno.h>
42 #include <pthread.h>
43 #include <stdbool.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include "shl_dlist.h"
47
48 struct shl_register_record {
49         struct shl_dlist list;
50
51         pthread_mutex_t mutex;
52         unsigned long ref;
53         char *name;
54         void *data;
55 };
56
57 struct shl_register {
58         pthread_mutex_t mutex;
59         struct shl_dlist records;
60 };
61
62 #define SHL_REGISTER_INIT(name) { \
63                 .mutex = PTHREAD_MUTEX_INITIALIZER, \
64                 .records = SHL_DLIST_INIT((name).records), \
65         }
66
67 static inline void shl_register_record_ref(struct shl_register_record *record)
68 {
69         int ret;
70
71         if (!record)
72                 return;
73
74         ret = pthread_mutex_lock(&record->mutex);
75         if (record->ref)
76                 ++record->ref;
77         if (!ret)
78                 pthread_mutex_unlock(&record->mutex);
79 }
80
81 static inline void shl_register_record_unref(struct shl_register_record *record)
82 {
83         unsigned long ref = 1;
84         int ret;
85
86         if (!record)
87                 return;
88
89         ret = pthread_mutex_lock(&record->mutex);
90         if (record->ref)
91                 ref = --record->ref;
92         if (!ret)
93                 pthread_mutex_unlock(&record->mutex);
94
95         if (ref)
96                 return;
97
98         pthread_mutex_destroy(&record->mutex);
99         free(record->name);
100         free(record);
101 }
102
103 static inline int shl_register_new(struct shl_register **out)
104 {
105         struct shl_register *reg;
106         int ret;
107
108         if (!out)
109                 return -EINVAL;
110
111         reg = malloc(sizeof(*reg));
112         if (!reg)
113                 return -ENOMEM;
114         memset(reg, 0, sizeof(*reg));
115         shl_dlist_init(&reg->records);
116
117         ret = pthread_mutex_init(&reg->mutex, NULL);
118         if (ret) {
119                 ret = -EFAULT;
120                 goto err_free;
121         }
122
123         *out = reg;
124         return 0;
125
126 err_free:
127         free(reg);
128         return ret;
129 }
130
131 static inline void shl_register_free(struct shl_register *reg)
132 {
133         struct shl_dlist *iter;
134         struct shl_register_record *record;
135
136         if (!reg)
137                 return;
138
139         shl_dlist_for_each(iter, &reg->records) {
140                 record = shl_dlist_entry(iter, struct shl_register_record,
141                                          list);
142                 shl_dlist_unlink(&record->list);
143                 shl_register_record_unref(record);
144         }
145
146         pthread_mutex_destroy(&reg->mutex);
147         free(reg);
148 }
149
150 static inline int shl_register_add(struct shl_register *reg, const char *name,
151                                    void *data)
152 {
153         struct shl_dlist *iter;
154         struct shl_register_record *record;
155         int ret;
156
157         if (!reg || !name)
158                 return -EINVAL;
159
160         ret = pthread_mutex_lock(&reg->mutex);
161         if (ret)
162                 return -EFAULT;
163
164         shl_dlist_for_each(iter, &reg->records) {
165                 record = shl_dlist_entry(iter, struct shl_register_record,
166                                          list);
167                 if (!strcmp(record->name, name)) {
168                         ret = -EALREADY;
169                         goto out_unlock;
170                 }
171         }
172
173         record = malloc(sizeof(*record));
174         if (!record) {
175                 ret = -ENOMEM;
176                 goto out_unlock;
177         }
178         memset(record, 0, sizeof(*record));
179         record->ref = 1;
180         record->data = data;
181
182         ret = pthread_mutex_init(&record->mutex, NULL);
183         if (ret) {
184                 ret = -EFAULT;
185                 goto err_free;
186         }
187
188         record->name = strdup(name);
189         if (!record->name) {
190                 ret = -ENOMEM;
191                 goto err_mutex;
192         }
193
194         shl_dlist_link_tail(&reg->records, &record->list);
195         ret = 0;
196         goto out_unlock;
197
198 err_mutex:
199         pthread_mutex_destroy(&record->mutex);
200 err_free:
201         free(record);
202 out_unlock:
203         pthread_mutex_unlock(&reg->mutex);
204         return ret;
205 }
206
207 static inline void shl_register_remove(struct shl_register *reg,
208                                        const char *name)
209 {
210         struct shl_dlist *iter;
211         struct shl_register_record *record;
212         int ret;
213
214         if (!reg || !name)
215                 return;
216
217         ret = pthread_mutex_lock(&reg->mutex);
218         if (ret)
219                 return;
220
221         shl_dlist_for_each(iter, &reg->records) {
222                 record = shl_dlist_entry(iter, struct shl_register_record,
223                                          list);
224                 if (strcmp(record->name, name))
225                         continue;
226
227                 shl_dlist_unlink(&record->list);
228                 shl_register_record_unref(record);
229                 break;
230         }
231
232         pthread_mutex_unlock(&reg->mutex);
233 }
234
235 static inline struct shl_register_record *shl_register_find(
236                                                 struct shl_register *reg,
237                                                 const char *name)
238 {
239         struct shl_dlist *iter;
240         struct shl_register_record *record, *res;
241         int ret;
242
243         if (!reg || !name)
244                 return NULL;
245
246         ret = pthread_mutex_lock(&reg->mutex);
247         if (ret)
248                 return NULL;
249
250         res = NULL;
251         shl_dlist_for_each(iter, &reg->records) {
252                 record = shl_dlist_entry(iter, struct shl_register_record,
253                                          list);
254                 if (!strcmp(record->name, name)) {
255                         res = record;
256                         shl_register_record_ref(res);
257                         break;
258                 }
259         }
260
261         pthread_mutex_unlock(&reg->mutex);
262         return res;
263 }
264
265 static inline struct shl_register_record *shl_register_first(
266                                                 struct shl_register *reg)
267 {
268         int ret;
269         void *res;
270
271         if (!reg)
272                 return NULL;
273
274         ret = pthread_mutex_lock(&reg->mutex);
275         if (ret)
276                 return NULL;
277
278         if (shl_dlist_empty(&reg->records)) {
279                 res = NULL;
280         } else {
281                 res = shl_dlist_entry(reg->records.next,
282                                       struct shl_register_record, list);
283                 shl_register_record_ref(res);
284         }
285
286         pthread_mutex_unlock(&reg->mutex);
287         return res;
288 }
289
290 static inline struct shl_register_record *shl_register_last(
291                                                 struct shl_register *reg)
292 {
293         int ret;
294         void *res;
295
296         if (!reg)
297                 return NULL;
298
299         ret = pthread_mutex_lock(&reg->mutex);
300         if (ret)
301                 return NULL;
302
303         if (shl_dlist_empty(&reg->records)) {
304                 res = NULL;
305         } else {
306                 res = shl_dlist_entry(reg->records.prev,
307                                       struct shl_register_record, list);
308                 shl_register_record_ref(res);
309         }
310
311         pthread_mutex_unlock(&reg->mutex);
312         return res;
313 }
314
315 #endif /* SHL_REGISTER_H */