| 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 | |