/* ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio. This file is part of ChibiOS. ChibiOS is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. ChibiOS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /** * @file oslib/src/chfactory.c * @brief ChibiOS objects factory and registry code. * * @addtogroup oslib_objects_factory * @details The object factory is a subsystem that allows to: * - Register static objects by name. * - Dynamically create objects and assign them a name. * - Retrieve existing objects by name. * - Free objects by reference. * . * Allocated OS objects are handled using a reference counter, only * when all references have been released then the object memory is * freed in a pool.
* @pre This subsystem requires the @p CH_CFG_USE_MEMCORE and * @p CH_CFG_USE_MEMPOOLS options to be set to @p TRUE. The * option @p CH_CFG_USE_HEAP is also required if the support * for variable length objects is enabled. * @note Compatible with RT and NIL. * @{ */ #include #include "ch.h" #if (CH_CFG_USE_FACTORY == TRUE) || defined(__DOXYGEN__) /*===========================================================================*/ /* Module local definitions. */ /*===========================================================================*/ /* * Defaults on the best synchronization mechanism available. */ #if (CH_CFG_USE_MUTEXES == TRUE) || defined(__DOXYGEN__) #define F_LOCK() chMtxLock(&ch_factory.mtx) #define F_UNLOCK() chMtxUnlock(&ch_factory.mtx) #else #define F_LOCK() (void) chSemWait(&ch_factory.sem) #define F_UNLOCK() chSemSignal(&ch_factory.sem) #endif /*===========================================================================*/ /* Module exported variables. */ /*===========================================================================*/ /** * @brief Factory object static instance. * @note It is a global object because it could be accessed through * a specific debugger plugin. */ objects_factory_t ch_factory; /*===========================================================================*/ /* Module local types. */ /*===========================================================================*/ /*===========================================================================*/ /* Module local variables. */ /*===========================================================================*/ /*===========================================================================*/ /* Module local functions. */ /*===========================================================================*/ static inline void dyn_list_init(dyn_list_t *dlp) { dlp->next = (dyn_element_t *)dlp; } static dyn_element_t *dyn_list_find(const char *name, dyn_list_t *dlp) { dyn_element_t *p = dlp->next; while (p != (dyn_element_t *)dlp) { if (strncmp(p->name, name, CH_CFG_FACTORY_MAX_NAMES_LENGTH) == 0) { return p; } p = p->next; } return NULL; } static dyn_element_t *dyn_list_unlink(dyn_element_t *element, dyn_list_t *dlp) { dyn_element_t *prev = (dyn_element_t *)dlp; /* Scanning the list.*/ while (prev->next != (dyn_element_t *)dlp) { if (prev->next == element) { /* Found.*/ prev->next = element->next; return element; } /* Next element in the list.*/ prev = prev->next; } return NULL; } #if CH_FACTORY_REQUIRES_HEAP || defined(__DOXYGEN__) static dyn_element_t *dyn_create_object_heap(const char *name, dyn_list_t *dlp, size_t size) { dyn_element_t *dep; chDbgCheck(name != NULL); /* Checking if an object with this name has already been created.*/ dep = dyn_list_find(name, dlp); if (dep != NULL) { return NULL; } /* Allocating space for the new buffer object.*/ /*lint -save -e668 [] Lint is confused by the above chDbgCheck() and incorrectly assumes that strncpy() could receive a NULL pointer.*/ dep = (dyn_element_t *)chHeapAlloc(NULL, size); if (dep == NULL) { return NULL; } /* Initializing object list element.*/ strncpy(dep->name, name, CH_CFG_FACTORY_MAX_NAMES_LENGTH); /*lint -restore*/ dep->refs = (ucnt_t)1; dep->next = dlp->next; /* Updating factory list.*/ dlp->next = dep; return dep; } static void dyn_release_object_heap(dyn_element_t *dep, dyn_list_t *dlp) { chDbgCheck(dep != NULL); chDbgAssert(dep->refs > (ucnt_t)0, "invalid references number"); dep->refs--; if (dep->refs == (ucnt_t)0) { dep = dyn_list_unlink(dep, dlp); chHeapFree((void *)dep); } } #endif /* CH_FACTORY_REQUIRES_HEAP */ #if CH_FACTORY_REQUIRES_POOLS || defined(__DOXYGEN__) static dyn_element_t *dyn_create_object_pool(const char *name, dyn_list_t *dlp, memory_pool_t *mp) { dyn_element_t *dep; chDbgCheck(name != NULL); /* Checking if an object object with this name has already been created.*/ dep = dyn_list_find(name, dlp); if (dep != NULL) { return NULL; } /* Allocating space for the new object.*/ dep = (dyn_element_t *)chPoolAlloc(mp); if (dep == NULL) { return NULL; } /* Initializing object list element.*/ /*lint -save -e668 [] Lint is confused by the above chDbgCheck() and incorrectly assumes that strncpy() could receive a NULL pointer.*/ strncpy(dep->name, name, CH_CFG_FACTORY_MAX_NAMES_LENGTH); /*lint -restore*/ dep->refs = (ucnt_t)1; dep->next = dlp->next; /* Updating factory list.*/ dlp->next = (dyn_element_t *)dep; return dep; } static void dyn_release_object_pool(dyn_element_t *dep, dyn_list_t *dlp, memory_pool_t *mp) { chDbgCheck(dep != NULL); chDbgAssert(dep->refs > (ucnt_t)0, "invalid references number"); dep->refs--; if (dep->refs == (ucnt_t)0) { dep = dyn_list_unlink(dep, dlp); chPoolFree(mp, (void *)dep); } } #endif /* CH_FACTORY_REQUIRES_POOLS */ static dyn_element_t *dyn_find_object(const char *name, dyn_list_t *dlp) { dyn_element_t *dep; chDbgCheck(name != NULL); /* Checking if an object with this name has already been created.*/ dep = dyn_list_find(name, dlp); if (dep != NULL) { /* Increasing references counter.*/ dep->refs++; } return dep; } /*===========================================================================*/ /* Module exported functions. */ /*===========================================================================*/ /** * @brief Initializes the objects factory. * * @init */ void _factory_init(void) { #if (CH_CFG_USE_MUTEXES == TRUE) || defined(__DOXYGEN__) chMtxObjectInit(&ch_factory.mtx); #else chSemObjectInit(&ch_factory.sem, (cnt_t)1); #endif #if CH_CFG_FACTORY_OBJECTS_REGISTRY == TRUE dyn_list_init(&ch_factory.obj_list); chPoolObjectInit(&ch_factory.obj_pool, sizeof (registered_object_t), chCoreAllocAlignedI); #endif #if CH_CFG_FACTORY_GENERIC_BUFFERS == TRUE dyn_list_init(&ch_factory.buf_list); #endif #if CH_CFG_FACTORY_SEMAPHORES == TRUE dyn_list_init(&ch_factory.sem_list); chPoolObjectInit(&ch_factory.sem_pool, sizeof (dyn_semaphore_t), chCoreAllocAlignedI); #endif #if CH_CFG_FACTORY_MAILBOXES == TRUE dyn_list_init(&ch_factory.mbx_list); #endif #if CH_CFG_FACTORY_OBJ_FIFOS == TRUE dyn_list_init(&ch_factory.fifo_list); #endif #if CH_CFG_FACTORY_PIPES == TRUE dyn_list_init(&ch_factory.pipe_list); #endif } #if (CH_CFG_FACTORY_OBJECTS_REGISTRY == TRUE) || defined(__DOXIGEN__) /** * @brief Registers a generic object. * @post A reference to the registered object is returned and the * reference counter is initialized to one. * * @param[in] name name to be assigned to the registered object * @param[in] objp pointer to the object to be registered * * @return The reference to the registered object. * @retval NULL if the object to be registered cannot be allocated or * a registered object with the same name exists. * * @api */ registered_object_t *chFactoryRegisterObject(const char *name, void *objp) { registered_object_t *rop; F_LOCK(); rop = (registered_object_t *)dyn_create_object_pool(name, &ch_factory.obj_list, &ch_factory.obj_pool); if (rop != NULL) { /* Initializing registered object data.*/ rop->objp = objp; } F_UNLOCK(); return rop; } /** * @brief Retrieves a registered object. * @post A reference to the registered object is returned with the * reference counter increased by one. * * @param[in] name name of the registered object * * @return The reference to the found registered object. * @retval NULL if a registered object with the specified name * does not exist. * * @api */ registered_object_t *chFactoryFindObject(const char *name) { registered_object_t *rop; F_LOCK(); rop = (registered_object_t *)dyn_find_object(name, &ch_factory.obj_list); F_UNLOCK(); return rop; } /** * @brief Retrieves a registered object by pointer. * @post A reference to the registered object is returned with the * reference counter increased by one. * * @param[in] objp pointer to the object to be retrieved * * @return The reference to the found registered object. * @retval NULL if a registered object with the specified pointer * does not exist. * * @api */ registered_object_t *chFactoryFindObjectByPointer(void *objp) { registered_object_t *rop = (registered_object_t *)ch_factory.obj_list.next; F_LOCK(); while ((void *)rop != (void *)&ch_factory.obj_list) { if (rop->objp == objp) { rop->element.refs++; F_UNLOCK(); return rop; } rop = (registered_object_t *)rop->element.next; } F_UNLOCK(); return NULL; } /** * @brief Releases a registered object. * @details The reference counter of the registered object is decreased * by one, if reaches zero then the registered object memory * is freed. * @note The object itself is not freed, it could be static, only the * allocated list element is freed. * * @param[in] rop registered object reference * * @api */ void chFactoryReleaseObject(registered_object_t *rop) { F_LOCK(); dyn_release_object_pool(&rop->element, &ch_factory.obj_list, &ch_factory.obj_pool); F_UNLOCK(); } #endif /* CH_CFG_FACTORY_OBJECTS_REGISTRY == TRUE */ #if (CH_CFG_FACTORY_GENERIC_BUFFERS == TRUE) || defined(__DOXIGEN__) /** * @brief Creates a generic dynamic buffer object. * @post A reference to the dynamic buffer object is returned and the * reference counter is initialized to one. * @post The dynamic buffer object is filled with zeros. * * @param[in] name name to be assigned to the new dynamic buffer object * @param[in] size payload size of the dynamic buffer object to be created * * @return The reference to the created dynamic buffer object. * @retval NULL if the dynamic buffer object cannot be allocated or * a dynamic buffer object with the same name exists. * * @api */ dyn_buffer_t *chFactoryCreateBuffer(const char *name, size_t size) { dyn_buffer_t *dbp; F_LOCK(); dbp = (dyn_buffer_t *)dyn_create_object_heap(name, &ch_factory.buf_list, size); if (dbp != NULL) { /* Initializing buffer object data.*/ memset((void *)(dbp + 1), 0, size); } F_UNLOCK(); return dbp; } /** * @brief Retrieves a dynamic buffer object. * @post A reference to the dynamic buffer object is returned with the * reference counter increased by one. * * @param[in] name name of the dynamic buffer object * * @return The reference to the found dynamic buffer object. * @retval NULL if a dynamic buffer object with the specified name * does not exist. * * @api */ dyn_buffer_t *chFactoryFindBuffer(const char *name) { dyn_buffer_t *dbp; F_LOCK(); dbp = (dyn_buffer_t *)dyn_find_object(name, &ch_factory.buf_list); F_UNLOCK(); return dbp; } /** * @brief Releases a dynamic buffer object. * @details The reference counter of the dynamic buffer object is decreased * by one, if reaches zero then the dynamic buffer object memory * is freed. * * @param[in] dbp dynamic buffer object reference * * @api */ void chFactoryReleaseBuffer(dyn_buffer_t *dbp) { F_LOCK(); dyn_release_object_heap(&dbp->element, &ch_factory.buf_list); F_UNLOCK(); } #endif /* CH_CFG_FACTORY_GENERIC_BUFFERS = TRUE */ #if (CH_CFG_FACTORY_SEMAPHORES == TRUE) || defined(__DOXIGEN__) /** * @brief Creates a dynamic semaphore object. * @post A reference to the dynamic semaphore object is returned and the * reference counter is initialized to one. * @post The dynamic semaphore object is initialized and ready to use. * * @param[in] name name to be assigned to the new dynamic semaphore object * @param[in] n dynamic semaphore object counter initialization value * * @return The reference to the created dynamic semaphore object. * @retval NULL if the dynamic semaphore object cannot be allocated or * a dynamic semaphore with the same name exists. * * @api */ dyn_semaphore_t *chFactoryCreateSemaphore(const char *name, cnt_t n) { dyn_semaphore_t *dsp; F_LOCK(); dsp = (dyn_semaphore_t *)dyn_create_object_pool(name, &ch_factory.sem_list, &ch_factory.sem_pool); if (dsp != NULL) { /* Initializing semaphore object dataa.*/ chSemObjectInit(&dsp->sem, n); } F_UNLOCK(); return dsp; } /** * @brief Retrieves a dynamic semaphore object. * @post A reference to the dynamic semaphore object is returned with the * reference counter increased by one. * * @param[in] name name of the dynamic semaphore object * * @return The reference to the found dynamic semaphore object. * @retval NULL if a dynamic semaphore object with the specified name * does not exist. * * @api */ dyn_semaphore_t *chFactoryFindSemaphore(const char *name) { dyn_semaphore_t *dsp; F_LOCK(); dsp = (dyn_semaphore_t *)dyn_find_object(name, &ch_factory.sem_list); F_UNLOCK(); return dsp; } /** * @brief Releases a dynamic semaphore object. * @details The reference counter of the dynamic semaphore object is decreased * by one, if reaches zero then the dynamic semaphore object memory * is freed. * * @param[in] dsp dynamic semaphore object reference * * @api */ void chFactoryReleaseSemaphore(dyn_semaphore_t *dsp) { F_LOCK(); dyn_release_object_pool(&dsp->element, &ch_factory.sem_list, &ch_factory.sem_pool); F_UNLOCK(); } #endif /* CH_CFG_FACTORY_SEMAPHORES = TRUE */ #if (CH_CFG_FACTORY_MAILBOXES == TRUE) || defined(__DOXIGEN__) /** * @brief Creates a dynamic mailbox object. * @post A reference to the dynamic mailbox object is returned and the * reference counter is initialized to one. * @post The dynamic mailbox object is initialized and ready to use. * * @param[in] name name to be assigned to the new dynamic mailbox object * @param[in] n mailbox buffer size as number of messages * * @return The reference to the created dynamic mailbox object. * @retval NULL if the dynamic mailbox object cannot be allocated or * a dynamic mailbox object with the same name exists. * * @api */ dyn_mailbox_t *chFactoryCreateMailbox(const char *name, size_t n) { dyn_mailbox_t *dmp; F_LOCK(); dmp = (dyn_mailbox_t *)dyn_create_object_heap(name, &ch_factory.mbx_list, sizeof (dyn_mailbox_t) + (n * sizeof (msg_t))); if (dmp != NULL) { /* Initializing mailbox object data.*/ chMBObjectInit(&dmp->mbx, (msg_t *)(dmp + 1), n); } F_UNLOCK(); return dmp; } /** * @brief Retrieves a dynamic mailbox object. * @post A reference to the dynamic mailbox object is returned with the * reference counter increased by one. * * @param[in] name name of the dynamic mailbox object * * @return The reference to the found dynamic mailbox object. * @retval NULL if a dynamic mailbox object with the specified name * does not exist. * * @api */ dyn_mailbox_t *chFactoryFindMailbox(const char *name) { dyn_mailbox_t *dmp; F_LOCK(); dmp = (dyn_mailbox_t *)dyn_find_object(name, &ch_factory.mbx_list); F_UNLOCK(); return dmp; } /** * @brief Releases a dynamic mailbox object. * @details The reference counter of the dynamic mailbox object is decreased * by one, if reaches zero then the dynamic mailbox object memory * is freed. * * @param[in] dmp dynamic mailbox object reference * * @api */ void chFactoryReleaseMailbox(dyn_mailbox_t *dmp) { F_LOCK(); dyn_release_object_heap(&dmp->element, &ch_factory.mbx_list); F_UNLOCK(); } #endif /* CH_CFG_FACTORY_MAILBOXES = TRUE */ #if (CH_CFG_FACTORY_OBJ_FIFOS == TRUE) || defined(__DOXIGEN__) /** * @brief Creates a dynamic "objects FIFO" object. * @post A reference to the dynamic "objects FIFO" object is returned and * the reference counter is initialized to one. * @post The dynamic "objects FIFO" object is initialized and ready to use. * * @param[in] name name to be assigned to the new dynamic "objects FIFO" * object * @param[in] objsize size of objects * @param[in] objn number of objects available * @param[in] objalign required objects alignment * @return The reference to the created dynamic "objects FIFO" * object. * @retval NULL if the dynamic "objects FIFO" object cannot be * allocated or a dynamic "objects FIFO" object with * the same name exists. * * @api */ dyn_objects_fifo_t *chFactoryCreateObjectsFIFO(const char *name, size_t objsize, size_t objn, unsigned objalign) { dyn_objects_fifo_t *dofp; F_LOCK(); dofp = (dyn_objects_fifo_t *)dyn_create_object_heap(name, &ch_factory.fifo_list, sizeof (dyn_objects_fifo_t) + (objn * sizeof (msg_t)) + (objn * objsize)); if (dofp != NULL) { msg_t *msgbuf = (msg_t *)(dofp + 1); /* Initializing mailbox object data.*/ chFifoObjectInitAligned(&dofp->fifo, objsize, objn, objalign, (void *)&msgbuf[objn], msgbuf); } F_UNLOCK(); return dofp; } /** * @brief Retrieves a dynamic "objects FIFO" object. * @post A reference to the dynamic "objects FIFO" object is returned with * the reference counter increased by one. * * @param[in] name name of the dynamic "objects FIFO" object * * @return The reference to the found dynamic "objects FIFO" * object. * @retval NULL if a dynamic "objects FIFO" object with the specified * name does not exist. * * @api */ dyn_objects_fifo_t *chFactoryFindObjectsFIFO(const char *name) { dyn_objects_fifo_t *dofp; F_LOCK(); dofp = (dyn_objects_fifo_t *)dyn_find_object(name, &ch_factory.fifo_list); F_UNLOCK(); return dofp; } /** * @brief Releases a dynamic "objects FIFO" object. * @details The reference counter of the dynamic "objects FIFO" object is * decreased by one, if reaches zero then the dynamic "objects FIFO" * object memory is freed. * * @param[in] dofp dynamic "objects FIFO" object reference * * @api */ void chFactoryReleaseObjectsFIFO(dyn_objects_fifo_t *dofp) { F_LOCK(); dyn_release_object_heap(&dofp->element, &ch_factory.fifo_list); F_UNLOCK(); } #endif /* CH_CFG_FACTORY_OBJ_FIFOS = TRUE */ #if (CH_CFG_FACTORY_PIPES == TRUE) || defined(__DOXIGEN__) /** * @brief Creates a dynamic pipe object. * @post A reference to the dynamic pipe object is returned and * the reference counter is initialized to one. * @post The dynamic pipe object is initialized and ready to use. * * @param[in] name name to be assigned to the new dynamic pipe * object * @param[in] size pipe buffer size * @return The reference to the created dynamic pipe * object. * @retval NULL if the dynamic pipe object cannot be * allocated or a dynamic pipe object with * the same name exists. * * @api */ dyn_pipe_t *chFactoryCreatePipe(const char *name, size_t size) { dyn_pipe_t *dpp; F_LOCK(); dpp = (dyn_pipe_t *)dyn_create_object_heap(name, &ch_factory.pipe_list, sizeof (dyn_pipe_t) + size); if (dpp != NULL) { /* Initializing mailbox object data.*/ chPipeObjectInit(&dpp->pipe, (uint8_t *)(dpp + 1), size); } F_UNLOCK(); return dpp; } /** * @brief Retrieves a dynamic pipe object. * @post A reference to the dynamic pipe object is returned with * the reference counter increased by one. * * @param[in] name name of the pipe object * * @return The reference to the found dynamic pipe * object. * @retval NULL if a dynamic pipe object with the specified * name does not exist. * * @api */ dyn_pipe_t *chFactoryFindPipe(const char *name) { dyn_pipe_t *dpp; F_LOCK(); dpp = (dyn_pipe_t *)dyn_find_object(name, &ch_factory.pipe_list); F_UNLOCK(); return dpp; } /** * @brief Releases a dynamic pipe object. * @details The reference counter of the dynamic pipe object is * decreased by one, if reaches zero then the dynamic pipe * object memory is freed. * * @param[in] dpp dynamic pipe object reference * * @api */ void chFactoryReleasePipe(dyn_pipe_t *dpp) { F_LOCK(); dyn_release_object_heap(&dpp->element, &ch_factory.pipe_list); F_UNLOCK(); } #endif /* CH_CFG_FACTORY_PIPES = TRUE */ #endif /* CH_CFG_USE_FACTORY == TRUE */ /** @} */