1/*
2Copyright (c) 2012, Broadcom Europe Ltd
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, 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
16THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23ON 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
25SOFTWARE, 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)
35static VCOS_LOG_CAT_T vcos_named_sem_log_cat =
36VCOS_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 */
68typedef 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
77static VCOS_MUTEX_T lock;
78static VCOS_NAMED_SEMAPHORE_IMPL_T* sems_in_use = NULL;
79static int sems_in_use_count = 0;
80static int sems_total_ref_count = 0;
81
82static VCOS_BLOCKPOOL_T sems_pool;
83static char pool_mem[VCOS_BLOCKPOOL_SIZE(
84 NUM_SEMS, sizeof(VCOS_NAMED_SEMAPHORE_IMPL_T), VCOS_BLOCKPOOL_ALIGN_DEFAULT)];
85
86VCOS_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
109fail_mutex:
110fail_extend:
111 vcos_blockpool_delete(&sems_pool);
112fail_blockpool:
113 return status;
114}
115
116void _vcos_named_semaphore_deinit(void)
117{
118 vcos_blockpool_delete(&sems_pool);
119 vcos_mutex_delete(&lock);
120 sems_in_use = NULL;
121}
122
123VCOS_STATUS_T
124vcos_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
223end:
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
235void 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