/* * File : semaphore.c * This file is part of RT-Thread RTOS * COPYRIGHT (C) 2006 - 2010, RT-Thread Development Team * * This program 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 2 of the License, or * (at your option) any later version. * * This program 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, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * Change Logs: * Date Author Notes * 2010-10-26 Bernard the first version */ #include #include #include "semaphore.h" #include "pthread_internal.h" static sem_t *posix_sem_list = RT_NULL; static struct rt_semaphore posix_sem_lock; void posix_sem_system_init() { rt_sem_init(&posix_sem_lock, "psem", 1, RT_IPC_FLAG_FIFO); } rt_inline void posix_sem_insert(sem_t *psem) { psem->next = posix_sem_list; posix_sem_list = psem; } static void posix_sem_delete(sem_t *psem) { sem_t *iter; if (posix_sem_list == psem) { posix_sem_list = psem->next; rt_sem_delete(psem->sem); rt_free(psem); return; } for (iter = posix_sem_list; iter->next != RT_NULL; iter = iter->next) { if (iter->next == psem) { /* delete this mq */ if (psem->next != RT_NULL) iter->next = psem->next; else iter->next = RT_NULL; /* delete RT-Thread mqueue */ rt_sem_delete(psem->sem); rt_free(psem); return ; } } } static sem_t *posix_sem_find(const char* name) { sem_t *iter; rt_object_t object; for (iter = posix_sem_list; iter != RT_NULL; iter = iter->next) { object = (rt_object_t)&(iter->sem); if (strncmp(object->name, name, RT_NAME_MAX) == 0) { return iter; } } return RT_NULL; } int sem_close(sem_t *sem) { if (sem == RT_NULL) { rt_set_errno(EINVAL); return -1; } /* lock posix semaphore list */ rt_sem_take(&posix_sem_lock, RT_WAITING_FOREVER); sem->refcount --; if (sem->refcount == 0) { /* delete from posix semaphore list */ if (sem->unlinked) posix_sem_delete(sem); sem = RT_NULL; } rt_sem_release(&posix_sem_lock); return 0; } RTM_EXPORT(sem_close); int sem_destroy(sem_t *sem) { rt_err_t result; if ((!sem) || !(sem->unamed)) { rt_set_errno(EINVAL); return -1; } /* lock posix semaphore list */ rt_sem_take(&posix_sem_lock, RT_WAITING_FOREVER); result = rt_sem_trytake(sem->sem); if (result != RT_EOK) { rt_sem_release(&posix_sem_lock); rt_set_errno(EBUSY); return -1; } /* destroy an unamed posix semaphore */ posix_sem_delete(sem); rt_sem_release(&posix_sem_lock); return 0; } RTM_EXPORT(sem_destroy); int sem_unlink(const char *name) { sem_t *psem; /* lock posix semaphore list */ rt_sem_take(&posix_sem_lock, RT_WAITING_FOREVER); psem = posix_sem_find(name); if (psem != RT_NULL) { psem->unlinked = 1; if (psem->refcount == 0) { /* remove this semaphore */ posix_sem_delete(psem); } rt_sem_release(&posix_sem_lock); return 0; } rt_sem_release(&posix_sem_lock); /* no this entry */ rt_set_errno(ENOENT); return -1; } RTM_EXPORT(sem_unlink); int sem_getvalue(sem_t *sem, int *sval) { if (!sem || !sval) { rt_set_errno(EINVAL); return -1; } *sval = sem->sem->value; return 0; } RTM_EXPORT(sem_getvalue); int sem_init(sem_t *sem, int pshared, unsigned int value) { char name[RT_NAME_MAX]; static rt_uint16_t psem_number = 0; if (sem == RT_NULL) { rt_set_errno(EINVAL); return -1; } rt_snprintf(name, sizeof(name), "psem%02d", psem_number++); sem->sem = rt_sem_create(name, value, RT_IPC_FLAG_FIFO); if (sem == RT_NULL) { rt_set_errno(ENOMEM); return -1; } /* initialize posix semaphore */ sem->refcount = 1; sem->unlinked = 0; sem->unamed = 1; /* lock posix semaphore list */ rt_sem_take(&posix_sem_lock, RT_WAITING_FOREVER); posix_sem_insert(sem); rt_sem_release(&posix_sem_lock); return 0; } RTM_EXPORT(sem_init); sem_t *sem_open(const char *name, int oflag, ...) { sem_t* sem; va_list arg; unsigned int value; sem = RT_NULL; /* lock posix semaphore list */ rt_sem_take(&posix_sem_lock, RT_WAITING_FOREVER); if (oflag & O_CREAT) { va_start(arg, oflag); value = va_arg( arg, unsigned int); va_end(arg); if (oflag & O_EXCL) { if (posix_sem_find(name) != RT_NULL) { rt_set_errno(EEXIST); goto __return; } } sem = (sem_t*) rt_malloc (sizeof(struct posix_sem)); if (sem == RT_NULL) { rt_set_errno(ENFILE); goto __return; } /* create RT-Thread semaphore */ sem->sem = rt_sem_create(name, value, RT_IPC_FLAG_FIFO); if (sem->sem == RT_NULL) /* create failed */ { rt_set_errno(ENFILE); goto __return; } /* initialize reference count */ sem->refcount = 1; sem->unlinked = 0; sem->unamed = 0; /* insert semaphore to posix semaphore list */ posix_sem_insert(sem); } else { /* find semaphore */ sem = posix_sem_find(name); if (sem != RT_NULL) { sem->refcount ++; /* increase reference count */ } else { rt_set_errno(ENOENT); goto __return; } } rt_sem_release(&posix_sem_lock); return sem; __return: /* release lock */ rt_sem_release(&posix_sem_lock); /* release allocated memory */ if (sem != RT_NULL) { /* delete RT-Thread semaphore */ if (sem->sem != RT_NULL) rt_sem_delete(sem->sem); rt_free(sem); } return RT_NULL; } RTM_EXPORT(sem_open); int sem_post(sem_t *sem) { rt_err_t result; if (!sem) { rt_set_errno(EINVAL); return -1; } result = rt_sem_release(sem->sem); if (result == RT_EOK) return 0; rt_set_errno(EINVAL); return -1; } RTM_EXPORT(sem_post); int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout) { rt_err_t result; rt_int32_t tick; if (!sem || !abs_timeout) return EINVAL; /* calculate os tick */ tick = clock_time_to_tick(abs_timeout); result = rt_sem_take(sem->sem, tick); if (result == -RT_ETIMEOUT) { rt_set_errno(ETIMEDOUT); return -1; } if (result == RT_EOK) return 0; rt_set_errno(EINTR); return -1; } RTM_EXPORT(sem_timedwait); int sem_trywait(sem_t *sem) { rt_err_t result; if (!sem) { rt_set_errno(EINVAL); return -1; } result = rt_sem_take(sem->sem, RT_WAITING_FOREVER); if (result == -RT_ETIMEOUT) { rt_set_errno(EAGAIN); return -1; } if (result == RT_EOK) return 0; rt_set_errno(EINTR); return -1; } RTM_EXPORT(sem_trywait); int sem_wait(sem_t *sem) { rt_err_t result; if (!sem) { rt_set_errno(EINVAL); return -1; } result = rt_sem_take(sem->sem, RT_WAITING_FOREVER); if (result == RT_EOK) return 0; rt_set_errno(EINTR); return -1; } RTM_EXPORT(sem_wait);