diff --git a/kernel/atom.h b/kernel/atom.h index 164c968c..08becd2c 100755 --- a/kernel/atom.h +++ b/kernel/atom.h @@ -76,6 +76,8 @@ typedef struct atom_tcb /* Suspension data */ uint8_t suspended; /* TRUE if task is currently suspended */ uint8_t suspend_wake_status; /* Status returned to woken suspend calls */ + uint32_t suspend_option; /* yujs used for event to save the event flags get option*/ + uint32_t suspend_info; /* Remember which event flags we are looking for. requested_flags yujs */ ATOM_TIMER *suspend_timo_cb; /* Callback registered for suspension timeouts */ uint8_t terminated; /* TRUE if task is being terminated (run to completion) */ diff --git a/kernel/atomevent.c b/kernel/atomevent.c new file mode 100755 index 00000000..39a04bb4 --- /dev/null +++ b/kernel/atomevent.c @@ -0,0 +1,857 @@ +/* + * Copyright (c) 2017, jinsong yu, ramaxel. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. No personal names or organizations' names associated with the + * Atomthreads project may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + + +/** + * \file + * event library. + * + * + * This module implements a event library with the following + * features: + * + * \par Flexible blocking APIs with event + * Threads which wish to get a event can choose whether to block, + * block with timeout, or not block if the event timeout value is -1. + * + * \par Interrupt-safe calls + * All APIs can be called from interrupt context. Any calls which could + * potentially block have optional parameters to prevent blocking if you + * wish to call them from interrupt context. Any attempt to make a call + * which would block from interrupt context will be automatically and + * safely prevented. + * + * \par Priority-based queueing + * Where multiple threads are blocking on a event, they are woken in + * order of the threads' priorities. Where multiple threads of the same + * priority are blocking, they are woken in FIFO order. + * + * \par event gruop + * event groups range up to a maximum of 32 bit flags. + * + * \par Smart event deletion + * Where a event is deleted while threads are blocking on it, all blocking + * threads are woken and returned a status code to indicate the reason for + * being woken. + * + * + * \n Usage instructions: \n + * + * All event objects must be initialised before use by calling + * atomEventCreate(). Once initialised atomEventGet() and atomEventGet() are used to + * put/get the event respectively. + * + * If event group is create with all flags cleared, further calls to atomEventGet() will block + * the calling thread (unless the calling parameters of timeout request no blocking). If + * a call is made to atomEventSet() to satify the requested flags,for the threads are blocking on a zero-count + * event flags, the highest priority thread is woken. Where multiple threads of + * the same priority are blocking, they are woken in the order in which the + * threads started blocking. + + * + * Note that those considering using a event group flags are initialised to 0 in + * Atomthreads. + * + */ + + +#include "atom.h" +#include "atomevent.h" +#include "atomtimer.h" + +#include + + +/* Local data types */ + +typedef struct event_timer +{ + ATOM_TCB *tcb_ptr; /* Thread which is suspended with timeout */ + ATOM_EVENT *event_ptr; /* event the thread is suspended on */ +} EVENT_TIMER; + + +static void atomEventTimerCallback (POINTER cb_data); + +/** + * \b atomEventCreate + * + * This function creates a group of 32 event flags. All the flags are + * initially in a cleared state. + * Must be called before calling any other event library routines on a + * event. Objects can be deleted later using atom_event_Delete(). + * + * Does not allocate storage, the caller provides the + * + * This function can be called from interrupt context. + * event_ptr is clear during creation + * + * @param[in] event_ptr Pointer to event flags group control block + * @param[in] Pointer to event flags name + * + * @retval ATOM_OK Success + * @retval ATOM_ERR_PARAM Bad parameters + */ +uint8_t atomEventCreate(ATOM_EVENT *event_ptr, char *name_ptr) +{ + + uint8_t status; + + /* Initialize event flags control block to all zeros. */ + //memset(event_ptr, 0, sizeof(ATOM_EVENT)); + + /* Parameter check */ + if (event_ptr == NULL) + { + /* Bad event pointer */ + status = ATOM_ERR_PARAM; + } + else + { + + /* Initialize the suspended threads queue */ + event_ptr->suspQ = NULL; + + event_ptr->atom_event_current=0; + + /* Successful */ + status = ATOM_OK; + } + + return (status); +} + + + + +/** + * \b atomEventGet + * + * Perform a get operation on a event group flags. + * + * This check the 32bit event groups flags and returns. + + * Depending on the \c timeout value specified the call will do one of + * the following if the count value is zero: + * @param[in] timeout + * \c timeout == 0 : Call will block until event flag is satisfied or event group is deleted \n + * \c timeout > 0 : Call will block until non-zero up to the specified timeout \n + * \c timeout == -1 : Return immediately even the event flags requested which is not satified \n + ** + * If the call needs to block and \c timeout is non-zero, the call will only + * block for the specified number of system ticks after which time, if the + * thread was not already woken, the call will return with \c ATOM_TIMEOUT. + * + * If the call would normally block and \c timeout is -1, the call will + * return immediately with \c ATOM_WOULDBLOCK. + * + * This function can only be called from interrupt context if the \c timeout + * parameter is -1 (in which case it does not block). + * + * @param[in] event Pointer to event object + * @param[in] timeout Max system ticks to block (0 = forever, -1 return immediately) + * + * @param[in] event_ptr Pointer to group control block + * @param[in] requested_flags Event flags requested + * @param[in] get_option Specifies the all flags/any flags is satified by AND/OR + * @param[in] actual_flags_ptr Pointer to place the actual flags + * @param[in] timeout Suspension option + * + + + * @retval ATOM_OK Success + * @retval ATOM_TIMEOUT event timed out before being woken + * @retval ATOM_WOULDBLOCK Called with timeout == -1 but count is zero + * @retval ATOM_ERR_DELETED event was deleted while suspended + * @retval ATOM_ERR_CONTEXT Not called in thread context and attempted to block + * @retval ATOM_ERR_PARAM Bad parameter + * @retval ATOM_ERR_QUEUE Problem putting the thread on the suspend queue + * @retval ATOM_ERR_TIMER Problem registering the timeout + */ + +uint8_t atomEventGet(ATOM_EVENT *event_ptr, uint32_t requested_flags, + uint8_t get_option, uint32_t *actual_flags_ptr, uint32_t timeout) +{ + uint8_t status; + EVENT_TIMER timer_data; + ATOM_TIMER timer_cb; + ATOM_TCB *curr_tcb_ptr; + + uint32_t current_flags; +#ifdef DEBUG_EVENT + printf("%s enter \n",__func__); +#endif + CRITICAL_STORE; + /* Check parameters + * Check for an invalid destination for actual flags. + * Check for invalid get option. + * */ + if (event_ptr == NULL || actual_flags_ptr == NULL || get_option > ATOM_AND_CLEAR) + { + /* Bad event pointer */ + return(ATOM_ERR_PARAM); + } + + else + { + /* Protect access to the event object and OS queues */ + CRITICAL_START (); + + current_flags = event_ptr -> atom_event_current; +#ifdef DEBUG_EVENT + printf("atomEventGet current_flags %d \n",(int)event_ptr -> atom_event_current); +#endif + /* Determine if the event flags are present, based on the get option. */ + if (get_option & ATOM_EVENT_AND_MASK) + { + + /* All flags must be present to satisfy request. */ + if ((current_flags & requested_flags) == requested_flags) + { + /* Yes, all the events are present. */ + status = ATOM_OK; + } + else + { + + /* No, not all the events are present. */ + status = ATOM_NO_EVENTS; + } + } + else + { + + /* Any of the events will satisfy the request. so return immediately or will suspending the thread..*/ + if (current_flags & requested_flags) + { + + /* Yes, one or more of the requested events are set. */ + status = ATOM_OK; + } + else + { + + /* No, none of the events are currently set. */ + status = ATOM_NO_EVENTS; + } + } + + /* Now determine if the request can be satisfied immediately. */ + if (status == ATOM_OK) + { + + /* Yes, this request can be handled immediately. */ + + /* Return the actual event flags that satisfied the request. */ + *actual_flags_ptr = current_flags; + + /* Determine whether or not clearing needs to take place. */ + if (get_option & ATOM_EVENT_CLEAR_MASK) + { + + /* Yes, clear the flags that satisfied this request. */ + event_ptr -> atom_event_current = + event_ptr -> atom_event_current & ~requested_flags; + } + CRITICAL_END ();/*ensure each exit have chance to end the critical in each case*/ + } + else + { +#ifdef DEBUG_EVENT + printf("%s not meet the reqeust flag go here \n",__func__); +#endif + /* Determine if the request specifies suspension. it is timeout value ATOM_WAIT_FOREVER*/ + if (timeout >=0 ) + { + /* Prepare for suspension of this thread. */ + + /* Get the current TCB */ + curr_tcb_ptr = atomCurrentContext(); + /* Check we are actually in thread context */ + if (curr_tcb_ptr) + { + /* Add current thread to the suspend list on this event */ + if (tcbEnqueuePriority (&event_ptr->suspQ, curr_tcb_ptr) != ATOM_OK) + { + /* Exit critical region */ + CRITICAL_END (); + + /* There was an error putting this thread on the suspend list */ + status = ATOM_ERR_QUEUE; + } + else + { + /* Set suspended status for the current thread */ + curr_tcb_ptr->suspended = TRUE; + status = ATOM_OK; + + /* Remember which event flags we are looking for. */ + curr_tcb_ptr -> suspend_info = requested_flags; + curr_tcb_ptr -> suspend_option = get_option; +#ifdef DEBUG_EVENT + printf("atomEventGet suspend_option %d \n",(int)curr_tcb_ptr -> suspend_option); + printf("atomEventGet suspend_info %d \n",(int)curr_tcb_ptr -> suspend_info); +#endif + + /* Track errors */ + + + /* Register a timer callback if requested */ + if (timeout) + { + /* Fill out the data needed by the callback to wake us up */ + timer_data.tcb_ptr = curr_tcb_ptr; + timer_data.event_ptr = event_ptr; + + /* Fill out the timer callback request structure */ + timer_cb.cb_func = atomEventTimerCallback; + timer_cb.cb_data = (POINTER)&timer_data; + timer_cb.cb_ticks = timeout; + + /** + * Store the timer details in the TCB so that we can + * cancel the timer callback if the event is put + * before the timeout occurs. + */ + curr_tcb_ptr->suspend_timo_cb = &timer_cb; + + /* Register a callback on timeout */ + if (atomTimerRegister (&timer_cb) != ATOM_OK) + { + /* Timer registration failed */ + status = ATOM_ERR_TIMER; + + /* Clean up and return to the caller */ + (void)tcbDequeueEntry (&event_ptr->suspQ, curr_tcb_ptr); + curr_tcb_ptr->suspended = FALSE; + curr_tcb_ptr->suspend_timo_cb = NULL; + } + } + + /* Set no timeout requested */ + else + { + /* No need to cancel timeouts on this one */ + curr_tcb_ptr->suspend_timo_cb = NULL; + } + + /* Exit critical region */ + CRITICAL_END (); + + /* Check no errors have occurred */ + if (status == ATOM_OK ) + + { + /** + * Current thread now blocking, schedule in a new + * one. We already know we are in thread context + * so can call the scheduler from here. + */ +#ifdef DEBUG_EVENT + printf("%s suspend here \n",__func__); +#endif + atomSched (FALSE); + +#ifdef DEBUG_EVENT + printf("%s suspend go \n",__func__); +#endif + + /** + * Normal atomEventGet() wakeups will set ATOM_OK status, + * while timeouts will set ATOM_TIMEOUT and event + * deletions will set ATOM_ERR_DELETED. + */ + status = curr_tcb_ptr->suspend_wake_status; + + /** + * If we have been woken up with ATOM_OK then + * another thread set the event flags and + * handed control to this thread. + */ + + } + } + } + else + { + /* Exit critical region */ + CRITICAL_END (); + + /* Not currently in thread context, can't suspend */ + status = ATOM_ERR_CONTEXT; + } + } + + else + { + /* timeout == -1, requested not to block and count is zero */ + CRITICAL_END(); + status = ATOM_WOULDBLOCK; + } + + } + + } +#ifdef DEBUG_EVENT + printf("status %d leave \n",(unsigned int)status); + printf("%s leave \n",__func__); +#endif + + /* Return actual completion status. */ + return(status); +} + + +/** + * \b atomEventTimerCallback + * + * This is an internal function not for use by application code. + * + * Timeouts on suspended threads are notified by the timer system through + * this generic callback. The timer system calls us back with a pointer to + * the relevant \c EVENT_TIMER object which is used to retrieve the + * event group flags. + * + * @param[in] cb_data Pointer to a EVENT_TIMER object + */ +static void atomEventTimerCallback (POINTER cb_data) +{ + EVENT_TIMER *timer_data_ptr; +#ifdef DEBUG_EVENT + printf("%s enter \n",__func__); +#endif + + CRITICAL_STORE; + + /* Get the EVENT_TIMER structure pointer */ + timer_data_ptr = (EVENT_TIMER *)cb_data; + + /* Check parameter is valid */ + if (timer_data_ptr) + { + /* Enter critical region */ + CRITICAL_START (); + + /* Set status to indicate to the waiting thread that it timed out */ + timer_data_ptr->tcb_ptr->suspend_wake_status = ATOM_TIMEOUT; + + /* Flag as no timeout registered */ + timer_data_ptr->tcb_ptr->suspend_timo_cb = NULL; + + /* Remove this thread from the event suspend list */ + (void)tcbDequeueEntry (&timer_data_ptr->event_ptr->suspQ, timer_data_ptr->tcb_ptr); + + /* Put the thread on the ready queue */ + (void)tcbEnqueuePriority (&tcbReadyQ, timer_data_ptr->tcb_ptr); + + /* Exit critical region */ + CRITICAL_END (); + + /** + * Note that we don't call the scheduler now as it will be called + * when we exit the ISR by atomIntExit(). + */ + } +#ifdef DEBUG_EVENT + printf("%s leave \n",__func__); +#endif + +} +/* */ +/* DESCRIPTION */ +/* */ +/* This function sets the specified flags in the event group based on */ +/* the set option specified. All threads suspended on the group whose */ +/* get request can now be satisfied are resumed. */ +/* */ +/* INPUT */ +/* */ +/* group_ptr Pointer to group control block */ +/* flags_to_set Event flags to set */ +/* set_option Specified either AND or OR */ +/* operation on the event flags */ +/* */ +/* OUTPUT */ +/* */ +/* TX_SUCCESS Always returns success */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_system_preempt_check Check for preemption */ +/* _tx_thread_system_resume Resume thread service */ +/* _tx_thread_system_ni_resume Non-interruptable resume thread */ +/* */ +/* CALLED BY */ + +/** + * \b atomEventSet + * + * This function sets the specified flags in the event group based on + * the set option specified. All threads suspended on the group whose + * get request can now be satisfied are resumed. + * if there are threads blocking on the + * event, the call will wake up the highest priority thread suspended. Only + * one thread is woken per call to atomEventSet(). If multiple threads of the + * same priority are suspended, they are woken in order of suspension (FIFO). + * + * This function can be called from interrupt context. + * + * @param[in] event_ptr group Pointer to event object + * @param[in] flags_to_set to set + * @param[in] set_option which define the OR/AND operation for the requested event flags in atomEventGet + * + * @retval ATOM_OK Success + * @retval ATOM_ERR_PARAM Bad parameter + * @retval ATOM_ERR_QUEUE Problem putting a woken thread on the ready queue + * @retval ATOM_ERR_TIMER Problem cancelling a timeout for a woken thread + */ +uint8_t atomEventSet(ATOM_EVENT *event_ptr, uint32_t flags_to_set, uint8_t set_option) +{ + + uint8_t status; + CRITICAL_STORE; + ATOM_TCB *tcb_ptr; + +#ifdef DEBUG_EVENT + printf("%s enter \n",__func__); +#endif + /* Check parameters + * Check for an invalid destination for actual flags. + * Check for invalid get option. + * */ + if (event_ptr == NULL || ((set_option != ATOM_AND) && (set_option != ATOM_OR)) ) + { + /* Bad event pointer */ + return(ATOM_ERR_PARAM); + } + + else + { + CRITICAL_START (); + /* Determine how to set this group's event flags. */ + if (set_option & ATOM_EVENT_AND_MASK) + { + + /* Previous set operation was not interrupted, simply clear the + specified flags by "ANDing" the flags into the current events + of the group. */ + event_ptr -> atom_event_current = + event_ptr -> atom_event_current & flags_to_set; + + /* There is no need to check for any suspended threads since no + new bits are set. */ + CRITICAL_END (); + + /* Return successful status. */ + return(ATOM_OK); + } + else + { + + /* "OR" the flags into the current events of the group. */ + event_ptr -> atom_event_current = + event_ptr -> atom_event_current | flags_to_set; + + /* Determine if there are any delayed flags to clear. */ + } + + +#ifdef DEBUG_EVENT + printf(" event_ptr -> atom_event_current 0x%8x \n",(unsigned int)event_ptr -> atom_event_current); +#endif + + /* If any threads are blocking on the event, wake up one */ + if (event_ptr->suspQ) + { +#ifdef DEBUG_EVENT + printf("tcbDequeueHead to pull out queue.\n"); +#endif + + tcb_ptr = tcbDequeueHead (&event_ptr->suspQ); + //////////////////////////////////////////////////////// + /* copy from thread event get.***********/ + /* Determine if this thread's get event flag request has been met. */ + if (tcb_ptr -> suspend_option & ATOM_EVENT_AND_MASK) + { +#ifdef DEBUG_EVENT + printf("AND event_ptr -> atom_event_current 0x%8x \n",(unsigned int)event_ptr -> atom_event_current); + printf("AND event_ptr -> suspend_info 0x%8x \n",(unsigned int)tcb_ptr -> suspend_info); +#endif + + + /* All flags must be present to satisfy request. */ + if ((event_ptr -> atom_event_current & tcb_ptr -> suspend_info) == + tcb_ptr -> suspend_info) + + /* Yes, all the events are present. */ + status = ATOM_OK; + else + + /* No, not all the events are present. */ + status = ATOM_NO_EVENTS; + } + else + { +#ifdef DEBUG_EVENT + printf("OR event_ptr -> atom_event_current 0x%8x \n",(unsigned int)event_ptr -> atom_event_current); + printf("OR event_ptr -> suspend_info 0x%8x \n",(unsigned int)tcb_ptr -> suspend_info); +#endif + + + /* Any of the events will satisfy the request. */ + if (event_ptr -> atom_event_current & tcb_ptr -> suspend_info) + + /* Yes, one or more of the requested events are set. */ + status = ATOM_OK; + else + + /* No, none of the events are currently set. */ + status = ATOM_NO_EVENTS; + } +#ifdef DEBUG_EVENT + printf("check suspQ status %d \n",(unsigned int)status); +#endif + + + + /* Was the suspended thread's event request satisfied? */ + /* enter this case it must return OK*/ + if (status == ATOM_OK) + { + + /* Yes, resume the thread and apply any event flag + clearing. */ + + /* Determine whether or not clearing needs to take place. */ + if (tcb_ptr ->suspend_option & ATOM_EVENT_CLEAR_MASK) + { + + /* Yes, clear the flags that satisfied this request. */ + event_ptr -> atom_event_current = + event_ptr -> atom_event_current & ~(tcb_ptr ->suspend_option); + } + + + /* Set OK status to be returned to the waiting thread */ + tcb_ptr->suspend_wake_status = ATOM_OK; + + +#ifdef DEBUG_EVENT + printf ("enqueue the thread into ready queue \n"); +#endif + if (tcbEnqueuePriority (&tcbReadyQ, tcb_ptr) != ATOM_OK) + { + /* Exit critical region */ + CRITICAL_END (); + + /* There was a problem putting the thread on the ready queue */ + status = ATOM_ERR_QUEUE; + } + else + { + if ((tcb_ptr->suspend_timo_cb != NULL) + && (atomTimerCancel (tcb_ptr->suspend_timo_cb) != ATOM_OK)) + { + /* There was a problem cancelling a timeout on this event */ + status = ATOM_ERR_TIMER; + } + else + { + /* Flag as no timeout registered */ + tcb_ptr->suspend_timo_cb = NULL; + + /* Successful */ + status = ATOM_OK; + } + + + /* Exit critical region */ + CRITICAL_END (); + + /** + * The scheduler may now make a policy decision to thread + * switch if we are currently in thread context. If we are + * in interrupt context it will be handled by atomIntExit(). + */ + if (atomCurrentContext()) + atomSched (FALSE); + } + } + + else/* if not the ATOM_OK, enqueue the ptr checkout just now*/ + { + + //case for ATOM_NO_EVENTS + +#ifdef DEBUG_EVENT + printf("status %d \n",status); + printf("tcbEnqueuePriority to suspq\n"); +#endif + if (tcb_ptr) + { + if (tcbEnqueuePriority (&event_ptr->suspQ, tcb_ptr) != ATOM_OK) + { + + /* There was a problem putting the thread on the ready queue */ + status = ATOM_ERR_QUEUE; + } + } + /* Exit critical region */ + CRITICAL_END (); + + } + + /** + * Threads are woken up in priority order, with a FIFO system + * used on same priority threads. We always take the head, + * ordering is taken care of by an ordered list enqueue. + */ + + }/*end of suspend quque*/ + else + { + /* Exit critical region added 0329*/ + CRITICAL_END (); + } + + }/* end of else event_ptr == NULL*/ + + +#ifdef DEBUG_EVENT + printf("status %d leave \n",(unsigned int)status); + printf("%s leave \n",__func__); +#endif + return (status); +} + + +/* DESCRIPTION */ +/* */ +/* This function deletes the specified event flag group. All threads */ +/* suspended on the group are resumed with the ATOM_DELETED status */ + +uint8_t atomEventDelete (ATOM_EVENT *event_ptr) +{ + uint8_t status; + CRITICAL_STORE; + ATOM_TCB *tcb_ptr; + uint8_t woken_threads = FALSE; + + /* Parameter check */ + if (event_ptr == NULL) + { + /* Bad event pointer */ + status = ATOM_ERR_PARAM; + } + else + { + /* Default to success status unless errors occur during wakeup */ + status = ATOM_OK; + + /* Wake up all suspended tasks */ + while (1) + { + /* Enter critical region */ + CRITICAL_START (); + + /* Check if any threads are suspended */ + tcb_ptr = tcbDequeueHead (&event_ptr->suspQ); + + /* A thread is suspended on the event group */ + if (tcb_ptr) + { +#ifdef DEBUG_EVENT + printf("tcb_ptr->suspend_wake_status %d\n",tcb_ptr->suspend_wake_status); +#endif + + /* Return error status to the waiting thread */ + tcb_ptr->suspend_wake_status = ATOM_ERR_DELETED; + + /* Put the thread on the ready queue */ + if (tcbEnqueuePriority (&tcbReadyQ, tcb_ptr) != ATOM_OK) + { + /* Exit critical region */ + CRITICAL_END (); + + /* Quit the loop, returning error */ + status = ATOM_ERR_QUEUE; + break; + } + + /* If there's a timeout on this suspension, cancel it */ + if (tcb_ptr->suspend_timo_cb) + { + /* Cancel the callback */ + if (atomTimerCancel (tcb_ptr->suspend_timo_cb) != ATOM_OK) + { + /* Exit critical region */ + CRITICAL_END (); + + /* Quit the loop, returning error */ + status = ATOM_ERR_TIMER; + break; + } + + /* Flag as no timeout registered */ + tcb_ptr->suspend_timo_cb = NULL; + + } + + /* Exit critical region */ + CRITICAL_END (); + + /* Request a reschedule */ + woken_threads = TRUE; + } + + /* No more suspended threads */ + else + { + /* Exit critical region and quit the loop */ + CRITICAL_END (); + break; + } + } + + /* Call scheduler if any threads were woken up */ + if (woken_threads == TRUE) + { + /** + * Only call the scheduler if we are in thread context, otherwise + * it will be called on exiting the ISR by atomIntExit(). + */ + if (atomCurrentContext()) + atomSched (FALSE); + } + } + + return (status); +} + + diff --git a/kernel/atomevent.h b/kernel/atomevent.h new file mode 100755 index 00000000..106ae68d --- /dev/null +++ b/kernel/atomevent.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2017, jinsong yu, ramaxel, All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. No personal names or organizations' names associated with the + * Atomthreads project may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __ATOM_EVENT_H +#define __ATOM_EVENT_H + + +#define ATOM_NO_WAIT 0 +#define ATOM_WAIT_FOREVER 0xFFFFFFFFUL +#define ATOM_AND 2 +#define ATOM_AND_CLEAR 3 +#define ATOM_OR 0 +#define ATOM_OR_CLEAR 1 + + + +#define ATOM_EVENT_AND_MASK 0x2 +#define ATOM_EVENT_CLEAR_MASK 0x1 + + + +#define ATOM_NO_EVENTS 0x07 //located it here temp.. place it in atom.h finnally. + +/*ATOM_EVENT_FLAGS_GROUP * _ATOM_event_flags_created_ptr;*/ + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Define the event flags group structure utilized by the application. */ + +typedef struct atom_event +{ + uint32_t atom_event_current; + ATOM_TCB * suspQ; /* Queue of threads suspended on this event */ + +} ATOM_EVENT; + + + +extern uint8_t atomEventCreate(ATOM_EVENT *event_ptr, char *name_ptr); +extern uint8_t atomEventDelete (ATOM_EVENT *event_ptr); +extern uint8_t atomEventGet(ATOM_EVENT *event_ptr, uint32_t requested_flags,\ + uint8_t get_option, uint32_t *actual_flags_ptr, uint32_t timeout); +extern uint8_t atomEventSet(ATOM_EVENT *event_ptr, uint32_t flags_to_set, uint8_t set_option); + +#ifdef __cplusplus +} +#endif + + + +#endif /* __ATOM_EVENT_H */ diff --git a/tests/event1.c b/tests/event1.c new file mode 100755 index 00000000..4bf285be --- /dev/null +++ b/tests/event1.c @@ -0,0 +1,307 @@ +/* + * Copyright (c) 2017, jinsong yu ramaxel. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. No personal names or organizations' names associated with the + * Atomthreads project may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + + +#include "atom.h" +#include "atomevent.h" +#include "atomtests.h" + +#define RX_COMPLETE_FLAG 0x01 +#define TX_COMPLETE_FLAG 0x02 + + +/* Number of test threads */ +#define NUM_TEST_THREADS 2 + + +/* Test OS objects */ +static ATOM_EVENT event1, event2; +static ATOM_TCB tcb[NUM_TEST_THREADS]; +static uint8_t test_thread_stack[NUM_TEST_THREADS][TEST_THREAD_STACK_SIZE]; + + +/* Forward declarations */ +static void test1_thread_func (uint32_t param); +static void test2_thread_func (uint32_t param); + +static int event_set_tx_flag(); +//static void settimer2(uint32_t timeout); +static int event_set_rx_flag(); +//static void settimer1(uint32_t timeout); + + +/** + * \b test_start + * + * Start event get/set test, + * + * case1: create the thread1,event1,get with option ATOM_AND_CLEAR which means tx/rx here should be satisfied simulatiouly. + * here set the tx/rx one by one to see what happens, it expect the thread2 get the flag after the event_set_tx_flag. + * + * case2: create the thread2,event2,get with option ATOM_AND_CLEAR which means tx/rx here should be satisfied simulatiouly. + * here set the tx/rx one by one to see what happens, it expect the thread2 get the flag after the event_set_tx_flag. + * + * @retval Number of failures + */ +uint32_t test_start (void) +{ + int failures; + + failures = 0; + //case1: + ATOMLOG (_STR("test event1 case1: get option ATOM_OR_CLEAR ,set option ATOM_OR \n")); + + if (atomEventCreate (&event1, 0) != ATOM_OK) + { + ATOMLOG (_STR("Error creating test event 1\n")); + failures++; + } + else if (atomThreadCreate(&tcb[0], TEST_THREAD_PRIO, test1_thread_func, 0, + &test_thread_stack[0][0], + TEST_THREAD_STACK_SIZE, TRUE) != ATOM_OK) + { + /* Fail */ + ATOMLOG (_STR("Error creating test thread 1\n")); + failures++; + } + else + { + /* let thread1 to exec */ + if (atomTimerDelay(SYSTEM_TICKS_PER_SEC*5) != ATOM_OK) + { + ATOMLOG (_STR("Failed timer delay\n")); + } + + if( event_set_rx_flag() != ATOM_OK ) + { + /* Fail */ + ATOMLOG (_STR("Error get the thread 1 flag \n")); + failures++; + } + + atomTimerDelay (150*SYSTEM_TICKS_PER_SEC); + printf("thread1 test passed \n"); + } + + + //case2: + ATOMLOG (_STR("test event2 case2: get option ATOM_AND_CLEAR,set option ATOM_OR \n")); + + if (atomEventCreate (&event2, 0) != ATOM_OK) + { + ATOMLOG (_STR("Error creating test event 2\n")); + failures++; + } + else if (atomThreadCreate(&tcb[1], TEST_THREAD_PRIO, test2_thread_func, 0, + &test_thread_stack[1][0], + TEST_THREAD_STACK_SIZE, TRUE) != ATOM_OK) + { + /* Fail */ + ATOMLOG (_STR("Error creating test thread 2\n")); + failures++; + } + else + { + /* let thread2 to exec */ + if (atomTimerDelay(SYSTEM_TICKS_PER_SEC*5) != ATOM_OK) + { + ATOMLOG (_STR("Failed timer delay\n")); + } + + if( event_set_rx_flag() == ATOM_OK ) + { + /* Fail */ + ATOMLOG (_STR("Error get the thread 2 flag \n")); + failures++; + } + + if (atomTimerDelay(SYSTEM_TICKS_PER_SEC*10) != ATOM_OK) + { + ATOMLOG (_STR("Failed timer delay\n")); + } + + if( event_set_tx_flag() != ATOM_OK ) + { + /* Fail */ + ATOMLOG (_STR("Error get the thread 2 flag \n")); + failures++; + } + + atomTimerDelay (150*SYSTEM_TICKS_PER_SEC); + printf("thread2 test passed \n"); + } + + /* Check thread stack usage (if enabled) */ +#ifdef ATOM_STACK_CHECKING + { + uint32_t used_bytes, free_bytes; + int thread; + + /* Check all threads */ + for (thread = 0; thread < NUM_TEST_THREADS; thread++) + { + /* Check thread stack usage */ + if (atomThreadStackCheck (&tcb[thread], &used_bytes, &free_bytes) != ATOM_OK) + { + ATOMLOG (_STR("StackCheck\n")); + failures++; + } + else + { + /* Check the thread did not use up to the end of stack */ + if (free_bytes == 0) + { + ATOMLOG (_STR("StackOverflow %d\n"), thread); + failures++; + } + + /* Log the stack usage */ +#ifdef TESTS_LOG_STACK_USAGE + ATOMLOG (_STR("StackUse:%d\n"), (int)used_bytes); +#endif + } + } + } +#endif + + /* Quit */ + return failures; +} + +/** + * \b test1_thread_func + * + * Entry point for test thread 1 + * + * @param[in] param Unused (optional thread entry parameter) + * + * @return None + */ +static void test1_thread_func (uint32_t param) +{ + uint8_t status; + uint32_t actual_flags_ptr; + uint32_t requested_flags; + /* Compiler warnings */ + param = param; + + /* + * Wait on event2 get the flag. We are expecting to be woken up + * by after the .event_set_tx_flag + */ + requested_flags = (RX_COMPLETE_FLAG | RX_COMPLETE_FLAG); + printf("test1_thread_func get requested_flags %d \n",(unsigned int)requested_flags); + requested_flags=3; + + status = atomEventGet(&event1, requested_flags,ATOM_OR_CLEAR,&actual_flags_ptr,0xf0000001);//ATOM_WAIT_FOREVER + + printf("test1_thread_func status11 %d\n",status);//can't get also exec to here??? + if (status != ATOM_OK) + { + ATOMLOG (_STR("Test1 thread failed to wakeup (%d)\n"), status); + } + + /* Wait forever */ + while (1) + { + atomTimerDelay (SYSTEM_TICKS_PER_SEC); + } +} + + +/** + * \b test2_thread_func + * + * Entry point for test thread 2. + * + * @param[in] param Unused (optional thread entry parameter) + * + * @return None + */ +static void test2_thread_func (uint32_t param) +{ + uint8_t status; + uint32_t actual_flags_ptr; + uint32_t requested_flags; + /* Compiler warnings */ + param = param; + + /* + * Wait on event2 get the flag. We are expecting to be woken up + * by after the .event_set_tx_flag + */ + requested_flags = (RX_COMPLETE_FLAG | RX_COMPLETE_FLAG); + printf("test2_thread_func get requested_flags %d \n",(unsigned int)requested_flags); + requested_flags=3; + + status = atomEventGet(&event2, requested_flags,ATOM_AND_CLEAR,&actual_flags_ptr,0xf0000001);//ATOM_WAIT_FOREVER + + printf("test2_thread_func status11 %d\n",status);//can't get also exec to here??? + if (status != ATOM_OK) + { + ATOMLOG (_STR("Test2 thread failed to wakeup (%d)\n"), status); + } + + + /* Wait forever */ + while (1) + { + atomTimerDelay (SYSTEM_TICKS_PER_SEC); + } +} + + + +static int event_set_rx_flag() +{ + int status; + + printf("event_set_rx_flag atom_event_current 0x%8x \n",(unsigned int)event2.atom_event_current); + if ((status = atomEventSet(&event2, RX_COMPLETE_FLAG,ATOM_OR)) != ATOM_OK) + { + ATOMLOG (_STR("event_set_rx_flag (%d)\n"), status); + } + return status; +} + + +static int event_set_tx_flag() +{ + int status; + + + printf("event_set_tx_flag atom_event_current 0x%8x\n",(unsigned int)event2.atom_event_current); + if ((status = atomEventSet(&event2, TX_COMPLETE_FLAG,ATOM_OR)) != ATOM_OK) + { + ATOMLOG (_STR("event_set_tx_flag failed (%d)\n"), status); + } + + return status; + +} diff --git a/tests/event2.c b/tests/event2.c new file mode 100755 index 00000000..8c0309f5 --- /dev/null +++ b/tests/event2.c @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2017, jinsong yu, ramaxel. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. No personal names or organizations' names associated with the + * Atomthreads project may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + + +#include "atom.h" +#include "atomevent.h" +#include "atomtests.h" + + + + +#define RX_COMPLETE_FLAG 0x01 +#define TX_COMPLETE_FLAG 0x02 + + +/* Number of test threads */ +#define NUM_TEST_THREADS 2 + + +/* Test OS objects */ +static ATOM_EVENT event1; +static ATOM_TCB tcb[NUM_TEST_THREADS]; +static uint8_t test_thread_stack[NUM_TEST_THREADS][TEST_THREAD_STACK_SIZE]; + + +/* Forward declarations */ +static void test1_thread_func (uint32_t param); + + + +/** + * \b test_start + * + * Start event test. + * + * This test exercises the event creation and deletion APIs, including + * waking threads blocking on a event if the event is deleted. + * Deletion wakeups are tested twice: once for a thread which is blocking + * with a timeout and once for a thread which is blocking with no timeout. + * + * @retval Number of failures + */ +uint32_t test_start (void) +{ + int failures; + int i; + failures = 0; + + + /* Test creation and deletion of event: good values */ + for (i = 0; i < 1000; i++) + { + if (atomEventCreate (&event1, "event1") == ATOM_OK) + { + if (atomEventDelete (&event1) == ATOM_OK) + { + /* Success */ + } + else + { + /* Fail */ + ATOMLOG (_STR("Error deleting event1\n")); + failures++; + break; + } + } + else + { + /* Fail */ + ATOMLOG (_STR("Error creating event1\n")); + failures++; + break; + } + } + ATOMLOG (_STR("stress test for 1000 times atomEventCreate/atomEventDelete passed! \n")); + + /* Test creation and deletion of event: creation checks */ + if (atomEventCreate (NULL, 0) != ATOM_OK) + { + /* Success */ + } + else + { + /* Fail */ + ATOMLOG (_STR("Bad event creation checks\n")); + failures++; + } + + /* Test creation and deletion of event: deletion checks */ + if (atomEventDelete (NULL) != ATOM_OK) + { + /* Success */ + } + else + { + /* Fail */ + ATOMLOG (_STR("Bad event deletion checks\n")); + failures++; + } + + ATOMLOG (_STR("atomEventCreate/atomEventDelete parameter passed. \n")); + + + /* Test wakeup of threads on event deletion (thread blocking with no timeout) */ + if (atomEventCreate (&event1, "event1") != ATOM_OK) + { + ATOMLOG (_STR("Error creating test event 1\n")); + failures++; + } + + + else if (atomThreadCreate(&tcb[0], TEST_THREAD_PRIO, test1_thread_func, 0, + &test_thread_stack[0][0], + TEST_THREAD_STACK_SIZE, TRUE) != ATOM_OK) + { + /* Fail */ + ATOMLOG (_STR("Error creating test thread 1\n")); + failures++; + } + else + { + + /* + * We have created two event. sem1 is for the other thread + * to wait on, which we will delete from this thread. We want + * to see that the other thread is woken up if its event + * is deleted. This is indicated through event being posted + * back to us. + */ + + /* Wait for the other thread to start blocking on sem1 */ + if (atomTimerDelay(SYSTEM_TICKS_PER_SEC) != ATOM_OK) + { + ATOMLOG (_STR("Failed timer delay\n")); + failures++; + } + else + { + ATOMLOG (_STR("atomEventDelete event\n")); + /* The other thread will be blocking on sem1 now, delete sem1 */ + if (atomEventDelete(&event1) != ATOM_OK) + { + ATOMLOG (_STR("Failed event1 delete\n")); + failures++; + } + else + { + /* delay and let thread1 to get the delete flag. */ + if (atomTimerDelay(SYSTEM_TICKS_PER_SEC) != ATOM_OK) + { + ATOMLOG (_STR("Failed timer delay\n")); + failures++; + } + } + } + } + + + /* Check thread stack usage (if enabled) */ +#ifdef ATOM_STACK_CHECKING + { + uint32_t used_bytes, free_bytes; + int thread; + + /* Check all threads */ + for (thread = 0; thread < NUM_TEST_THREADS; thread++) + { + /* Check thread stack usage */ + if (atomThreadStackCheck (&tcb[thread], &used_bytes, &free_bytes) != ATOM_OK) + { + ATOMLOG (_STR("StackCheck\n")); + failures++; + } + else + { + /* Check the thread did not use up to the end of stack */ + if (free_bytes == 0) + { + ATOMLOG (_STR("StackOverflow %d\n"), thread); + failures++; + } + + /* Log the stack usage */ +#ifdef TESTS_LOG_STACK_USAGE + ATOMLOG (_STR("StackUse:%d\n"), (int)used_bytes); +#endif + } + } + } +#endif + + /* Quit */ + return failures; +} + +/** + * \b test1_thread_func + * + * Entry point for test thread 1. + * + * @param[in] param Unused (optional thread entry parameter) + * + * @return None + */ +static void test1_thread_func (uint32_t param) +{ + uint8_t status; + uint32_t actual_flags_ptr; + + /* Compiler warnings */ + param = param; + + /* + * Wait on event with no timeout. We are expecting to be woken up + * by the main thread delete while blocking. + */ + status = atomEventGet(&event1, (RX_COMPLETE_FLAG | RX_COMPLETE_FLAG),ATOM_AND_CLEAR,&actual_flags_ptr,ATOM_WAIT_FOREVER);//wait wait option 0 is failed. + if (status != ATOM_ERR_DELETED) + { + ATOMLOG (_STR("Test1 thread woke without deletion (%d)\n"), status); + } + else + { + ATOMLOG (_STR("unblock the thread by delete the event \n")); + } + + /* Wait forever */ + while (1) + { + atomTimerDelay (SYSTEM_TICKS_PER_SEC); + } +} + diff --git a/tests/event3.c b/tests/event3.c new file mode 100755 index 00000000..d98a627e --- /dev/null +++ b/tests/event3.c @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2017 jinsong yu, ramaxel. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. No personal names or organizations' names associated with the + * Atomthreads project may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + + +#include "atom.h" +#include "atomtests.h" +#include "atomevent.h" + + +/* Number of test threads */ +#define NUM_TEST_THREADS 3 + +#define RX_COMPLETE_FLAG 0x01 +#define TX_COMPLETE_FLAG 0x02 + +/* Test OS objects */ +static ATOM_EVENT event1; +static ATOM_TCB tcb[NUM_TEST_THREADS]; +static uint8_t test_thread_stack[NUM_TEST_THREADS][TEST_THREAD_STACK_SIZE]; + + +/* Test running flag */ +static volatile int test_running; + + +/* Forward declarations */ +static void test_thread_func (uint32_t param); +static void testCallback (POINTER cb_data); + + +/** + * \b test_start + * + * Start event3 test. + * + * This stress-tests atomEventGet()/atomEventSet() with a timer ticker callback + * continually calling atom_event_set() and several contexts continually + * calling atomEventGet(). This stresses in particular the atomEventGet() + * API, with one threads and main thread at different priorities get + * simultaneously, as well as a timer callback set it from + * interrupt context. In all cases the event is successful to get and set. + * + * This tests the thread-safety and interrupt-safety of the event + * APIs. + * + * @retval Number of failures + */ +uint32_t test_start (void) +{ + int failures; + uint32_t end_time,count; + ATOM_TIMER timer_cb; + uint32_t requested_flags; + uint32_t actual_flags_ptr; + requested_flags = (RX_COMPLETE_FLAG | RX_COMPLETE_FLAG); + + /* Default to zero failures */ + failures = 0; + count = 0; + + /* Create sem with count of zero */ + if (atomEventCreate(&event1, "event1") != ATOM_OK) + + { + ATOMLOG (_STR("Error creating test event 1\n")); + failures++; + } + else + { + /* Set the test running flag */ + test_running = TRUE; + + /* + * Fill out a timer callback request structure. Pass the timer + * structure itself so that the callback can requeue the request. + */ + timer_cb.cb_func = testCallback; + timer_cb.cb_data = &timer_cb; + timer_cb.cb_ticks = 1; + + /* + * Request a timer callback to run in one tick's time. The callback + * will automatically queue another so that this happens repeatedly + * until the test is flagged as finished. + */ + if (atomTimerRegister (&timer_cb) != ATOM_OK) + { + ATOMLOG (_STR("Error registering timer\n")); + failures++; + } + + /* Create thread 1 */ + else if (atomThreadCreate(&tcb[0], TEST_THREAD_PRIO , test_thread_func, 1, + &test_thread_stack[0][0], + TEST_THREAD_STACK_SIZE, TRUE) != ATOM_OK) + { + /* Fail */ + ATOMLOG (_STR("Error creating test thread 1\n")); + failures++; + } + + /* The test threads have now all been created */ + else + { + /* + * Continually decrement the event while the test threads + * and timer callbacks are continually incrementing it. The + * test finishes after this runs without error for 5 seconds. + */ + end_time = atomTimeGet() + (60*60*15 * SYSTEM_TICKS_PER_SEC); + while (atomTimeGet() < end_time) + { + /* Decrement the event */ + if(atomEventGet(&event1, requested_flags,ATOM_AND_CLEAR,&actual_flags_ptr,3*SYSTEM_TICKS_PER_SEC)!=ATOM_OK)//ATOM_WAIT_FOREVER + { + ATOMLOG (_STR("atomEventGet failure, actual_flags_ptr %d \n"),(int)actual_flags_ptr); + failures++; + break; + } + count++; + if(count%100 == 0) + { + ATOMLOG (_STR("atomEventGet atomTimeGet success, count %d \n"),(int)count); + } + } + + /* Test finished, stop the other threads and timer callbacks */ + test_running = FALSE; + + /* + * Wait before finishing: a timer callback could be due + * shortly, and we allocated the timer structure off the + * local call stack. + */ + atomTimerDelay(2); + + } + if (atomEventDelete (&event1) != ATOM_OK) + { + ATOMLOG (_STR("Delete failed\n")); + failures++; + } + } + + + + + /* Check thread stack usage (if enabled) */ +#ifdef ATOM_STACK_CHECKING + { + uint32_t used_bytes, free_bytes; + int thread; + + /* Check all threads */ + for (thread = 0; thread < NUM_TEST_THREADS; thread++) + { + /* Check thread stack usage */ + if (atomThreadStackCheck (&tcb[thread], &used_bytes, &free_bytes) != ATOM_OK) + { + ATOMLOG (_STR("StackCheck\n")); + failures++; + } + else + { + /* Check the thread did not use up to the end of stack */ + if (free_bytes == 0) + { + ATOMLOG (_STR("StackOverflow %d\n"), thread); + failures++; + } + + /* Log the stack usage */ +#ifdef TESTS_LOG_STACK_USAGE + ATOMLOG (_STR("StackUse:%d\n"), (int)used_bytes); +#endif + } + } + } +#endif + + /* Quit */ + return failures; + +} + + +/** + * \b test_thread_func + * + * Entry point for test thread. + * + * @param[in] param sleep_flag passed through here + * + * @return None + */ +static void test_thread_func (uint32_t param) +{ + int failures,count; + uint32_t requested_flags; + uint32_t actual_flags_ptr; + requested_flags = (RX_COMPLETE_FLAG | RX_COMPLETE_FLAG); + + /* Were we requested to sleep occasionally? */ + param = (int)param; + + /* Run until the main thread sets the finish flag or we get an error */ + failures = 0; + + while ((test_running == TRUE) && (failures == 0)) + { + /* get the event each 2 system time ticks */ + + if(atomEventGet(&event1, requested_flags,ATOM_AND_CLEAR,&actual_flags_ptr,2*SYSTEM_TICKS_PER_SEC)!=ATOM_OK)//ATOM_WAIT_FOREVER + { + ATOMLOG (_STR("atomEventGet failure, actual_flags_ptr %d \n"),(int)actual_flags_ptr); + failures++; + break; + } + count++; + if(count%100 == 0) + { + ATOMLOG (_STR("atomEventGet test_thread_func success, count %d \n"),(int)count); + } + } + + /* Loop forever */ + while (1) + { + atomTimerDelay (SYSTEM_TICKS_PER_SEC); + } +} + + +/** + * \b testCallback + * + * set the event from interrupt context. This will be occurring while + * atomEventGet() calls for 2 threads are in progress, + * + * Automatically requeues itself for one tick in the future, so this + * continually fires until the finish flag is set. + * + * @param[in] cb_data Pointer to the original ATOM_TIMER structure + */ +static void testCallback (POINTER cb_data) +{ + ATOM_TIMER *ptimer; + uint8_t status; + /* Pull out the original timer request */ + ptimer = (ATOM_TIMER *)cb_data; + + /* set event1 */ + if ((status = atomEventSet(&event1, RX_COMPLETE_FLAG||TX_COMPLETE_FLAG,ATOM_OR)) != ATOM_OK) + { + ATOMLOG (_STR("atomEventGet failure, actual_flags_ptr %d \n"),(int)status); + } + + /* Enqueue another timer callback in one tick's time */ + if (test_running == TRUE) + { + /* Update the callback time and requeue */ + ptimer->cb_ticks = 1; + if (atomTimerRegister (ptimer) != ATOM_OK) + { + } + } + else + { + /* Test finished, no more will be queued */ + } + +}