mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
Merge pull request #3549 from authmillenon/sem/feat/initial
sem: initial import of a lightweight semaphore layer
This commit is contained in:
commit
3d4f373ba5
@ -301,6 +301,15 @@ ifneq (,$(filter posix,$(USEMODULE)))
|
||||
USEMODULE += vtimer
|
||||
endif
|
||||
|
||||
ifneq (,$(filter posix_semaphore,$(USEMODULE)))
|
||||
USEMODULE += sem
|
||||
USEMODULE += vtimer
|
||||
endif
|
||||
|
||||
ifneq (,$(filter sem,$(USEMODULE)))
|
||||
USEMODULE += vtimer
|
||||
endif
|
||||
|
||||
ifneq (,$(filter vtimer,$(USEMODULE)))
|
||||
USEMODULE += xtimer
|
||||
USEMODULE += timex
|
||||
|
@ -1,3 +1,6 @@
|
||||
ifneq (,$(filter posix_semaphore,$(USEMODULE)))
|
||||
DIRS += posix/semaphore
|
||||
endif
|
||||
ifneq (,$(filter posix_sockets,$(USEMODULE)))
|
||||
DIRS += posix/sockets
|
||||
endif
|
||||
@ -68,6 +71,9 @@ endif
|
||||
ifneq (,$(filter netopt,$(USEMODULE)))
|
||||
DIRS += net/crosslayer/netopt
|
||||
endif
|
||||
ifneq (,$(filter sem,$(USEMODULE)))
|
||||
DIRS += sem
|
||||
endif
|
||||
|
||||
DIRS += $(dir $(wildcard $(addsuffix /Makefile, ${USEMODULE})))
|
||||
|
||||
|
@ -18,6 +18,9 @@ ifneq (,$(filter posix,$(USEMODULE)))
|
||||
USEMODULE_INCLUDES += $(RIOTBASE)/sys/posix/include
|
||||
USEMODULE_INCLUDES += $(RIOTBASE)/sys/net/include
|
||||
endif
|
||||
ifneq (,$(filter posix_semaphore,$(USEMODULE)))
|
||||
USEMODULE_INCLUDES += $(RIOTBASE)/sys/posix/include
|
||||
endif
|
||||
ifneq (,$(filter posix_sockets,$(USEMODULE)))
|
||||
USEMODULE_INCLUDES += $(RIOTBASE)/sys/posix/include
|
||||
endif
|
||||
|
145
sys/include/sem.h
Normal file
145
sys/include/sem.h
Normal file
@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Martine Lenders <mlenders@inf.fu-berlin.de>
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup sys_sem Semaphores
|
||||
* @ingroup sys
|
||||
* @brief Lightweight semaphore implementation
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Semaphore definitions
|
||||
*
|
||||
* @author Martine Lenders <mlenders@inf.fu-berlin.de>
|
||||
* @author Christian Mehlis <mehlis@inf.fu-berlin.de>
|
||||
* @author René Kijewski <kijewski@inf.fu-berlin.de>
|
||||
*/
|
||||
#ifndef SEM_H_
|
||||
#define SEM_H_
|
||||
|
||||
#include "priority_queue.h"
|
||||
#include "timex.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief A Semaphore.
|
||||
*/
|
||||
typedef struct {
|
||||
volatile unsigned int value; /**< value of the semaphore */
|
||||
priority_queue_t queue; /**< list of threads waiting for the semaphore */
|
||||
} sem_t;
|
||||
|
||||
/**
|
||||
* @brief Creates semaphore.
|
||||
*
|
||||
* @see <a href="http://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_init.html">
|
||||
* The Open Group Base Specifications Issue 7, sem_init()
|
||||
* </a> (without `pshared` parameter)
|
||||
*
|
||||
* @param[out] sem The created semaphore.
|
||||
* @param[in] value Initial value for the semaphore.
|
||||
*
|
||||
* @return 0 on success.
|
||||
* @return -EINVAL, if semaphore is invalid.
|
||||
*/
|
||||
int sem_create(sem_t *sem, unsigned int value);
|
||||
|
||||
/**
|
||||
* @brief Destroys a semaphore.
|
||||
*
|
||||
* @see <a href="http://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_destroy.html">
|
||||
* The Open Group Base Specifications Issue 7, sem_destroy()
|
||||
* </a>
|
||||
*
|
||||
* @param[in] sem The semaphore to destroy.
|
||||
*
|
||||
* @return 0 on success.
|
||||
* @return -EINVAL, if semaphore is invalid.
|
||||
*/
|
||||
int sem_destroy(sem_t *sem);
|
||||
|
||||
/**
|
||||
* @brief Wait for a semaphore being posted.
|
||||
*
|
||||
* @pre Message queue of active thread is initialized (see @ref msg_init_queue()).
|
||||
*
|
||||
* @param[in] sem A semaphore.
|
||||
* @param[in] timeout Time until the semaphore times out. NULL for no timeout.
|
||||
* @param[out] msg Container for a spurious message during the timed wait (result == -EAGAIN).
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -EINVAL, if semaphore is invalid.
|
||||
* @return -ETIMEDOUT, if the semaphore times out.
|
||||
* @return -ECANCELED, if the semaphore was destroyed.
|
||||
* @return -EAGAIN, if the thread received a message while waiting for the lock.
|
||||
*/
|
||||
int sem_wait_timed_msg(sem_t *sem, timex_t *timeout, msg_t *msg);
|
||||
|
||||
/**
|
||||
* @brief Wait for a semaphore being posted (without timeout).
|
||||
*
|
||||
* @param[in] sem A semaphore.
|
||||
* @param[out] msg Container for a spurious message during the timed wait (result == -EAGAIN).
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -EINVAL, if semaphore is invalid.
|
||||
* @return -ECANCELED, if the semaphore was destroyed.
|
||||
* @return -EAGAIN, if the thread received a message while waiting for the lock.
|
||||
*/
|
||||
static inline int sem_wait_msg(sem_t *sem, msg_t *msg)
|
||||
{
|
||||
return sem_wait_timed_msg(sem, NULL, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Wait for a semaphore being posted (dropping spurious messages).
|
||||
* @details Any spurious messages received while waiting for the semaphore are silently dropped.
|
||||
*
|
||||
* @param[in] sem A semaphore.
|
||||
* @param[in] timeout Time until the semaphore times out. NULL for no timeout.
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -EINVAL, if semaphore is invalid.
|
||||
* @return -ETIMEDOUT, if the semaphore times out.
|
||||
* @return -ECANCELED, if the semaphore was destroyed.
|
||||
*/
|
||||
int sem_wait_timed(sem_t *sem, timex_t *timeout);
|
||||
|
||||
/**
|
||||
* @brief Wait for a semaphore being posted (without timeout, dropping spurious messages).
|
||||
*
|
||||
* @param[in] sem A semaphore.
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -EINVAL, if semaphore is invalid.
|
||||
* @return -ECANCELED, if the semaphore was destroyed.
|
||||
*/
|
||||
static inline int sem_wait(sem_t *sem)
|
||||
{
|
||||
return sem_wait_timed(sem, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Signal semaphore.
|
||||
*
|
||||
* @param[in] sem A semaphore.
|
||||
*
|
||||
* @return -EINVAL, if semaphore is invalid.
|
||||
* @return -EOVERFLOW, if the semaphore's value would overflow.
|
||||
*/
|
||||
int sem_post(sem_t *sem);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SEM_H_ */
|
||||
/** @} */
|
@ -6,117 +6,170 @@
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
#ifndef _SEMAPHORE_H
|
||||
#define _SEMAPHORE_H 1
|
||||
/**
|
||||
* @defgroup posix_semaphore POSIX semaphores
|
||||
* @ingroup posix
|
||||
* @{
|
||||
* @file
|
||||
* @brief Semaphores
|
||||
* @see <a href="http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/semaphore.h.html">
|
||||
* The Open Group Base Specifications Issue 7, <semaphore.h>
|
||||
* </a>
|
||||
* @author Christian Mehlis <mehlis@inf.fu-berlin.de>
|
||||
* @author Martine Lenders <mlenders@inf.fu-berlin.de>
|
||||
* @author René Kijewski <kijewski@inf.fu-berlin.de>
|
||||
*/
|
||||
|
||||
#ifndef POSIX_SEMAPHORE_H_
|
||||
#define POSIX_SEMAPHORE_H_
|
||||
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "priority_queue.h"
|
||||
#include "sem.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Value returned if `sem_open' failed. */
|
||||
/**
|
||||
* @brief Value returned if `sem_open' failed.
|
||||
*/
|
||||
#define SEM_FAILED ((sem_t *) 0)
|
||||
|
||||
/**
|
||||
* @brief Semaphore struct
|
||||
* @brief Initialize semaphore.
|
||||
*
|
||||
* @see <a href="http://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_init.html">
|
||||
* The Open Group Base Specifications Issue 7, sem_init()
|
||||
* </a>
|
||||
*
|
||||
* The sem_init() function shall initialize the unnamed semaphore referred to by @p sem. The value
|
||||
* of the initialized semaphore shall be @p value. Following a successful call to sem_init(),
|
||||
* the semaphore may be used in subsequent calls to sem_wait(), sem_timedwait(),
|
||||
* sem_trywait(), sem_post(), and sem_destroy(). This semaphore shall remain usable until the
|
||||
* semaphore is destroyed.
|
||||
*
|
||||
* @param[out] sem Semaphore to initialize.
|
||||
* @param[in] pshared **(unused, since RIOT only has threads)**
|
||||
* Semaphore is shared between processes not threads.
|
||||
* @param[in] value Value to set.
|
||||
*
|
||||
* @return 0 on success.
|
||||
* @return -EINVAL, if semaphore is invalid.
|
||||
*/
|
||||
typedef struct sem {
|
||||
/** the value of the semaphore */
|
||||
volatile unsigned int value;
|
||||
/** list of threads waiting for the semaphore */
|
||||
priority_queue_t queue;
|
||||
} sem_t;
|
||||
#define sem_init(sem, pshared, value) sem_create(sem, value)
|
||||
|
||||
/**
|
||||
* @brief Initialize semaphore object SEM to VALUE.
|
||||
* @brief Open a named semaphore @p name with open flags @p oflag.
|
||||
*
|
||||
* @param sem Semaphore to initialize
|
||||
* @param pshared unused
|
||||
* @param value Value to set
|
||||
* @see <a href="http://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_open.html">
|
||||
* The Open Group Base Specifications Issue 7, sem_open()
|
||||
* </a>
|
||||
*
|
||||
* @todo named semaphore are currently not supported
|
||||
*
|
||||
* @param[in] name Name to set.
|
||||
* @param[in] oflag Flags to set.
|
||||
*
|
||||
* @return Always @ref SEM_FAILED, since it is not implemented currently.
|
||||
*/
|
||||
int sem_init(sem_t *sem, int pshared, unsigned int value);
|
||||
#define sem_open(name, oflag, ...) (SEM_FAILED)
|
||||
|
||||
/**
|
||||
* @brief Free resources associated with semaphore object SEM.
|
||||
* @brief Close descriptor for named semaphore @p sem.
|
||||
*
|
||||
* @param sem Semaphore to destroy
|
||||
* @see <a href="http://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_close.html">
|
||||
* The Open Group Base Specifications Issue 7, sem_close()
|
||||
* </a>
|
||||
*
|
||||
* @todo named semaphore are currently not supported
|
||||
*
|
||||
* @param[in] sem Semaphore to close.
|
||||
*
|
||||
* @return Always -1, since it is not implemented currently.
|
||||
*/
|
||||
int sem_destroy(sem_t *sem);
|
||||
|
||||
/*
|
||||
* @brief Open a named semaphore NAME with open flags OFLAG.
|
||||
*
|
||||
* @brief WARNING: named semaphore are currently not supported
|
||||
*
|
||||
* @param name Name to set
|
||||
* @param oflag Flags to set
|
||||
*/
|
||||
sem_t *sem_open(const char *name, int oflag, ...);
|
||||
#define sem_close(sem) (-1)
|
||||
|
||||
/**
|
||||
* @brief Close descriptor for named semaphore SEM.
|
||||
* @brief Remove named semaphore @p name.
|
||||
*
|
||||
* @brief WARNING: named semaphore are currently not supported
|
||||
* @see <a href="http://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_unlink.html">
|
||||
* The Open Group Base Specifications Issue 7, sem_unlink()
|
||||
* </a>
|
||||
*
|
||||
* @param sem Semaphore to close
|
||||
* @todo named semaphore are currently not supported
|
||||
*
|
||||
* @param[in] name Name to unlink.
|
||||
*
|
||||
* @return Always -1, since it is not implemented currently.
|
||||
*/
|
||||
int sem_close(sem_t *sem);
|
||||
#define sem_unlink(name) (-1)
|
||||
|
||||
/**
|
||||
* @brief Remove named semaphore NAME.
|
||||
* @brief Similar to `sem_wait' but wait only until @p abstime.
|
||||
*
|
||||
* @brief WARNING: named semaphore are currently not supported
|
||||
* @see <a href="http://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_timedwait.html">
|
||||
* The Open Group Base Specifications Issue 7, sem_timedwait()
|
||||
* </a>
|
||||
*
|
||||
* @param name Name to unlink
|
||||
*/
|
||||
int sem_unlink(const char *name);
|
||||
|
||||
/**
|
||||
* @brief Wait for SEM being posted.
|
||||
* The sem_timedwait() function shall lock the semaphore referenced by @p sem as in the sem_wait()
|
||||
* function. However, if the semaphore cannot be locked without waiting for another process or
|
||||
* thread to unlock the semaphore by performing a sem_post() function, this wait shall be
|
||||
* terminated when the specified timeout expires.
|
||||
*
|
||||
* @param sem Semaphore to wait
|
||||
*/
|
||||
int sem_wait(sem_t *sem);
|
||||
|
||||
/**
|
||||
* @brief Similar to `sem_wait' but wait only until ABSTIME.
|
||||
*
|
||||
* @brief WARNING: currently not supported
|
||||
*
|
||||
* @param sem Semaphore to wait on
|
||||
* @param abstime Max time to wait for a post
|
||||
* @param[in] sem Semaphore to wait on.
|
||||
* @param[in] abstime Absolute time (that is when the clock on which temouts are based equals
|
||||
* this value) the timeout for the wait shall expire. If the value specified
|
||||
* has already passed the timeout expires immediately.
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -EINVAL, if semaphore is invalid.
|
||||
* @return -ETIMEDOUT, if the semaphore times out.
|
||||
* @return -ECANCELED, if the semaphore was destroyed.
|
||||
*/
|
||||
int sem_timedwait(sem_t *sem, const struct timespec *abstime);
|
||||
|
||||
/**
|
||||
* @brief Test whether SEM is posted.
|
||||
*
|
||||
* @param sem Semaphore to trywait on
|
||||
* @see <a href="http://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_trywait.html">
|
||||
* The Open Group Base Specifications Issue 7, sem_trywait()
|
||||
* </a>
|
||||
*
|
||||
* @param[in] sem Semaphore to try to wait on
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -EINVAL, if semaphore is invalid.
|
||||
* @return -EAGAIN, if the semaphore was already locked.
|
||||
*/
|
||||
int sem_trywait(sem_t *sem);
|
||||
|
||||
/**
|
||||
* @brief Post SEM.
|
||||
*
|
||||
* @param sem Semaphore to post on
|
||||
*/
|
||||
int sem_post(sem_t *sem);
|
||||
|
||||
/**
|
||||
* @brief Get current value of SEM and store it in *SVAL.
|
||||
*
|
||||
* @param sem Semaphore to get value from
|
||||
* @param sval place whre value goes to
|
||||
* @see <a href="http://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_getvalue.html">
|
||||
* The Open Group Base Specifications Issue 7, sem_getvalue()
|
||||
* </a>
|
||||
*
|
||||
* @param[in] sem Semaphore to get the value from.
|
||||
* @param[out] sval Place where value goes to.
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -EINVAL, if semaphore is invalid.
|
||||
*/
|
||||
int sem_getvalue(sem_t *sem, int *sval);
|
||||
static inline int sem_getvalue(sem_t *sem, int *sval)
|
||||
{
|
||||
if (sem != NULL) {
|
||||
*sval = (int)sem->value;
|
||||
return 0;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* semaphore.h */
|
||||
#endif /* POSIX_SEMAPHORE_H_ */
|
||||
/** @} */
|
||||
|
@ -1,159 +0,0 @@
|
||||
/**
|
||||
* Semaphore implemenation
|
||||
*
|
||||
* Copyright (C) 2013 Freie Universität Berlin
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*
|
||||
* @ingroup posix
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Implementation of semaphores with priority queues
|
||||
*
|
||||
* @author Christian Mehlis <mehlis@inf.fu-berlin.de>
|
||||
* @author René Kijewski <kijewski@inf.fu-berlin.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "irq.h"
|
||||
#include "sched.h"
|
||||
#include "tcb.h"
|
||||
#include "thread.h"
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
#include "semaphore.h"
|
||||
|
||||
int sem_init(sem_t *sem, int pshared, unsigned int value)
|
||||
{
|
||||
(void) pshared; /* nothing to do */
|
||||
|
||||
sem->value = value;
|
||||
|
||||
/* waiters for the mutex */
|
||||
sem->queue.first = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sem_destroy(sem_t *sem)
|
||||
{
|
||||
if (sem->queue.first) {
|
||||
DEBUG("sem_destroy: %" PRIkernel_pid ": tried to destroy active semaphore.\n",
|
||||
sched_active_thread->pid);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
sem_t *sem_open(const char *name, int oflag, ...)
|
||||
{
|
||||
(void) name; /* named semaphore currently not supported */
|
||||
(void) oflag;
|
||||
return SEM_FAILED;
|
||||
}
|
||||
|
||||
int sem_close(sem_t *sem)
|
||||
{
|
||||
(void) sem; /* named semaphore currently not supported */
|
||||
return -1;
|
||||
}
|
||||
|
||||
int sem_unlink(const char *name)
|
||||
{
|
||||
(void) name; /* named semaphore currently not supported */
|
||||
return -1;
|
||||
}
|
||||
|
||||
int sem_wait(sem_t *sem)
|
||||
{
|
||||
while (1) {
|
||||
unsigned old_state = disableIRQ();
|
||||
|
||||
unsigned value = sem->value;
|
||||
if (value != 0) {
|
||||
sem->value = value - 1;
|
||||
restoreIRQ(old_state);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* I'm going blocked */
|
||||
priority_queue_node_t n;
|
||||
n.priority = (uint32_t) sched_active_thread->priority;
|
||||
n.data = (uintptr_t) sched_active_thread;
|
||||
n.next = NULL;
|
||||
priority_queue_add(&sem->queue, &n);
|
||||
|
||||
DEBUG("sem_wait: %" PRIkernel_pid ": Adding node to mutex queue: prio: %" PRIu32 "\n",
|
||||
sched_active_thread->pid, sched_active_thread->priority);
|
||||
|
||||
/* scheduler should schedule an other thread, that unlocks the
|
||||
* mutex in the future, when this happens I get scheduled again
|
||||
*/
|
||||
sched_set_status((tcb_t*) sched_active_thread, STATUS_MUTEX_BLOCKED);
|
||||
restoreIRQ(old_state);
|
||||
thread_yield_higher();
|
||||
}
|
||||
}
|
||||
|
||||
int sem_timedwait(sem_t *sem, const struct timespec *abstime)
|
||||
{
|
||||
(void) sem; /* timedwait currently not supported */
|
||||
(void) abstime;
|
||||
return -1; /* signal problem */
|
||||
}
|
||||
|
||||
int sem_trywait(sem_t *sem)
|
||||
{
|
||||
unsigned old_state = disableIRQ();
|
||||
int result;
|
||||
|
||||
unsigned value = sem->value;
|
||||
if (value == 0) {
|
||||
result = -1;
|
||||
}
|
||||
else {
|
||||
result = 0;
|
||||
sem->value = value - 1;
|
||||
}
|
||||
|
||||
restoreIRQ(old_state);
|
||||
return result;
|
||||
}
|
||||
|
||||
int sem_post(sem_t *sem)
|
||||
{
|
||||
unsigned old_state = disableIRQ();
|
||||
++sem->value;
|
||||
|
||||
priority_queue_node_t *next = priority_queue_remove_head(&sem->queue);
|
||||
if (next) {
|
||||
tcb_t *next_process = (tcb_t*) next->data;
|
||||
DEBUG("sem_post: %" PRIkernel_pid ": waking up %" PRIkernel_pid "\n",
|
||||
sched_active_thread->pid, next_process->pid);
|
||||
sched_set_status(next_process, STATUS_PENDING);
|
||||
|
||||
uint16_t prio = next_process->priority;
|
||||
restoreIRQ(old_state);
|
||||
sched_switch(prio);
|
||||
}
|
||||
else {
|
||||
restoreIRQ(old_state);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int sem_getvalue(sem_t *sem, int *sval)
|
||||
{
|
||||
*sval = sem->value;
|
||||
return 0;
|
||||
}
|
3
sys/posix/semaphore/Makefile
Normal file
3
sys/posix/semaphore/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
MODULE = posix_semaphore
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
77
sys/posix/semaphore/posix_semaphore.c
Normal file
77
sys/posix/semaphore/posix_semaphore.c
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Freie Universität Berlin
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
*
|
||||
* @author Christian Mehlis <mehlis@inf.fu-berlin.de>
|
||||
* @author Martine Lenders <mlenders@inf.fu-berlin.de>
|
||||
* @author René Kijewski <kijewski@inf.fu-berlin.de>
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "irq.h"
|
||||
#include "sched.h"
|
||||
#include "sem.h"
|
||||
#include "tcb.h"
|
||||
#include "timex.h"
|
||||
#include "thread.h"
|
||||
#include "vtimer.h"
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
#include "semaphore.h"
|
||||
|
||||
#define USEC_IN_NS (1000)
|
||||
|
||||
int sem_timedwait(sem_t *sem, const struct timespec *abstime)
|
||||
{
|
||||
timex_t now, timeout = { abstime->tv_sec, abstime->tv_nsec / USEC_IN_NS };
|
||||
int res;
|
||||
vtimer_now(&now);
|
||||
if (timex_cmp(now, timeout) > 0) {
|
||||
errno = ETIMEDOUT;
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
timeout = timex_sub(timeout, now);
|
||||
res = sem_wait_timed(sem, &timeout);
|
||||
if (res < 0) {
|
||||
errno = -res;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int sem_trywait(sem_t *sem)
|
||||
{
|
||||
unsigned int old_state, value;
|
||||
int result;
|
||||
if (sem == NULL) {
|
||||
errno = EINVAL;
|
||||
return -EINVAL;
|
||||
}
|
||||
old_state = disableIRQ();
|
||||
value = sem->value;
|
||||
if (value == 0) {
|
||||
errno = EAGAIN;
|
||||
result = -EAGAIN;
|
||||
}
|
||||
else {
|
||||
result = 0;
|
||||
sem->value = value - 1;
|
||||
}
|
||||
|
||||
restoreIRQ(old_state);
|
||||
return result;
|
||||
}
|
||||
|
||||
/** @} */
|
1
sys/sem/Makefile
Normal file
1
sys/sem/Makefile
Normal file
@ -0,0 +1 @@
|
||||
include $(RIOTBASE)/Makefile.base
|
173
sys/sem/sem.c
Normal file
173
sys/sem/sem.c
Normal file
@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Copyright (C) 2013-15 Freie Universität Berlin
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
*
|
||||
* @author Christian Mehlis <mehlis@inf.fu-berlin.de>
|
||||
* @author Martine Lenders <mlenders@inf.fu-berlin.de>
|
||||
* @author René Kijewski <kijewski@inf.fu-berlin.de>
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "irq.h"
|
||||
#include "msg.h"
|
||||
#include "vtimer.h"
|
||||
|
||||
#include "sem.h"
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
#define MSG_SIGNAL (0x0501)
|
||||
#define MSG_TIMEOUT (0x0502)
|
||||
#define MSG_DESTROYED (0x0503)
|
||||
|
||||
int sem_create(sem_t *sem, unsigned int value)
|
||||
{
|
||||
if (sem == NULL) {
|
||||
#ifdef MODULE_POSIX_SEMAPHORE
|
||||
errno = EINVAL;
|
||||
#endif
|
||||
return -EINVAL;
|
||||
}
|
||||
sem->value = value;
|
||||
/* waiters for the mutex */
|
||||
sem->queue.first = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sem_destroy(sem_t *sem)
|
||||
{
|
||||
unsigned int old_state;
|
||||
priority_queue_node_t *next;
|
||||
if (sem == NULL) {
|
||||
#ifdef MODULE_POSIX_SEMAPHORE
|
||||
errno = EINVAL;
|
||||
#endif
|
||||
return -EINVAL;
|
||||
}
|
||||
old_state = disableIRQ();
|
||||
while ((next = priority_queue_remove_head(&sem->queue)) != NULL) {
|
||||
msg_t msg;
|
||||
kernel_pid_t pid = (kernel_pid_t)next->data;
|
||||
msg.type = MSG_DESTROYED;
|
||||
msg.content.ptr = (void *) sem;
|
||||
msg_send_int(&msg, pid);
|
||||
}
|
||||
restoreIRQ(old_state);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sem_wait_timed_msg(sem_t *sem, timex_t *timeout, msg_t *msg)
|
||||
{
|
||||
if (sem == NULL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
while (1) {
|
||||
unsigned old_state = disableIRQ();
|
||||
priority_queue_node_t n;
|
||||
vtimer_t timeout_timer;
|
||||
|
||||
unsigned value = sem->value;
|
||||
if (value != 0) {
|
||||
sem->value = value - 1;
|
||||
restoreIRQ(old_state);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* I'm going blocked */
|
||||
n.priority = (uint32_t)sched_active_thread->priority;
|
||||
n.data = (unsigned int)sched_active_pid;
|
||||
n.next = NULL;
|
||||
priority_queue_add(&sem->queue, &n);
|
||||
|
||||
DEBUG("sem_wait: %" PRIkernel_pid ": Adding node to semaphore queue: prio: %" PRIu32 "\n",
|
||||
sched_active_thread->pid, sched_active_thread->priority);
|
||||
|
||||
if (timeout != NULL) {
|
||||
vtimer_set_msg(&timeout_timer, *timeout, sched_active_pid,
|
||||
MSG_TIMEOUT, sem);
|
||||
}
|
||||
|
||||
restoreIRQ(old_state);
|
||||
msg_receive(msg);
|
||||
|
||||
if (timeout != NULL) {
|
||||
vtimer_remove(&timeout_timer); /* remove timer just to be sure */
|
||||
}
|
||||
|
||||
if (msg->content.ptr != (void *) sem) {
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
switch (msg->type) {
|
||||
case MSG_SIGNAL:
|
||||
continue;
|
||||
case MSG_TIMEOUT:
|
||||
return -ETIMEDOUT;
|
||||
case MSG_DESTROYED:
|
||||
return -ECANCELED;
|
||||
default:
|
||||
return -EAGAIN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int sem_wait_timed(sem_t *sem, timex_t *timeout)
|
||||
{
|
||||
int result;
|
||||
do {
|
||||
msg_t msg;
|
||||
result = sem_wait_timed_msg(sem, timeout, &msg);
|
||||
DEBUG("sem_wait: %" PRIkernel_pid ": Discarding message from %" PRIkernel_pid "\n",
|
||||
sched_active_thread->pid, msg->sender_pid);
|
||||
} while (result == -EAGAIN);
|
||||
return result;
|
||||
}
|
||||
|
||||
int sem_post(sem_t *sem)
|
||||
{
|
||||
unsigned int old_state, value;
|
||||
priority_queue_node_t *next;
|
||||
if (sem == NULL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
old_state = disableIRQ();
|
||||
value = sem->value;
|
||||
if (value == UINT_MAX) {
|
||||
restoreIRQ(old_state);
|
||||
return -EOVERFLOW;
|
||||
}
|
||||
++sem->value;
|
||||
next = priority_queue_remove_head(&sem->queue);
|
||||
if (next) {
|
||||
uint16_t prio = (uint16_t)next->priority;
|
||||
kernel_pid_t pid = (kernel_pid_t) next->data;
|
||||
msg_t msg;
|
||||
DEBUG("sem_post: %" PRIkernel_pid ": waking up %" PRIkernel_pid "\n",
|
||||
sched_active_thread->pid, next_process->pid);
|
||||
msg.type = MSG_SIGNAL;
|
||||
msg.content.ptr = (void *) sem;
|
||||
msg_send_int(&msg, pid);
|
||||
restoreIRQ(old_state);
|
||||
sched_switch(prio);
|
||||
}
|
||||
else {
|
||||
restoreIRQ(old_state);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** @} */
|
@ -2,10 +2,14 @@ APPLICATION = posix_semaphore
|
||||
include ../Makefile.tests_common
|
||||
|
||||
BOARD_INSUFFICIENT_MEMORY := msb-430 msb-430h mbed_lpc1768 chronos stm32f0discovery \
|
||||
pca10000 pca10005 weio yunjia-nrf51822 nrf6310 spark-core
|
||||
pca10000 pca10005 weio yunjia-nrf51822 nrf6310 spark-core \
|
||||
nucleo-f334
|
||||
|
||||
USEMODULE += posix
|
||||
USEMODULE += posix_semaphore
|
||||
|
||||
DISABLE_MODULE += auto_init
|
||||
|
||||
include $(RIOTBASE)/Makefile.include
|
||||
|
||||
test:
|
||||
python tests/01-run.py
|
||||
|
@ -1,6 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Christian Mehlis <mehlis@inf.fu-berlin.de>
|
||||
* Copyright (C) 2013 René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
* Copyright (C) 2015 Martine Lenders <mlenders@inf.fu-berlin.de>
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
@ -16,27 +17,36 @@
|
||||
*
|
||||
* @author Christian Mehlis <mehlis@inf.fu-berlin.de>
|
||||
* @author René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
* @author Martine Lenders <mlenders@inf.fu-berlin.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "msg.h"
|
||||
#include "timex.h"
|
||||
#include "thread.h"
|
||||
#include "semaphore.h"
|
||||
#include "vtimer.h"
|
||||
|
||||
#define SEMAPHORE_TEST_THREADS 5
|
||||
char test1_thread_stack[THREAD_STACKSIZE_MAIN];
|
||||
char test2_thread_stack[SEMAPHORE_TEST_THREADS][THREAD_STACKSIZE_MAIN];
|
||||
#define SEMAPHORE_MSG_QUEUE_SIZE (8)
|
||||
#define SEMAPHORE_TEST_THREADS (5)
|
||||
static char test1_thread_stack[THREAD_STACKSIZE_MAIN];
|
||||
static char test2_thread_stack[SEMAPHORE_TEST_THREADS][THREAD_STACKSIZE_MAIN];
|
||||
static msg_t main_msg_queue[SEMAPHORE_MSG_QUEUE_SIZE];
|
||||
static msg_t test1_msg_queue[SEMAPHORE_MSG_QUEUE_SIZE];
|
||||
|
||||
sem_t s;
|
||||
static sem_t s1;
|
||||
static sem_t s2;
|
||||
|
||||
static void *test1_second_thread(void *arg)
|
||||
{
|
||||
(void) arg;
|
||||
msg_init_queue(test1_msg_queue, SEMAPHORE_MSG_QUEUE_SIZE);
|
||||
puts("second: sem_trywait");
|
||||
|
||||
if (sem_trywait(&s) == 0) {
|
||||
if (sem_trywait(&s1) == 0) {
|
||||
puts("second: sem_trywait failed");
|
||||
}
|
||||
|
||||
@ -44,7 +54,7 @@ static void *test1_second_thread(void *arg)
|
||||
|
||||
puts("second: wait for post");
|
||||
|
||||
if (sem_wait(&s) != 1) {
|
||||
if (sem_wait(&s1) != 1) {
|
||||
puts("second: sem_wait failed");
|
||||
}
|
||||
|
||||
@ -58,7 +68,7 @@ static void test1(void)
|
||||
{
|
||||
puts("first: sem_init");
|
||||
|
||||
if (sem_init(&s, 0, 0) != 0) {
|
||||
if (sem_init(&s1, 0, 0) != 0) {
|
||||
puts("first: sem_init failed");
|
||||
}
|
||||
|
||||
@ -78,7 +88,7 @@ static void test1(void)
|
||||
puts("first: sem_getvalue");
|
||||
int val;
|
||||
|
||||
if (sem_getvalue(&s, &val) != 0 || val != 0) {
|
||||
if (sem_getvalue(&s1, &val) != 0 || val != 0) {
|
||||
puts("first: sem_getvalue FAILED");
|
||||
}
|
||||
|
||||
@ -92,7 +102,7 @@ static void test1(void)
|
||||
|
||||
puts("first: sem_trywait");
|
||||
|
||||
if (sem_trywait(&s) != -1) {
|
||||
if (sem_trywait(&s1) != -1) {
|
||||
puts("first: sem_trywait FAILED");
|
||||
}
|
||||
|
||||
@ -100,7 +110,7 @@ static void test1(void)
|
||||
|
||||
puts("first: sem_post");
|
||||
|
||||
if (sem_post(&s) != 1) {
|
||||
if (sem_post(&s1) != 1) {
|
||||
puts("first: sem_post FAILED");
|
||||
}
|
||||
|
||||
@ -110,7 +120,7 @@ static void test1(void)
|
||||
|
||||
puts("first: sem_destroy");
|
||||
|
||||
if (sem_destroy(&s) != 0) {
|
||||
if (sem_destroy(&s1) != 0) {
|
||||
puts("first: sem_destroy FAILED");
|
||||
}
|
||||
|
||||
@ -119,7 +129,9 @@ static void test1(void)
|
||||
|
||||
static void *priority_sema_thread(void *name)
|
||||
{
|
||||
sem_wait(&s);
|
||||
msg_t msg_queue[SEMAPHORE_MSG_QUEUE_SIZE];
|
||||
msg_init_queue(msg_queue, SEMAPHORE_MSG_QUEUE_SIZE);
|
||||
sem_wait(&s1);
|
||||
printf("Thread '%s' woke up.\n", (const char *) name);
|
||||
return NULL;
|
||||
}
|
||||
@ -129,7 +141,7 @@ void test2(void)
|
||||
{
|
||||
puts("first: sem_init");
|
||||
|
||||
if (sem_init(&s, 0, 0) != 0) {
|
||||
if (sem_init(&s1, 0, 0) != 0) {
|
||||
puts("first: sem_init FAILED");
|
||||
}
|
||||
|
||||
@ -157,17 +169,105 @@ void test2(void)
|
||||
|
||||
for (int i = 0; i < SEMAPHORE_TEST_THREADS; i++) {
|
||||
printf("post no. %d\n", i);
|
||||
sem_post(&s);
|
||||
sem_post(&s1);
|
||||
puts("Back in main thread.");
|
||||
}
|
||||
}
|
||||
|
||||
static void *test3_one_two_thread(void *arg)
|
||||
{
|
||||
msg_t msg_queue[SEMAPHORE_MSG_QUEUE_SIZE];
|
||||
(void)arg;
|
||||
msg_init_queue(msg_queue, SEMAPHORE_MSG_QUEUE_SIZE);
|
||||
sem_wait(&s1);
|
||||
puts("Thread 1 woke up after waiting for s1.");
|
||||
sem_wait(&s2);
|
||||
puts("Thread 1 woke up after waiting for s2.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *test3_two_one_thread(void *arg)
|
||||
{
|
||||
msg_t msg_queue[SEMAPHORE_MSG_QUEUE_SIZE];
|
||||
(void)arg;
|
||||
msg_init_queue(msg_queue, SEMAPHORE_MSG_QUEUE_SIZE);
|
||||
sem_wait(&s2);
|
||||
puts("Thread 2 woke up after waiting for s2.");
|
||||
sem_wait(&s1);
|
||||
puts("Thread 2 woke up after waiting for s1.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void test3(void)
|
||||
{
|
||||
puts("first: sem_init s1");
|
||||
if (sem_init(&s1, 0, 0) != 0) {
|
||||
puts("first: sem_init FAILED");
|
||||
}
|
||||
puts("first: sem_init s2");
|
||||
if (sem_init(&s2, 0, 0) != 0) {
|
||||
puts("first: sem_init FAILED");
|
||||
}
|
||||
puts("first: create thread 1");
|
||||
if (thread_create(test2_thread_stack[0], sizeof(test2_thread_stack[0]),
|
||||
THREAD_PRIORITY_MAIN - 1, CREATE_STACKTEST,
|
||||
test3_one_two_thread, NULL, "thread 1") < 0) {
|
||||
puts("first: thread create FAILED");
|
||||
return;
|
||||
}
|
||||
puts("first: create thread 2");
|
||||
if (thread_create(test2_thread_stack[1], sizeof(test2_thread_stack[1]),
|
||||
THREAD_PRIORITY_MAIN - 1, CREATE_STACKTEST,
|
||||
test3_two_one_thread, NULL, "thread 2") < 0) {
|
||||
puts("first: thread create FAILED");
|
||||
return;
|
||||
}
|
||||
puts("------------------------------------------");
|
||||
puts("post s1");
|
||||
sem_post(&s1);
|
||||
puts("post s2");
|
||||
sem_post(&s2);
|
||||
puts("post s2");
|
||||
sem_post(&s2);
|
||||
puts("post s1");
|
||||
sem_post(&s1);
|
||||
}
|
||||
|
||||
void test4(void)
|
||||
{
|
||||
struct timespec abs;
|
||||
char timestamp[TIMEX_MAX_STR_LEN];
|
||||
timex_t now, start, stop, exp = { 1, 0 };
|
||||
vtimer_now(&now);
|
||||
abs.tv_sec = now.seconds + 1;
|
||||
abs.tv_nsec = now.microseconds * 1000;
|
||||
puts("first: sem_init s1");
|
||||
if (sem_init(&s1, 0, 0) != 0) {
|
||||
puts("first: sem_init FAILED");
|
||||
}
|
||||
vtimer_now(&start);
|
||||
puts("first: wait 1 sec for s1");
|
||||
sem_timedwait(&s1, &abs);
|
||||
vtimer_now(&stop);
|
||||
stop = timex_sub(stop, start);
|
||||
if (timex_cmp(stop, exp) < 0) {
|
||||
printf("first: waited only %s => FAILED\n",
|
||||
timex_to_str(stop, timestamp));
|
||||
}
|
||||
printf("first: waited %s\n", timex_to_str(stop, timestamp));
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
msg_init_queue(main_msg_queue, SEMAPHORE_MSG_QUEUE_SIZE);
|
||||
puts("######################### TEST1:");
|
||||
test1();
|
||||
puts("######################### TEST2:");
|
||||
test2();
|
||||
puts("######################### TEST3:");
|
||||
test3();
|
||||
puts("######################### TEST4:");
|
||||
test4();
|
||||
puts("######################### DONE");
|
||||
return 0;
|
||||
}
|
||||
|
100
tests/posix_semaphore/tests/01-run.py
Executable file
100
tests/posix_semaphore/tests/01-run.py
Executable file
@ -0,0 +1,100 @@
|
||||
#! /usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim:fenc=utf-8
|
||||
#
|
||||
# Copyright (C) 2015 Martine Lenders <mlenders@inf.fu-berlin.de>
|
||||
#
|
||||
# This file is subject to the terms and conditions of the GNU Lesser
|
||||
# General Public License v2.1. See the file LICENSE in the top level
|
||||
# directory for more details.
|
||||
|
||||
import sys
|
||||
import pexpect
|
||||
|
||||
def init():
|
||||
term = pexpect.spawn("make term", timeout=1.1)
|
||||
term.logfile = sys.stdout
|
||||
return term
|
||||
|
||||
def test1(term):
|
||||
term.expect_exact("######################### TEST1:")
|
||||
term.expect_exact("first: sem_init")
|
||||
term.expect_exact("first: thread create")
|
||||
term.expect_exact("first: thread created")
|
||||
term.expect_exact("first: sem_getvalue")
|
||||
term.expect_exact("first: sem_getvalue != 0")
|
||||
term.expect_exact("first: do yield")
|
||||
term.expect_exact("second: sem_trywait")
|
||||
term.expect_exact("second: sem_trywait done with == 0")
|
||||
term.expect_exact("second: wait for post")
|
||||
term.expect_exact("first: done yield")
|
||||
term.expect_exact("first: sem_trywait")
|
||||
term.expect_exact("first: sem_trywait FAILED")
|
||||
term.expect_exact("first: sem_trywait done")
|
||||
term.expect_exact("first: sem_post")
|
||||
term.expect_exact("second: sem_wait failed")
|
||||
term.expect_exact("second: sem was posted")
|
||||
term.expect_exact("second: end")
|
||||
term.expect_exact("first: sem_post done")
|
||||
term.expect_exact("first: sem_destroy")
|
||||
term.expect_exact("first: end")
|
||||
|
||||
def test2(term):
|
||||
term.expect_exact("######################### TEST2:")
|
||||
term.expect_exact("first: sem_init")
|
||||
term.expect_exact("first: thread create: 5")
|
||||
term.expect_exact("first: thread created: priority 5 (1/5)")
|
||||
term.expect_exact("first: thread create: 4")
|
||||
term.expect_exact("first: thread created: priority 4 (2/5)")
|
||||
term.expect_exact("first: thread create: 3")
|
||||
term.expect_exact("first: thread created: priority 3 (3/5)")
|
||||
term.expect_exact("first: thread create: 2")
|
||||
term.expect_exact("first: thread created: priority 2 (4/5)")
|
||||
term.expect_exact("first: thread create: 1")
|
||||
term.expect_exact("first: thread created: priority 1 (5/5)")
|
||||
term.expect_exact("------------------------------------------")
|
||||
term.expect_exact("post no. 0")
|
||||
term.expect_exact("Thread 'priority 1' woke up.")
|
||||
term.expect_exact("Back in main thread.")
|
||||
term.expect_exact("post no. 1")
|
||||
term.expect_exact("Thread 'priority 2' woke up.")
|
||||
term.expect_exact("Back in main thread.")
|
||||
term.expect_exact("post no. 2")
|
||||
term.expect_exact("Thread 'priority 3' woke up.")
|
||||
term.expect_exact("Back in main thread.")
|
||||
term.expect_exact("post no. 3")
|
||||
term.expect_exact("Thread 'priority 4' woke up.")
|
||||
term.expect_exact("Back in main thread.")
|
||||
term.expect_exact("post no. 4")
|
||||
term.expect_exact("Thread 'priority 5' woke up.")
|
||||
term.expect_exact("Back in main thread.")
|
||||
|
||||
def test3(term):
|
||||
term.expect_exact("######################### TEST3:")
|
||||
term.expect_exact("first: sem_init s1")
|
||||
term.expect_exact("first: sem_init s2")
|
||||
term.expect_exact("first: create thread 1")
|
||||
term.expect_exact("first: create thread 2")
|
||||
term.expect_exact("------------------------------------------")
|
||||
term.expect_exact("post s1")
|
||||
term.expect_exact("Thread 1 woke up after waiting for s1.")
|
||||
term.expect_exact("post s2")
|
||||
term.expect_exact("Thread 2 woke up after waiting for s2.")
|
||||
term.expect_exact("post s2")
|
||||
term.expect_exact("Thread 1 woke up after waiting for s2.")
|
||||
term.expect_exact("post s1")
|
||||
term.expect_exact("Thread 2 woke up after waiting for s1.")
|
||||
|
||||
def test4(term):
|
||||
term.expect_exact("######################### TEST4:")
|
||||
term.expect_exact("first: sem_init s1")
|
||||
term.expect_exact("first: wait 1 sec for s1")
|
||||
term.expect(r"first: waited 1\.\d{6} s")
|
||||
|
||||
if __name__ == "__main__":
|
||||
TERM = init()
|
||||
test1(TERM)
|
||||
test2(TERM)
|
||||
test3(TERM)
|
||||
test4(TERM)
|
||||
TERM.expect("######################### DONE")
|
Loading…
Reference in New Issue
Block a user