1 | /* |
2 | Copyright (c) 2012, Broadcom Europe Ltd |
3 | All rights reserved. |
4 | |
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted provided that the following conditions are met: |
7 | * Redistributions of source code must retain the above copyright |
8 | notice, this list of conditions and the following disclaimer. |
9 | * Redistributions in binary form must reproduce the above copyright |
10 | notice, this list of conditions and the following disclaimer in the |
11 | documentation and/or other materials provided with the distribution. |
12 | * Neither the name of the copyright holder nor the |
13 | names of its contributors may be used to endorse or promote products |
14 | derived from this software without specific prior written permission. |
15 | |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY |
20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
26 | */ |
27 | |
28 | #define VCOS_LOG_CATEGORY (&vcos_named_sem_log_cat) |
29 | |
30 | #include "interface/vcos/vcos.h" |
31 | #include "interface/vcos/generic/vcos_generic_named_sem.h" |
32 | #include "interface/vcos/vcos_blockpool.h" |
33 | |
34 | #if defined(VCOS_LOGGING_ENABLED) |
35 | static VCOS_LOG_CAT_T vcos_named_sem_log_cat = |
36 | VCOS_LOG_INIT("vcos_named_sem" , VCOS_LOG_ERROR); |
37 | #endif |
38 | |
39 | /** |
40 | * \file |
41 | * |
42 | * Named semaphores, primarily for VCFW. |
43 | * |
44 | * Does not actually work across processes; merely emulate the API. |
45 | * |
46 | * The client initialises a VCOS_NAMED_SEMAPHORE_T, but this merely |
47 | * points at the real underlying VCOS_NAMED_SEMAPHORE_IMPL_T. |
48 | * |
49 | * semaphore_t ---\ |
50 | * ----- semaphore_impl_t |
51 | * semaphore_t ---/ |
52 | * / |
53 | * semaphore_t -/ |
54 | * |
55 | */ |
56 | |
57 | /* Maintain a block pool of semaphore implementations */ |
58 | #define NUM_SEMS 16 |
59 | |
60 | /* Allow the pool to expand to MAX_SEMS in size */ |
61 | #define MAX_SEMS 512 |
62 | |
63 | /** Each actual real semaphore is stored in one of these. Clients just |
64 | * get a structure with a pointer to this in it. |
65 | * |
66 | * It also contains a doubly linked list tracking the semaphores in-use. |
67 | */ |
68 | typedef struct VCOS_NAMED_SEMAPHORE_IMPL_T |
69 | { |
70 | VCOS_SEMAPHORE_T sem; /**< Actual underlying semaphore */ |
71 | char name[VCOS_NAMED_SEMAPHORE_NAMELEN]; /**< Name of semaphore, copied */ |
72 | unsigned refs; /**< Reference count */ |
73 | struct VCOS_NAMED_SEMAPHORE_IMPL_T *next; /**< Next in the in-use list */ |
74 | struct VCOS_NAMED_SEMAPHORE_IMPL_T *prev; /**< Previous in the in-use list */ |
75 | } VCOS_NAMED_SEMAPHORE_IMPL_T; |
76 | |
77 | static VCOS_MUTEX_T lock; |
78 | static VCOS_NAMED_SEMAPHORE_IMPL_T* sems_in_use = NULL; |
79 | static int sems_in_use_count = 0; |
80 | static int sems_total_ref_count = 0; |
81 | |
82 | static VCOS_BLOCKPOOL_T sems_pool; |
83 | static char pool_mem[VCOS_BLOCKPOOL_SIZE( |
84 | NUM_SEMS, sizeof(VCOS_NAMED_SEMAPHORE_IMPL_T), VCOS_BLOCKPOOL_ALIGN_DEFAULT)]; |
85 | |
86 | VCOS_STATUS_T _vcos_named_semaphore_init() |
87 | { |
88 | VCOS_STATUS_T status; |
89 | |
90 | status = vcos_blockpool_init(&sems_pool, |
91 | NUM_SEMS, sizeof(VCOS_NAMED_SEMAPHORE_IMPL_T), |
92 | pool_mem, sizeof(pool_mem), |
93 | VCOS_BLOCKPOOL_ALIGN_DEFAULT, 0, "vcos named semaphores" ); |
94 | |
95 | if (status != VCOS_SUCCESS) |
96 | goto fail_blockpool; |
97 | |
98 | status = vcos_blockpool_extend(&sems_pool, VCOS_BLOCKPOOL_MAX_SUBPOOLS - 1, |
99 | (MAX_SEMS - NUM_SEMS) / (VCOS_BLOCKPOOL_MAX_SUBPOOLS - 1)); |
100 | if (status != VCOS_SUCCESS) |
101 | goto fail_extend; |
102 | |
103 | status = vcos_mutex_create(&lock, "vcosnmsem" ); |
104 | if (status != VCOS_SUCCESS) |
105 | goto fail_mutex; |
106 | |
107 | return status; |
108 | |
109 | fail_mutex: |
110 | fail_extend: |
111 | vcos_blockpool_delete(&sems_pool); |
112 | fail_blockpool: |
113 | return status; |
114 | } |
115 | |
116 | void _vcos_named_semaphore_deinit(void) |
117 | { |
118 | vcos_blockpool_delete(&sems_pool); |
119 | vcos_mutex_delete(&lock); |
120 | sems_in_use = NULL; |
121 | } |
122 | |
123 | VCOS_STATUS_T |
124 | vcos_generic_named_semaphore_create(VCOS_NAMED_SEMAPHORE_T *sem, |
125 | const char *name, VCOS_UNSIGNED count) |
126 | { |
127 | VCOS_STATUS_T status = VCOS_ENOSPC; |
128 | int name_len, cmp = -1; |
129 | VCOS_NAMED_SEMAPHORE_IMPL_T *impl; |
130 | VCOS_NAMED_SEMAPHORE_IMPL_T *new_impl; |
131 | |
132 | vcos_log_trace("%s: sem %p name %s count %d" , __FUNCTION__, |
133 | sem, (name ? name : "null" ), count); |
134 | |
135 | vcos_assert(name); |
136 | |
137 | vcos_mutex_lock(&lock); |
138 | name_len = vcos_strlen(name); |
139 | if (name_len >= VCOS_NAMED_SEMAPHORE_NAMELEN) |
140 | { |
141 | vcos_assert(0); |
142 | status = VCOS_EINVAL; |
143 | goto end; |
144 | } |
145 | |
146 | /* do we already have this semaphore? */ |
147 | impl = sems_in_use; |
148 | while (impl && (cmp = vcos_strcmp(name, impl->name)) < 0) |
149 | impl = impl->next; |
150 | |
151 | if (impl && cmp == 0) |
152 | { |
153 | /* Semaphore is already in use so just increase the ref count */ |
154 | impl->refs++; |
155 | sems_total_ref_count++; |
156 | sem->actual = impl; |
157 | sem->sem = &impl->sem; |
158 | status = VCOS_SUCCESS; |
159 | vcos_log_trace( |
160 | "%s: ref count %d name %s total refs %d num sems %d" , |
161 | __FUNCTION__, impl->refs, impl->name, |
162 | sems_total_ref_count, sems_in_use_count); |
163 | goto end; |
164 | } |
165 | |
166 | /* search for unused semaphore */ |
167 | new_impl = vcos_blockpool_calloc(&sems_pool); |
168 | if (new_impl) |
169 | { |
170 | status = vcos_semaphore_create(&new_impl->sem, name, count); |
171 | if (status == VCOS_SUCCESS) |
172 | { |
173 | new_impl->refs = 1; |
174 | sems_total_ref_count++; |
175 | sems_in_use_count++; |
176 | memcpy(new_impl->name, name, name_len + 1); /* already checked length! */ |
177 | sem->actual = new_impl; |
178 | sem->sem = &new_impl->sem; |
179 | |
180 | /* Insert into the sorted list |
181 | * impl is either NULL or the first element where |
182 | * name > impl->name. |
183 | */ |
184 | if (impl) |
185 | { |
186 | new_impl->prev = impl->prev; |
187 | impl->prev = new_impl; |
188 | new_impl->next = impl; |
189 | |
190 | if (new_impl->prev) |
191 | new_impl->prev->next = new_impl; |
192 | } |
193 | else |
194 | { |
195 | /* Appending to the tail of the list / empty list */ |
196 | VCOS_NAMED_SEMAPHORE_IMPL_T *tail = sems_in_use; |
197 | while(tail && tail->next) |
198 | tail = tail->next; |
199 | |
200 | if (tail) |
201 | { |
202 | tail->next = new_impl; |
203 | new_impl->prev = tail; |
204 | } |
205 | } |
206 | |
207 | if (sems_in_use == impl) |
208 | { |
209 | /* Inserted at head or list was empty */ |
210 | sems_in_use = new_impl; |
211 | } |
212 | |
213 | vcos_log_trace( |
214 | "%s: new ref actual %p prev %p next %p count %d name %s " \ |
215 | "total refs %d num sems %d" , |
216 | __FUNCTION__, |
217 | new_impl, new_impl->prev, new_impl->next, |
218 | new_impl->refs, new_impl->name, |
219 | sems_total_ref_count, sems_in_use_count); |
220 | } |
221 | } |
222 | |
223 | end: |
224 | vcos_mutex_unlock(&lock); |
225 | if (status != VCOS_SUCCESS) |
226 | { |
227 | vcos_log_error("%s: failed to create named semaphore name %s status %d " \ |
228 | "total refs %d num sems %d" , |
229 | __FUNCTION__, (name ? name : "NULL" ), status, |
230 | sems_total_ref_count, sems_in_use_count); |
231 | } |
232 | return status; |
233 | } |
234 | |
235 | void vcos_named_semaphore_delete(VCOS_NAMED_SEMAPHORE_T *sem) |
236 | { |
237 | VCOS_NAMED_SEMAPHORE_IMPL_T *actual = sem->actual; |
238 | vcos_mutex_lock(&lock); |
239 | |
240 | /* if this fires, the semaphore has already been deleted */ |
241 | vcos_assert(actual->refs); |
242 | |
243 | vcos_log_trace( |
244 | "%s: actual %p ref count %d name %s prev %p next %p total refs %d num sems %d" , |
245 | __FUNCTION__, actual, actual->refs, actual->name, |
246 | actual->prev, actual->next, |
247 | sems_total_ref_count, sems_in_use_count); |
248 | |
249 | sems_total_ref_count--; |
250 | if (--actual->refs == 0) |
251 | { |
252 | sems_in_use_count--; |
253 | if (actual->prev) |
254 | actual->prev->next = actual->next; |
255 | |
256 | if (actual->next) |
257 | actual->next->prev = actual->prev; |
258 | |
259 | if (sems_in_use == actual) |
260 | sems_in_use = actual->next; |
261 | |
262 | vcos_semaphore_delete(&actual->sem); |
263 | sem->actual = NULL; |
264 | sem->sem = NULL; |
265 | vcos_blockpool_free(actual); |
266 | } |
267 | vcos_mutex_unlock(&lock); |
268 | } |
269 | |