Modify it to adjust Tizen IVI enviroment
[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 typedef void (*shl_register_destroy_cb) (void *data);
49
50 struct shl_register_record {
51         struct shl_dlist list;
52
53         pthread_mutex_t mutex;
54         unsigned long ref;
55         char *name;
56         void *data;
57         shl_register_destroy_cb destroy;
58 };
59
60 struct shl_register {
61         pthread_mutex_t mutex;
62         struct shl_dlist records;
63 };
64
65 #define SHL_REGISTER_INIT(name) { \
66                 .mutex = PTHREAD_MUTEX_INITIALIZER, \
67                 .records = SHL_DLIST_INIT((name).records), \
68         }
69
70 static inline void shl_register_record_ref(struct shl_register_record *record)
71 {
72         int ret;
73
74         if (!record)
75                 return;
76
77         ret = pthread_mutex_lock(&record->mutex);
78         if (record->ref)
79                 ++record->ref;
80         if (!ret)
81                 pthread_mutex_unlock(&record->mutex);
82 }
83
84 static inline void shl_register_record_unref(struct shl_register_record *record)
85 {
86         unsigned long ref = 1;
87         int ret;
88
89         if (!record)
90                 return;
91
92         ret = pthread_mutex_lock(&record->mutex);
93         if (record->ref)
94                 ref = --record->ref;
95         if (!ret)
96                 pthread_mutex_unlock(&record->mutex);
97
98         if (ref)
99                 return;
100
101         if (record->destroy)
102                 record->destroy(record->data);
103         pthread_mutex_destroy(&record->mutex);
104         free(record->name);
105         free(record);
106 }
107
108 static inline int shl_register_new(struct shl_register **out)
109 {
110         struct shl_register *reg;
111         int ret;
112
113         if (!out)
114                 return -EINVAL;
115
116         reg = malloc(sizeof(*reg));
117         if (!reg)
118                 return -ENOMEM;
119         memset(reg, 0, sizeof(*reg));
120         shl_dlist_init(&reg->records);
121
122         ret = pthread_mutex_init(&reg->mutex, NULL);
123         if (ret) {
124                 ret = -EFAULT;
125                 goto err_free;
126         }
127
128         *out = reg;
129         return 0;
130
131 err_free:
132         free(reg);
133         return ret;
134 }
135
136 static inline void shl_register_free(struct shl_register *reg)
137 {
138         struct shl_dlist *iter;
139         struct shl_register_record *record;
140
141         if (!reg)
142                 return;
143
144         shl_dlist_for_each(iter, &reg->records) {
145                 record = shl_dlist_entry(iter, struct shl_register_record,
146                                          list);
147                 shl_dlist_unlink(&record->list);
148                 shl_register_record_unref(record);
149         }
150
151         pthread_mutex_destroy(&reg->mutex);
152         free(reg);
153 }
154
155 static inline int shl_register_add_cb(struct shl_register *reg,
156                                       const char *name, void *data,
157                                       shl_register_destroy_cb destroy)
158 {
159         struct shl_dlist *iter;
160         struct shl_register_record *record;
161         int ret;
162
163         if (!reg || !name)
164                 return -EINVAL;
165
166         ret = pthread_mutex_lock(&reg->mutex);
167         if (ret)
168                 return -EFAULT;
169
170         shl_dlist_for_each(iter, &reg->records) {
171                 record = shl_dlist_entry(iter, struct shl_register_record,
172                                          list);
173                 if (!strcmp(record->name, name)) {
174                         ret = -EALREADY;
175                         goto out_unlock;
176                 }
177         }
178
179         record = malloc(sizeof(*record));
180         if (!record) {
181                 ret = -ENOMEM;
182                 goto out_unlock;
183         }
184         memset(record, 0, sizeof(*record));
185         record->ref = 1;
186         record->data = data;
187         record->destroy = destroy;
188
189         ret = pthread_mutex_init(&record->mutex, NULL);
190         if (ret) {
191                 ret = -EFAULT;
192                 goto err_free;
193         }
194
195         record->name = strdup(name);
196         if (!record->name) {
197                 ret = -ENOMEM;
198                 goto err_mutex;
199         }
200
201         shl_dlist_link_tail(&reg->records, &record->list);
202         ret = 0;
203         goto out_unlock;
204
205 err_mutex:
206         pthread_mutex_destroy(&record->mutex);
207 err_free:
208         free(record);
209 out_unlock:
210         pthread_mutex_unlock(&reg->mutex);
211         return ret;
212 }
213
214 static inline int shl_register_add(struct shl_register *reg, const char *name,
215                                    void *data)
216 {
217         return shl_register_add_cb(reg, name, data, NULL);
218 }
219
220 static inline void shl_register_remove(struct shl_register *reg,
221                                        const char *name)
222 {
223         struct shl_dlist *iter;
224         struct shl_register_record *record;
225         int ret;
226
227         if (!reg || !name)
228                 return;
229
230         ret = pthread_mutex_lock(&reg->mutex);
231         if (ret)
232                 return;
233
234         shl_dlist_for_each(iter, &reg->records) {
235                 record = shl_dlist_entry(iter, struct shl_register_record,
236                                          list);
237                 if (strcmp(record->name, name))
238                         continue;
239
240                 shl_dlist_unlink(&record->list);
241                 shl_register_record_unref(record);
242                 break;
243         }
244
245         pthread_mutex_unlock(&reg->mutex);
246 }
247
248 static inline struct shl_register_record *shl_register_find(
249                                                 struct shl_register *reg,
250                                                 const char *name)
251 {
252         struct shl_dlist *iter;
253         struct shl_register_record *record, *res;
254         int ret;
255
256         if (!reg || !name)
257                 return NULL;
258
259         ret = pthread_mutex_lock(&reg->mutex);
260         if (ret)
261                 return NULL;
262
263         res = NULL;
264         shl_dlist_for_each(iter, &reg->records) {
265                 record = shl_dlist_entry(iter, struct shl_register_record,
266                                          list);
267                 if (!strcmp(record->name, name)) {
268                         res = record;
269                         shl_register_record_ref(res);
270                         break;
271                 }
272         }
273
274         pthread_mutex_unlock(&reg->mutex);
275         return res;
276 }
277
278 static inline struct shl_register_record *shl_register_first(
279                                                 struct shl_register *reg)
280 {
281         int ret;
282         void *res;
283
284         if (!reg)
285                 return NULL;
286
287         ret = pthread_mutex_lock(&reg->mutex);
288         if (ret)
289                 return NULL;
290
291         if (shl_dlist_empty(&reg->records)) {
292                 res = NULL;
293         } else {
294                 res = shl_dlist_entry(reg->records.next,
295                                       struct shl_register_record, list);
296                 shl_register_record_ref(res);
297         }
298
299         pthread_mutex_unlock(&reg->mutex);
300         return res;
301 }
302
303 static inline struct shl_register_record *shl_register_last(
304                                                 struct shl_register *reg)
305 {
306         int ret;
307         void *res;
308
309         if (!reg)
310                 return NULL;
311
312         ret = pthread_mutex_lock(&reg->mutex);
313         if (ret)
314                 return NULL;
315
316         if (shl_dlist_empty(&reg->records)) {
317                 res = NULL;
318         } else {
319                 res = shl_dlist_entry(reg->records.prev,
320                                       struct shl_register_record, list);
321                 shl_register_record_ref(res);
322         }
323
324         pthread_mutex_unlock(&reg->mutex);
325         return res;
326 }
327
328 #endif /* SHL_REGISTER_H */