rt-thread/components/pthreads/semaphore.c

400 lines
8.0 KiB
C
Raw Normal View History

2013-06-26 23:18:30 +08:00
/*
* 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 <rtthread.h>
2013-08-12 22:21:29 +08:00
#include <string.h>
#include "semaphore.h"
#include "pthread_internal.h"
2013-06-26 23:18:30 +08:00
static sem_t *posix_sem_list = RT_NULL;
static struct rt_semaphore posix_sem_lock;
void posix_sem_system_init()
{
2013-06-26 23:18:30 +08:00
rt_sem_init(&posix_sem_lock, "psem", 1, RT_IPC_FLAG_FIFO);
}
rt_inline void posix_sem_insert(sem_t *psem)
{
2013-06-26 23:18:30 +08:00
psem->next = posix_sem_list;
posix_sem_list = psem;
}
static void posix_sem_delete(sem_t *psem)
{
2013-06-26 23:18:30 +08:00
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)
{
2013-06-26 23:18:30 +08:00
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)
{
2013-06-26 23:18:30 +08:00
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)
{
2013-06-26 23:18:30 +08:00
/* 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)
{
2013-06-26 23:18:30 +08:00
rt_err_t result;
if ((!sem) || !(sem->unamed))
{
rt_set_errno(EINVAL);
2013-06-26 23:18:30 +08:00
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);
2013-06-26 23:18:30 +08:00
return -1;
}
/* destroy an unamed posix semaphore */
2013-06-26 23:18:30 +08:00
posix_sem_delete(sem);
rt_sem_release(&posix_sem_lock);
return 0;
}
RTM_EXPORT(sem_destroy);
int sem_unlink(const char *name)
{
2013-06-26 23:18:30 +08:00
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)
{
2013-06-26 23:18:30 +08:00
psem->unlinked = 1;
if (psem->refcount == 0)
{
/* remove this semaphore */
posix_sem_delete(psem);
}
rt_sem_release(&posix_sem_lock);
2013-06-26 23:18:30 +08:00
return 0;
}
rt_sem_release(&posix_sem_lock);
/* no this entry */
rt_set_errno(ENOENT);
2013-06-26 23:18:30 +08:00
return -1;
}
RTM_EXPORT(sem_unlink);
int sem_getvalue(sem_t *sem, int *sval)
{
2013-06-26 23:18:30 +08:00
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)
{
2013-06-26 23:18:30 +08:00
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);
2013-06-26 23:18:30 +08:00
return 0;
}
RTM_EXPORT(sem_init);
sem_t *sem_open(const char *name, int oflag, ...)
{
2013-06-26 23:18:30 +08:00
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);
2013-06-26 23:18:30 +08:00
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:
2013-06-26 23:18:30 +08:00
/* 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)
{
2013-06-26 23:18:30 +08:00
rt_err_t result;
if (!sem)
{
rt_set_errno(EINVAL);
return -1;
}
2013-06-26 23:18:30 +08:00
result = rt_sem_release(sem->sem);
if (result == RT_EOK)
return 0;
2013-06-26 23:18:30 +08:00
rt_set_errno(EINVAL);
2013-06-26 23:18:30 +08:00
return -1;
}
RTM_EXPORT(sem_post);
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout)
{
2013-06-26 23:18:30 +08:00
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)
{
2013-06-26 23:18:30 +08:00
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)
{
2013-06-26 23:18:30 +08:00
rt_err_t result;
2013-06-26 23:18:30 +08:00
if (!sem)
{
rt_set_errno(EINVAL);
2013-06-26 23:18:30 +08:00
return -1;
}
2013-06-26 23:18:30 +08:00
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);