From bbd71681e45b3b018f8fa467f9a21da5ca29c137 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Thu, 9 Oct 2014 14:38:17 +0400 Subject: [PATCH 01/42] added publish_doc_release.sh --- stuff/doxygen/publish_doc_release.sh | 96 ++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 stuff/doxygen/publish_doc_release.sh diff --git a/stuff/doxygen/publish_doc_release.sh b/stuff/doxygen/publish_doc_release.sh new file mode 100644 index 0000000..55311bc --- /dev/null +++ b/stuff/doxygen/publish_doc_release.sh @@ -0,0 +1,96 @@ +#!/bin/bash + +# get tag name to generate docs for +target_tag_name="$1" + +# compare it +if [[ "$target_tag_name" == "" ]]; then + echo "usage: $ bash $0 " + exit 0 +fi + +# update to this revision +hg up $target_tag_name + +# get version string +version_string="$(bash ./hg_ver_echo.sh)" + +# generate commit message +new_commit_message="docs updated: $version_string" + +# make sure we have docs repo +if ! [ -d ./dfrank.bitbucket.org ]; then + # clone docs repo + hg clone https://bitbucket.org/dfrank/dfrank.bitbucket.org +fi + +# now, docs repo should be cloned in any case, go to it +cd dfrank.bitbucket.org + +# make sure we have the latest revision +hg pull -u + +# check last commit message +last_commit_message="$(hg log -l1 --template '{desc}')" + +# go back +cd .. + +# compare it +if [[ "$new_commit_message" == "$last_commit_message" ]]; then + echo "tneokernel repository has not changed, exiting" + exit 0 +fi + + +# ---- tneokernel repo has changed, so, continue ---- + +# remove all current output +rm -r ./output + +# make doxygen output (html, latex) +make + +# make pdf file +cd output/latex +make + +# rename refman to tneokernel.pdf +mv refman.pdf tneokernel.pdf + +# go back +cd ../.. + +# go to dev documentation dir +cd dfrank.bitbucket.org/tneokernel_api/latest + +# remove everything from there +rm -r ./* + +# copy new data there +cp -r ../../../output/* . + +# copy new data as new version docs as well +cp -r . ../$target_tag_name + +# addremove +hg addremove + +# commit +hg ci -m"$new_commit_message" + +# push it +# COMMENTED to be more safe +# hg push +echo "--------------------------------------------------------------------------" +echo "DON'T FORGET to push your changes now, if everything is ok:" +echo " $ cd dfrank.bitbucket.org" +echo " $ hg push" +echo "--------------------------------------------------------------------------" + +# go back +cd ../../.. + +# update repo to the tip +hg up tip + From 73cbd3ccc646e7fa2755e14bcff49a40ac0ad157 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Thu, 9 Oct 2014 14:59:16 +0400 Subject: [PATCH 02/42] working on create_version_archive.sh --- stuff/doxygen/create_version_archive.sh | 82 +++++++++++++++++++++++++ stuff/doxygen/publish_doc_release.sh | 2 +- 2 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 stuff/doxygen/create_version_archive.sh diff --git a/stuff/doxygen/create_version_archive.sh b/stuff/doxygen/create_version_archive.sh new file mode 100644 index 0000000..6c95708 --- /dev/null +++ b/stuff/doxygen/create_version_archive.sh @@ -0,0 +1,82 @@ +#!/bin/bash + +# get tag name to generate docs for +target_tag_name="$1" + +# compare it +if [[ "$target_tag_name" == "" ]]; then + echo "usage: $ bash $0 " + exit 0 +fi + +# update to this revision +hg up $target_tag_name + +# get version string +version_string="$(bash ./hg_ver_echo.sh)" + +# generate archive name (and directory name) +archive_name="tneokernel-$version_string" + +# get full path to output archive +archive_full_name="/tmp/$archive_name" + +# remove all current doxygen output +rm -r ./output + +# make doxygen output (html, latex) +make + +# make pdf file +cd output/latex +make + +# rename refman to tneokernel.pdf +mv refman.pdf tneokernel.pdf + +# go back +cd ../.. + +# go to root of the repo +pushd ../.. + +# make directory that will be packed soon +mkdir $archive_full_name + +# copy data there +rsync -av --exclude=".hg*" . $archive_full_name + +# cd to newly created directory (that will be packed soon) +cd $archive_full_name + +# remove tn_cfg.h +rm $archive_full_name/src/tn_cfg.h + +# create doc dirs +mkdir doc{html,pdf} + +# copy all html output as is +cp -r stuff/doxygen/output/html/* doc + +# copy pdf +cp stuff/doxygen/output/latex/tneokernel.pdf doc/pdf + +# delete doxygen output dir +rm -r stuff/doxygen/output + +# pack dir + + +echo "--------------------------------------------------------------------------" +echo "DON'T FORGET to push your changes now, if everything is ok:" +echo " $ cd dfrank.bitbucket.org" +echo " $ hg push" +echo "--------------------------------------------------------------------------" + +# go back +popd + +# update repo to the tip +hg up tip + + diff --git a/stuff/doxygen/publish_doc_release.sh b/stuff/doxygen/publish_doc_release.sh index 55311bc..779c929 100644 --- a/stuff/doxygen/publish_doc_release.sh +++ b/stuff/doxygen/publish_doc_release.sh @@ -61,7 +61,7 @@ mv refman.pdf tneokernel.pdf # go back cd ../.. -# go to dev documentation dir +# go to latest stable documentation dir cd dfrank.bitbucket.org/tneokernel_api/latest # remove everything from there From 7676feab947242f2aeb55d30512e0a311de2fbe8 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Thu, 9 Oct 2014 15:30:00 +0400 Subject: [PATCH 03/42] create_version_archive.sh works, examples_readme updated --- examples/examples_readme!!!.txt | 2 +- stuff/doxygen/create_version_archive.sh | 42 +++++++++++++++++++------ 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/examples/examples_readme!!!.txt b/examples/examples_readme!!!.txt index d31be36..f96a6bc 100644 --- a/examples/examples_readme!!!.txt +++ b/examples/examples_readme!!!.txt @@ -1,6 +1,6 @@ Before trying to build examples, please read the following page carefully: -http://dfrank.bitbucket.org/tneokernel_api/html/building.html +http://dfrank.bitbucket.org/tneokernel_api/latest/html/building.html In short, you need to copy configuration file in the tneokernel directory to build it. Each example has `tn_cfg_appl.h` file, and you should either create diff --git a/stuff/doxygen/create_version_archive.sh b/stuff/doxygen/create_version_archive.sh index 6c95708..f7e43f8 100644 --- a/stuff/doxygen/create_version_archive.sh +++ b/stuff/doxygen/create_version_archive.sh @@ -21,9 +21,12 @@ archive_name="tneokernel-$version_string" # get full path to output archive archive_full_name="/tmp/$archive_name" +echo "target directory: \"$archive_full_name\"" + # remove all current doxygen output rm -r ./output + # make doxygen output (html, latex) make @@ -40,23 +43,29 @@ cd ../.. # go to root of the repo pushd ../.. +# if target directory exists, remove it +if test -d "$archive_full_name"; then + echo "target directory exists, removing.." + rm -r "$archive_full_name" +fi + # make directory that will be packed soon -mkdir $archive_full_name +mkdir "$archive_full_name" # copy data there -rsync -av --exclude=".hg*" . $archive_full_name +rsync -av --exclude=".hg*" --exclude=".vimprj" . "$archive_full_name" # cd to newly created directory (that will be packed soon) -cd $archive_full_name +cd "$archive_full_name" # remove tn_cfg.h -rm $archive_full_name/src/tn_cfg.h +rm "$archive_full_name/src/tn_cfg.h" # create doc dirs -mkdir doc{html,pdf} +mkdir -p doc/{html,pdf} # copy all html output as is -cp -r stuff/doxygen/output/html/* doc +cp -r stuff/doxygen/output/html/* doc/html # copy pdf cp stuff/doxygen/output/latex/tneokernel.pdf doc/pdf @@ -64,13 +73,28 @@ cp stuff/doxygen/output/latex/tneokernel.pdf doc/pdf # delete doxygen output dir rm -r stuff/doxygen/output +# remove dfrank.bitbucket.org repo +if test -d "stuff/doxygen/dfrank.bitbucket.org"; then + echo "removing stuff/doxygen/dfrank.bitbucket.org.." + rm -r "stuff/doxygen/dfrank.bitbucket.org" +fi + +# cd one dir back, in order to make paths in the resulting zip archive correct +cd .. + + +# if target archive exists, remove it +if test -f "$archive_full_name.zip"; then + echo "target archve exists, removing.." + rm "$archive_full_name.zip" +fi + # pack dir +zip -r "$archive_full_name.zip" "$archive_name" echo "--------------------------------------------------------------------------" -echo "DON'T FORGET to push your changes now, if everything is ok:" -echo " $ cd dfrank.bitbucket.org" -echo " $ hg push" +echo " resulting archive is saved as $archive_full_name.zip" echo "--------------------------------------------------------------------------" # go back From c9ad6d6a8df8b37f1e0cc4b5a7333b4e64c0a416 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Fri, 10 Oct 2014 02:43:28 +0400 Subject: [PATCH 04/42] added draft of tn_timer. It already works: task waiting is implemented on top of timers now. --- .../tneokernel.X/nbproject/configurations.xml | 1 + src/core/tn_common.h | 1 + src/core/tn_dqueue.c | 2 +- src/core/tn_internal.h | 51 +++- src/core/tn_mutex.c | 2 +- src/core/tn_sem.c | 2 +- src/core/tn_sys.c | 27 +- src/core/tn_tasks.c | 32 +- src/core/tn_tasks.h | 14 +- src/core/tn_timer.c | 280 ++++++++++++++++++ src/core/tn_timer.h | 156 ++++++++++ 11 files changed, 545 insertions(+), 23 deletions(-) create mode 100644 src/core/tn_timer.c create mode 100644 src/core/tn_timer.h diff --git a/src/arch/pic32/tneokernel.X/nbproject/configurations.xml b/src/arch/pic32/tneokernel.X/nbproject/configurations.xml index 8fa1ba5..8007b9b 100644 --- a/src/arch/pic32/tneokernel.X/nbproject/configurations.xml +++ b/src/arch/pic32/tneokernel.X/nbproject/configurations.xml @@ -27,6 +27,7 @@ ../../../core/tn_list.c ../../../core/tn_eventgrp.c ../../../core/tn_fmem.c + ../../../core/tn_timer.c timer, _task_wait_timeout, task); + _tn_task_set_dormant(task); //-- Add task to created task queue @@ -892,7 +903,7 @@ void _tn_task_set_waiting( struct TN_Task *task, struct TN_ListItem *wait_que, enum TN_WaitReason wait_reason, - unsigned long timeout + TN_Timeout timeout ) { #if TN_DEBUG @@ -904,7 +915,7 @@ void _tn_task_set_waiting( task->task_state |= TN_TASK_STATE_WAIT; task->task_wait_reason = wait_reason; - task->tick_count = timeout; + //task->tick_count = timeout; task->waited = TRUE; @@ -921,7 +932,8 @@ void _tn_task_set_waiting( //--- Add to the timers queue if (timeout != TN_WAIT_INFINITE){ - tn_list_add_tail(&tn_wait_timeout_list, &(task->timer_queue)); + _tn_timer_start(&task->timer, timeout); + //tn_list_add_tail(&tn_wait_timeout_list, &(task->timer_queue)); } } @@ -966,12 +978,18 @@ void _tn_task_clear_waiting(struct TN_Task *task, enum TN_RCode wait_rc) task->pwait_queue = NULL; task->task_wait_rc = wait_rc; +#if 0 //-- remove task from timer queue (if it is there) if (task->tick_count != TN_WAIT_INFINITE){ tn_list_remove_entry(&(task->timer_queue)); } task->tick_count = TN_WAIT_INFINITE; +#endif + + if (_tn_timer_is_active(&task->timer)){ + _tn_timer_cancel(&task->timer); + } //-- remove WAIT state task->task_state &= ~TN_TASK_STATE_WAIT; @@ -1019,19 +1037,19 @@ void _tn_task_set_dormant(struct TN_Task* task) #endif tn_list_reset(&(task->task_queue)); - tn_list_reset(&(task->timer_queue)); + //tn_list_reset(&(task->timer_queue)); _init_mutex_queue(task); _init_deadlock_list(task); task->pwait_queue = NULL; - task->priority = task->base_priority; //-- Task curr priority + task->priority = task->base_priority; //-- Task curr priority task->task_state |= TN_TASK_STATE_DORMANT; //-- Task state - task->task_wait_reason = 0; //-- Reason for waiting + task->task_wait_reason = TN_WAIT_REASON_NONE; //-- Reason for waiting task->task_wait_rc = TN_RC_OK; - task->tick_count = TN_WAIT_INFINITE; //-- Remaining time until timeout + //task->tick_count = TN_WAIT_INFINITE; //-- Remaining time until timeout task->tslice_count = 0; } diff --git a/src/core/tn_tasks.h b/src/core/tn_tasks.h index b760eea..49322af 100644 --- a/src/core/tn_tasks.h +++ b/src/core/tn_tasks.h @@ -55,6 +55,7 @@ #include "tn_eventgrp.h" #include "tn_dqueue.h" #include "tn_fmem.h" +#include "tn_timer.h" @@ -177,9 +178,12 @@ struct TN_Task { /// /// queue is used to include task in ready/wait lists struct TN_ListItem task_queue; + // + // queue is used to include task in timer list + //struct TN_ListItem timer_queue; /// - /// queue is used to include task in timer list - struct TN_ListItem timer_queue; + /// timer object to implement task waiting for timeout + struct TN_Timer timer; /// /// pointer to object's (semaphore, mutex, event, etc) wait list in which /// task is included for waiting @@ -235,9 +239,9 @@ struct TN_Task { /// /// waiting result code (reason why waiting finished) enum TN_RCode task_wait_rc; - /// - /// remaining time until timeout; may be `#TN_WAIT_INFINITE`. - unsigned long tick_count; + // + // remaining time until timeout; may be `#TN_WAIT_INFINITE`. + //TN_Timeout tick_count; /// /// time slice counter int tslice_count; diff --git a/src/core/tn_timer.c b/src/core/tn_timer.c new file mode 100644 index 0000000..03bc3b8 --- /dev/null +++ b/src/core/tn_timer.c @@ -0,0 +1,280 @@ +/******************************************************************************* + * + * TNeoKernel: real-time kernel initially based on TNKernel + * + * TNKernel: copyright © 2004, 2013 Yuri Tiomkin. + * PIC32-specific routines: copyright © 2013, 2014 Anders Montonen. + * TNeoKernel: copyright © 2014 Dmitry Frank. + * + * TNeoKernel was born as a thorough review and re-implementation of + * TNKernel. The new kernel has well-formed code, inherited bugs are fixed + * as well as new features being added, and it is tested carefully with + * unit-tests. + * + * API is changed somewhat, so it's not 100% compatible with TNKernel, + * hence the new name: TNeoKernel. + * + * Permission to use, copy, modify, and distribute this software in source + * and binary forms and its documentation for any purpose and without fee + * is hereby granted, provided that the above copyright notice appear + * in all copies and that both that copyright notice and this permission + * notice appear in supporting documentation. + * + * THIS SOFTWARE IS PROVIDED BY THE DMITRY FRANK 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 DMITRY FRANK 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. + * + ******************************************************************************/ + + +/******************************************************************************* + * INCLUDED FILES + ******************************************************************************/ + +//-- common tnkernel headers +#include "tn_common.h" +#include "tn_sys.h" +#include "tn_internal.h" + +//-- header of current module +#include "tn_timer.h" + +//-- header of other needed modules +#include "tn_tasks.h" + + + + +/******************************************************************************* + * PRIVATE FUNCTIONS + ******************************************************************************/ + +//-- Additional param checking {{{ +#if TN_CHECK_PARAM +static inline enum TN_RCode _check_param_generic( + struct TN_Timer *timer + ) +{ + enum TN_RCode rc = TN_RC_OK; + + if (timer == NULL){ + rc = TN_RC_WPARAM; + } else if (timer->id_timer != TN_ID_TIMER){ + rc = TN_RC_INVALID_OBJ; + } + + return rc; +} + +static inline enum TN_RCode _check_param_create( + struct TN_Timer *timer, + TN_TimerFunc *func + ) +{ + enum TN_RCode rc = TN_RC_OK; + + if (timer == NULL){ + rc = TN_RC_WPARAM; + } else if (0 + || timer->id_timer == TN_ID_TIMER + || func == NULL + ) + { + rc = TN_RC_WPARAM; + } + + return rc; +} + +#else +# define _check_param_generic(timer) (TN_RC_OK) +# define _check_param_create(timer, func) (TN_RC_OK) +#endif +// }}} + + + + + + +/******************************************************************************* + * PUBLIC FUNCTIONS + ******************************************************************************/ + +/* + * See comments in the header file (tn_timer.h) + */ +enum TN_RCode tn_timer_create( + struct TN_Timer *timer, + TN_TimerFunc *func, + void *p_user_data + ) +{ + enum TN_RCode rc = TN_RC_OK; + + rc = _check_param_create(timer, func); + if (rc != TN_RC_OK){ + goto out; + } + + if (!tn_is_task_context()){ + rc = TN_RC_WCONTEXT; + goto out; + } + + rc = _tn_timer_create(timer, func, p_user_data); + +out: + return rc; +} + +/* + * See comments in the header file (tn_timer.h) + */ +enum TN_RCode tn_timer_delete(struct TN_Timer *timer) +{ + TN_INTSAVE_DATA; + enum TN_RCode rc = TN_RC_OK; + + rc = _check_param_generic(timer); + if (rc != TN_RC_OK){ + goto out; + } + + if (!tn_is_task_context()){ + rc = TN_RC_WCONTEXT; + goto out; + } + + TN_INT_DIS_SAVE(); + + timer->id_timer = 0; //-- Timer does not exist now + + TN_INT_RESTORE(); + +out: + return rc; +} + +/* + * See comments in the header file (tn_timer.h) + */ +enum TN_RCode tn_timer_start(struct TN_Timer *timer, TN_Timeout timeout) +{ + TN_INTSAVE_DATA; + enum TN_RCode rc = TN_RC_OK; + + rc = _check_param_generic(timer); + if (rc != TN_RC_OK){ + goto out; + } + + if (!tn_is_task_context()){ + rc = TN_RC_WCONTEXT; + goto out; + } + + TN_INT_DIS_SAVE(); + + rc = _tn_timer_start(timer, timeout); + + TN_INT_RESTORE(); + +out: + return rc; +} + + + + +/******************************************************************************* + * PROTECTED FUNCTIONS + ******************************************************************************/ + +/** + * See comments in the tn_internal.h file + */ +void _tn_timer_tick_proceed(struct TN_Timer *timer) +{ + if (timer->timeout_cur == TN_WAIT_INFINITE || timer->timeout_cur == 0){ + //-- should never be here + _TN_FATAL_ERROR(); + } + + timer->timeout_cur--; + + if (timer->timeout_cur == 0){ + //-- it's time to fire. + //-- first of all, cancel timer, so that + // function could start it again if it wants to. + _tn_timer_cancel(timer); + + //-- call user function + timer->func(timer->p_user_data); + } +} + +enum TN_RCode _tn_timer_start(struct TN_Timer *timer, TN_Timeout timeout) +{ + enum TN_RCode rc = TN_RC_OK; + + if (timeout == TN_WAIT_INFINITE || timeout == 0){ + rc = TN_RC_WPARAM; + } else { + timer->timeout_cur = timeout; + + tn_list_add_tail(&tn_timer_list, &(timer->timer_queue)); + } + + return rc; +} + +enum TN_RCode _tn_timer_cancel(struct TN_Timer *timer) +{ + enum TN_RCode rc = TN_RC_OK; + + //-- set timeout to zero (but this is actually not necessary) + timer->timeout_cur = 0; + //timer->timeout_cur = 300; + + //-- remove entry from timer queue + tn_list_remove_entry(&(timer->timer_queue)); + tn_list_reset(&(timer->timer_queue)); + + return rc; +} + +enum TN_RCode _tn_timer_create( + struct TN_Timer *timer, + TN_TimerFunc *func, + void *p_user_data + ) +{ + enum TN_RCode rc = TN_RC_OK; + + tn_list_reset(&(timer->timer_queue)); + + timer->func = func; + timer->p_user_data = p_user_data; + timer->timeout_cur = 0; + + timer->id_timer = TN_ID_TIMER; + + return rc; +} + +BOOL _tn_timer_is_active(struct TN_Timer *timer) +{ + return !tn_is_list_empty(&(timer->timer_queue)); +} + + + diff --git a/src/core/tn_timer.h b/src/core/tn_timer.h new file mode 100644 index 0000000..fe356f2 --- /dev/null +++ b/src/core/tn_timer.h @@ -0,0 +1,156 @@ +/******************************************************************************* + * + * TNeoKernel: real-time kernel initially based on TNKernel + * + * TNKernel: copyright © 2004, 2013 Yuri Tiomkin. + * PIC32-specific routines: copyright © 2013, 2014 Anders Montonen. + * TNeoKernel: copyright © 2014 Dmitry Frank. + * + * TNeoKernel was born as a thorough review and re-implementation of + * TNKernel. The new kernel has well-formed code, inherited bugs are fixed + * as well as new features being added, and it is tested carefully with + * unit-tests. + * + * API is changed somewhat, so it's not 100% compatible with TNKernel, + * hence the new name: TNeoKernel. + * + * Permission to use, copy, modify, and distribute this software in source + * and binary forms and its documentation for any purpose and without fee + * is hereby granted, provided that the above copyright notice appear + * in all copies and that both that copyright notice and this permission + * notice appear in supporting documentation. + * + * THIS SOFTWARE IS PROVIDED BY THE DMITRY FRANK 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 DMITRY FRANK 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 + * + * A timer + * + */ + +#ifndef _TN_TIMER_H +#define _TN_TIMER_H + +/******************************************************************************* + * INCLUDED FILES + ******************************************************************************/ + +#include "tn_list.h" +#include "tn_common.h" + + + +#ifdef __cplusplus +extern "C" { /*}*/ +#endif + +/******************************************************************************* + * PUBLIC TYPES + ******************************************************************************/ + +typedef void (TN_TimerFunc)(void *p_user_data); + +/** + * Timer + */ +struct TN_Timer { + /// + /// A list item to be included in the system timer queue + struct TN_ListItem timer_queue; + /// + /// Function to be called by timer + TN_TimerFunc *func; + /// + /// User data pointer that is given to user-provided `func`. + void *p_user_data; + // + // Timeout value that is set by user + //TN_Timeout timeout_base; + /// + /// Current (left) timeout value + TN_Timeout timeout_cur; + /// + /// id for object validity verification + enum TN_ObjId id_timer; +}; + + +/******************************************************************************* + * GLOBAL VARIABLES + ******************************************************************************/ + +/******************************************************************************* + * DEFINITIONS + ******************************************************************************/ + +/******************************************************************************* + * PUBLIC FUNCTION PROTOTYPES + ******************************************************************************/ + +/** + * Construct the timer. `id_timer` field should not contain + * `#TN_ID_TIMER`, otherwise, `#TN_RC_WPARAM` is returned. + * + * $(TN_CALL_FROM_TASK) + * $(TN_CALL_FROM_ISR) + * $(TN_LEGEND_LINK) + * + * @param timer + * Pointer to already allocated `struct TN_Timer` + * @param func + * Function to be called by timer + * @param p_user_data + * User data pointer that is given to user-provided `func`. + * + * @return + * * `#TN_RC_OK` if timer was successfully created; + * * If `#TN_CHECK_PARAM` is non-zero, additional return code + * is available: `#TN_RC_WPARAM`. + */ +enum TN_RCode tn_timer_create( + struct TN_Timer *timer, + TN_TimerFunc *func, + void *p_user_data + ); + +/** + * Destruct the timer. If the timer is running, it is cancelled first. + * + * $(TN_CALL_FROM_TASK) + * $(TN_LEGEND_LINK) + * + * @param timer timer to destruct + * + * @return + * * `#TN_RC_OK` if timer was successfully deleted; + * * `#TN_RC_WCONTEXT` if called from wrong context; + * * If `#TN_CHECK_PARAM` is non-zero, additional return codes + * are available: `#TN_RC_WPARAM` and `#TN_RC_INVALID_OBJ`. + */ +enum TN_RCode tn_timer_delete(struct TN_Timer *timer); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // _TN_TIMER_H + +/******************************************************************************* + * end of file + ******************************************************************************/ + + From f8bd7a37e2ad20f3eb986467ef08e3e618f597c6 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Fri, 10 Oct 2014 02:48:31 +0400 Subject: [PATCH 05/42] tn_timer: little refactor --- src/core/tn_timer.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/core/tn_timer.c b/src/core/tn_timer.c index 03bc3b8..eedfdf9 100644 --- a/src/core/tn_timer.c +++ b/src/core/tn_timer.c @@ -241,12 +241,13 @@ enum TN_RCode _tn_timer_cancel(struct TN_Timer *timer) { enum TN_RCode rc = TN_RC_OK; - //-- set timeout to zero (but this is actually not necessary) + //-- reset timeout to zero timer->timeout_cur = 0; - //timer->timeout_cur = 300; //-- remove entry from timer queue tn_list_remove_entry(&(timer->timer_queue)); + + //-- reset the list (but this is actually not necessary) tn_list_reset(&(timer->timer_queue)); return rc; @@ -273,7 +274,7 @@ enum TN_RCode _tn_timer_create( BOOL _tn_timer_is_active(struct TN_Timer *timer) { - return !tn_is_list_empty(&(timer->timer_queue)); + return (timer->timeout_cur != 0); } From 429a2b10b3bf10158d6aecff29da94a063904f18 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Fri, 10 Oct 2014 03:12:14 +0400 Subject: [PATCH 06/42] tn_tasks: refactor: _tn_task_set_dormant() now does much less --- src/core/tn_tasks.c | 41 +++++++++++++++++++---------------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/src/core/tn_tasks.c b/src/core/tn_tasks.c index 4047d60..d1d4516 100644 --- a/src/core/tn_tasks.c +++ b/src/core/tn_tasks.c @@ -431,13 +431,26 @@ enum TN_RCode tn_task_create( task->task_state = TN_TASK_STATE_NONE; task->id_task = TN_ID_TASK; + task->task_wait_reason = TN_WAIT_REASON_NONE; + task->task_wait_rc = TN_RC_OK; + + task->pwait_queue = NULL; + //-- fill all task stack space by #TN_FILL_STACK_VAL for (ptr_stack = task->stk_start, i = 0; i < task->stk_size; i++){ *ptr_stack-- = TN_FILL_STACK_VAL; } + //-- reset task_queue (the queue used to include task to runqueue or + // waitqueue) + tn_list_reset(&(task->task_queue)); + + //-- init timer that is needed to implement task wait timeout _tn_timer_create(&task->timer, _task_wait_timeout, task); + _init_mutex_queue(task); + _init_deadlock_list(task); + _tn_task_set_dormant(task); //-- Add task to created task queue @@ -835,7 +848,6 @@ void _tn_task_set_runnable(struct TN_Task * task) priority = task->priority; task->task_state |= TN_TASK_STATE_RUNNABLE; - task->pwait_queue = NULL; //-- Add the task to the end of 'ready queue' for the current priority _add_entry_to_ready_queue(&(task->task_queue), priority); @@ -978,15 +990,8 @@ void _tn_task_clear_waiting(struct TN_Task *task, enum TN_RCode wait_rc) task->pwait_queue = NULL; task->task_wait_rc = wait_rc; -#if 0 - //-- remove task from timer queue (if it is there) - if (task->tick_count != TN_WAIT_INFINITE){ - tn_list_remove_entry(&(task->timer_queue)); - } - - task->tick_count = TN_WAIT_INFINITE; -#endif - + //-- if timer is active (i.e. task waits for timeout), + // cancel that timer if (_tn_timer_is_active(&task->timer)){ _tn_timer_cancel(&task->timer); } @@ -1033,23 +1038,15 @@ void _tn_task_set_dormant(struct TN_Task* task) #if TN_DEBUG if (task->task_state != TN_TASK_STATE_NONE){ _TN_FATAL_ERROR(""); + } else if (!tn_is_list_empty(&task->mutex_queue)){ + _TN_FATAL_ERROR(""); + } else if (!tn_is_list_empty(&task->deadlock_list)){ + _TN_FATAL_ERROR(""); } #endif - tn_list_reset(&(task->task_queue)); - //tn_list_reset(&(task->timer_queue)); - - _init_mutex_queue(task); - _init_deadlock_list(task); - - task->pwait_queue = NULL; - task->priority = task->base_priority; //-- Task curr priority task->task_state |= TN_TASK_STATE_DORMANT; //-- Task state - task->task_wait_reason = TN_WAIT_REASON_NONE; //-- Reason for waiting - task->task_wait_rc = TN_RC_OK; - - //task->tick_count = TN_WAIT_INFINITE; //-- Remaining time until timeout task->tslice_count = 0; } From 968e4868e9feb825389b483fcf2826990a43f8ff Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Fri, 10 Oct 2014 03:19:45 +0400 Subject: [PATCH 07/42] tn_tasks: added one more self-check --- src/core/tn_tasks.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/tn_tasks.c b/src/core/tn_tasks.c index d1d4516..fdcd25f 100644 --- a/src/core/tn_tasks.c +++ b/src/core/tn_tasks.c @@ -922,6 +922,8 @@ void _tn_task_set_waiting( //-- only SUSPEND bit is allowed here if (task->task_state & ~(TN_TASK_STATE_SUSPEND)){ _TN_FATAL_ERROR(""); + } else if (timeout == 0){ + _TN_FATAL_ERROR(""); } #endif From 4af05fc1e6450be7408c5e088831fb7481987bd9 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Fri, 10 Oct 2014 03:57:06 +0400 Subject: [PATCH 08/42] tn_timer: timer func now takes pointer to the timer --- src/core/tn_tasks.c | 2 +- src/core/tn_timer.c | 2 +- src/core/tn_timer.h | 8 +++++++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/core/tn_tasks.c b/src/core/tn_tasks.c index fdcd25f..51fac18 100644 --- a/src/core/tn_tasks.c +++ b/src/core/tn_tasks.c @@ -350,7 +350,7 @@ static void _task_terminate(struct TN_Task *task) /** * This function is called by timer */ -static void _task_wait_timeout(void *p_user_data) +static void _task_wait_timeout(struct TN_Timer *timer, void *p_user_data) { struct TN_Task *task = (struct TN_Task *)p_user_data; diff --git a/src/core/tn_timer.c b/src/core/tn_timer.c index eedfdf9..82458a4 100644 --- a/src/core/tn_timer.c +++ b/src/core/tn_timer.c @@ -218,7 +218,7 @@ void _tn_timer_tick_proceed(struct TN_Timer *timer) _tn_timer_cancel(timer); //-- call user function - timer->func(timer->p_user_data); + timer->func(timer, timer->p_user_data); } } diff --git a/src/core/tn_timer.h b/src/core/tn_timer.h index fe356f2..9c6521d 100644 --- a/src/core/tn_timer.h +++ b/src/core/tn_timer.h @@ -61,7 +61,13 @@ extern "C" { /*}*/ * PUBLIC TYPES ******************************************************************************/ -typedef void (TN_TimerFunc)(void *p_user_data); +struct TN_Timer; + + +/** + * Prototype of the function that should be called by timer + */ +typedef void (TN_TimerFunc)(struct TN_Timer *timer, void *p_user_data); /** * Timer From 0f83d52d0dd07af8d76595ef6f6d217fb392c107 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Fri, 10 Oct 2014 13:17:45 +0400 Subject: [PATCH 09/42] vimwiki cleaned, added 'how to release' --- stuff/doc_pages/why_reimplement.dox | 8 ++- stuff/vimwiki/how_to_release.wiki | 41 ++++++++++++++ stuff/vimwiki/index.wiki | 85 +++-------------------------- 3 files changed, 55 insertions(+), 79 deletions(-) create mode 100644 stuff/vimwiki/how_to_release.wiki diff --git a/stuff/doc_pages/why_reimplement.dox b/stuff/doc_pages/why_reimplement.dox index 9cf883f..076c1e1 100644 --- a/stuff/doc_pages/why_reimplement.dox +++ b/stuff/doc_pages/why_reimplement.dox @@ -244,7 +244,8 @@ TNKernel 2.7 has several bugs, which are caught by detailed unit tests and fixed Of course, task should be able to delete non-locked mutex; - If task that waits for mutex is in $(TN_TASK_STATE_WAITSUSP) state, and mutex is deleted, `#TERR_NO_ERR` is returned after returning from - $(TN_TASK_STATE_SUSPEND) state, instead of `#TERR_DLT` + $(TN_TASK_STATE_SUSPEND) state, instead of `#TERR_DLT`. The same for queue + deletion, semaphore deletion, event deletion. - `tn_sys_tslice_ticks()` : if wrong params are given, `#TERR_WRONG_PARAM` is returned and interrupts remain disabled. - `tn_queue_receive()` and `tn_fmem_get()` : if `timeout` is in effect, then @@ -252,6 +253,11 @@ TNKernel 2.7 has several bugs, which are caught by detailed unit tests and fixed (some garbage data is written there) - Probably not a "bug", but an issue in the data queue: actual capacity of the buffer is less by 1 than user has specified and allocated + - Event: if `TN_EVENT_ATTR_CLR` flag is set, and the task that is + waiting for event is suspended, this flag `TN_EVENT_ATTR_CLR` is ignored + (pattern is not reset). I can't say this bug is "fixed" because TNeoKernel + has \ref tnkernel_diff_event "event groups instead of events", and there is + no `TN_EVENT_ATTR_CLR` flag. Bugs with mutexes are the direct result of the inconsistency and copy-pasting the code, as well as lack of unit tests. diff --git a/stuff/vimwiki/how_to_release.wiki b/stuff/vimwiki/how_to_release.wiki new file mode 100644 index 0000000..095323e --- /dev/null +++ b/stuff/vimwiki/how_to_release.wiki @@ -0,0 +1,41 @@ + += tneokernel dev / how to release = + + * modify changelog: move everything from "_current development version_" to the new release section, specify release date. + * make sure changelog does NOT containt "_current development version_", because it should not be present in the release documentation. + * from the repo: + {{{ + + $ hg ci -m"changelog is updated for release vX.XX" + $ hg up stable + $ hg merge default + $ hg ci -m "merge default into stable for release vX.XX" + $ hg tag vX.XX + $ hg up default + $ hg merge stable + $ hg ci -m "merged stable vX.XX into default: ready for more development" + + }}} + + * add 'current development version' in the changelog back + * commit it: + `hg ci -m"changelog: added current development version section"` + + * publish release docs: + `$ bash ./publish_doc_release.sh vX.XX` + + * publish dev docs: + `$ bash ./publish_doc_dev.sh` + + * add link to docs index.html: + * `$ cd dfrank.bitbucket.org/tnkernel_api` + * in the index.html, add link to new vX.XX dir docs + + * push docs: `$ hg push` + * cd back to tneokernel repo: `$ cd ../..` + * create downloadable archive: `$ bash ./create_version_archive.sh vX.XX` + * go to https://bitbucket.org/dfrank/tneokernel/downloads and upload new archive there + * push repo to the bitbucket `$ hg push https://dfrank@bitbucket.org/dfrank/tneokernel` + + + diff --git a/stuff/vimwiki/index.wiki b/stuff/vimwiki/index.wiki index e8ab189..99a9b2b 100644 --- a/stuff/vimwiki/index.wiki +++ b/stuff/vimwiki/index.wiki @@ -1,91 +1,20 @@ -= tnkernel_df dev = += tneokernel dev = -== mods that don't change API == +[[how_to_release]] - * [X] remove `typedef`s, use `struct TNCamelCase` instead. - * [X] `tn_common.h`: convert macros to enums. - * [X] rename queue_... functions to list_.. (because it is actually a list, not a queue) - * [X] rename `tn_list.h` to `tn_list.h` or something like that - * [X] probably add bool/true/false - * [X] Implement a kind of `list_for_each` - * [X] workaround for crappy compilers: instead of recursive function call, use `goto in`; - * [X] fix bug: if some task tried to lock mutex with priority inheritance, and some other task's priority was changed because of that, and then tasks stopped waiting by timeout, priority of mutex holder doesn't change back (remains elevated) - * [X] implement deadlock detection (probably, with user-provided callback that will be called if something bad happens. Probably use this callback instead of TN_FATAL_ERROR) - * [X] move _unlock_all_mutexes to tn_mutex.c, make _tn_mutex_do_unlock private - * [X] probably don't initialize stack in the `tn_task_exit()`, because it is tricky. Do it in `_task_activate` probably - * [X] make `_tn_task_set_runnable` and `_tn_task_clear_runnable` to return `void`, and use special function `_tn_switch_context_if_needed`. - * [X] probably _tn_task_wait_complete should take task_wait_rc as a parameter? Say, sometimes, TERR_DLT needs to be returned - * [ ] get rid of `enum TN_WComplFlags`, _tn_task_clear_waiting should always behave like `TN_WCOMPL__REMOVE_WQUEUE` is set. While we refactor events, dqueue, sem, mem, we should always specify this flag when calling _tn_task_wait_complete, so eventually there will be no calls to _tn_task_wait_complete without setting this flag. At this moment, we should get rid of it. - * [ ] probably add new return value, such as TERR_FORCE or TERR_RELEASED, and return it when task was released from waiting by tn_task_release_wait. Currently TERR_NO_ERR returned which is not correct - * [ ] implement timer object that would run user-provided function at some time in the future - * [ ] clear tn_sys c/h files up - * [ ] task tests - * [X] re-activate task after it is deleted (try both tn_task_exit and tn_task_terminate) - * [ ] mutex tests - * [X] tn_task_delete on task_high waiting for mutex locked by task_low -> task_low's priority should go back to its base_priority - * [X] test deletion of mutex (event, sem, queue) while waiting task is suspended; when it is resumed, TERR_DLT should be returned - * [ ] ceiling mutexes - * [X] this one, regarding to order: - * A l M1 - * B l M1 - B blocks, A has priority of B - * C l M1 - C blocks, A has priority of C - * A u M1 - B unlocks, A has base priority - * B u M1 - C unlocks - * C u M1 -_tn_task_wait_complete -_mutex_do_unlock -== mods that DO change API == - - * [ ] Add a destroy callback for each task, this callback (if not NULL) should be called whenever task is terminated with `tn_task_exit()` or `tn_task_terminate()`. - * [ ] Should we actually allow to delete mutex while it's hold? - * [ ] Should we maintain wakeup_count, activate_count? +== Routine == -== modifications of original TNKernel == +== Plans == - * timer_task removed (huge performance boost: each timer tick time: ~700 ticks -> ~300 ticks) - * tn_mutex_lock() behaves just like tn_mutex_lock_polling() if zero timeout was given (instead of returning TERR_WRONG_PARAM) - * TN_USE_MUTEXES, TN_USE_EVENTS were checked by #ifdef, now they are checked by #if (actually there was an inconsistency: TN_CHECK_PARAM was always checked by #if) - * implemented deadlock detection (optionally) - -== bug fixes of original TNKernel == + * [ ] Timer object + * [ ] Event group connection (at least, it would be useful to connect event group to queue) + * [ ] Add a destroy callback for each task, this callback (if not NULL) should be called whenever task is terminated with `tn_task_exit()` or `tn_task_terminate()`. - * fixed bug: low-priority task_low locks mutex M1 with priority inheritance, high-priority task_high tries to lock mutex M1 and gets blocked -> task_low's priority elevates to task_high's priority; then task_high stops waiting for mutex by timeout -> priority of task_low remains elevated, but it should be set back. The same happens if task_high is terminated by tn_task_terminate() while it was waiting for mutex. The same happens if mutex is deleted while task_high is waiting for it. The same happens if task was waken up by tn_task_wakeup(). - * fixed bug: low-priority task A locks mutex M1 with priority inheritance, another task B with the same priority tries to lock M1 and gets blocked (priority of A = priority of B), then high-priority task C tries to lock M1 and gets blocked (priority of A = priority of C). Now, A unlocks M1 -> priority of A returns to base value, B locks M1 (because it is the next task in the mutex's queue), and its priority should be elevated to priority of C, but it doesn't happen. - * fixed bug in tn_sys_tslice_ticks() : if wrong params were given, interrupts remain disabled - * fixed bug in tn_mutex_delete() : if mutex is not locked, TERR_ILUSE is returned. - * fixed minor bug in _tn_task_wait_complete : we don't actually need to check TN_USE_MUTEXES there at all, it is even plain wrong. It doesn't break things because the condition was always false. - * fixed minor bug: when deleting mutex or semaphore, tn_switch_context was called for each non-suspended task that was waiting for that mutex/sepaphore. It's better to just make them runnable one-by-one without calling tn_switch_context after each one, and switch context just once after all (if needed). - * fixed bug: if task that waits for mutex was in WAITING_SUSPENDED state, and mutex is deleted, TERR_NO_ERR is returned instead of TERR_DLT. The same for queue deletion, semaphore deletion, event deletion. - * fixed bug: dqueue: actual capacity of buffer was less by 1 than user has specified (and allocated). - * found bug: event: if TN_EVENT_ATTR_CLR flag is set, and the task waiting for event is suspended, this flag TN_EVENT_ATTR_CLR is ignored (pattern is not reset). I can't say this bug is "fixed" because TNeoKernel has event groups instead of events, and there is no TN_EVENT_ATTR_CLR flag. -== bad style of original TNKernel == - - * A lot of #if...#endif in the code. examples: - * in tn_tasks.c, TN_USE_MUTEXES is checked. Now, _unlock_all_mutexes() was added instead - * #if TN_CHECK_PARAM ... #endif - * types: all tnkernel structures are hidden by typedef. It is not good idea. - * macro TN_CHECK_NON_INT_CONTEXT that returns from the function - * types: typedef name was in CAPS, so, it looks like a macro. - * a lot of 'tn_disable_interrupt(); return;' snippets, instead of "one entry, one exit" rule with goto statements. - * a lot of code duplication. Same tasks are accomplished just by copy-paste, instead of creating function for that. Examples of what I've added instead: - * _add_entry_to_ready_queue() / _remove_entry_from_ready_queue() - * _unlock_all_mutexes() - * task/interrupt/polling versions of functions were implemented separately, instead of provide some kind of wrapper functions that eventually call real worker function. (in tn_tasks.c: tn_task_release_wait/tn_task_irelease_wait, etc) - * inconsistency in several things - * TN_CHECK_PARAM was checked by #if, but TN_USE_MUTEXES, TN_USE_EVENTS were checked by #ifdef - -== bad things in TNKernel == - - * [ ] Is it really good idea that task does have wakeup_count, so you can call tn_task_wakeup before tn_task_sleep, and on the next tn_task_sleep call task won't go to sleep? It seems just like dirty hack to prevent race conditions. This dirty hack makes programmer able to not provide proper syncronization, this is really bad idea IMO. The same with activate_count and tn_task_terminate(). - * [ ] Is it really good idea that we are able to suspend any non-current task? It should be totally up to the task itself when it should sleep and when it shouldn't. If we need to pause some task from outside, we should implement a message to this task, and then task should put itself to sleep. - * [ ] Even more, is it really good idea that we are able to terminate any task? More, tasks in TNKernel don't even have any callback like "destructor" that should be called when task is terminated. So any non-tnkernel resources (such as memory from heap) that might be held by task will leak. - * [ ] The rule "one entry point, one exit point" is violated constantly across the whole sources. - * [ ] Code organization in general is far from perfect: this is just one header file with lots of stuff, including some private-to-tnkernel definitions like task_to_runnable, task_wait_complete etc. From 380d36284c28cd8968df597507c6a511b5d06531 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Fri, 10 Oct 2014 14:32:52 +0400 Subject: [PATCH 10/42] working on timers: added tn_timer_cancel(), added docs --- src/core/tn_timer.c | 96 +++++++++++++++++++++++++++++++++++- src/core/tn_timer.h | 76 ++++++++++++++++++++++++++-- stuff/doc_pages/mainpage.dox | 1 + stuff/vimwiki/index.wiki | 4 ++ 4 files changed, 173 insertions(+), 4 deletions(-) diff --git a/src/core/tn_timer.c b/src/core/tn_timer.c index 82458a4..d8144da 100644 --- a/src/core/tn_timer.c +++ b/src/core/tn_timer.c @@ -156,7 +156,13 @@ enum TN_RCode tn_timer_delete(struct TN_Timer *timer) TN_INT_DIS_SAVE(); - timer->id_timer = 0; //-- Timer does not exist now + //-- if timer is active, cancel it first + if (_tn_timer_is_active(timer)){ + rc = _tn_timer_cancel(timer); + } + + //-- now, delete timer + timer->id_timer = 0; TN_INT_RESTORE(); @@ -192,6 +198,94 @@ enum TN_RCode tn_timer_start(struct TN_Timer *timer, TN_Timeout timeout) return rc; } +/* + * See comments in the header file (tn_timer.h) + */ +enum TN_RCode tn_timer_istart(struct TN_Timer *timer, TN_Timeout timeout) +{ + TN_INTSAVE_DATA_INT; + enum TN_RCode rc = TN_RC_OK; + + rc = _check_param_generic(timer); + if (rc != TN_RC_OK){ + goto out; + } + + if (!tn_is_isr_context()){ + rc = TN_RC_WCONTEXT; + goto out; + } + + TN_INT_IDIS_SAVE(); + + rc = _tn_timer_start(timer, timeout); + + TN_INT_IRESTORE(); + +out: + return rc; +} + +/* + * See comments in the header file (tn_timer.h) + */ +enum TN_RCode tn_timer_cancel(struct TN_Timer *timer) +{ + TN_INTSAVE_DATA; + enum TN_RCode rc = TN_RC_OK; + + rc = _check_param_generic(timer); + if (rc != TN_RC_OK){ + goto out; + } + + if (!tn_is_task_context()){ + rc = TN_RC_WCONTEXT; + goto out; + } + + TN_INT_DIS_SAVE(); + + if (_tn_timer_is_active(timer)){ + rc = _tn_timer_cancel(timer); + } + + TN_INT_RESTORE(); + +out: + return rc; +} + +/* + * See comments in the header file (tn_timer.h) + */ +enum TN_RCode tn_timer_icancel(struct TN_Timer *timer) +{ + TN_INTSAVE_DATA_INT; + enum TN_RCode rc = TN_RC_OK; + + rc = _check_param_generic(timer); + if (rc != TN_RC_OK){ + goto out; + } + + if (!tn_is_isr_context()){ + rc = TN_RC_WCONTEXT; + goto out; + } + + TN_INT_IDIS_SAVE(); + + if (_tn_timer_is_active(timer)){ + rc = _tn_timer_cancel(timer); + } + + TN_INT_IRESTORE(); + +out: + return rc; +} + diff --git a/src/core/tn_timer.h b/src/core/tn_timer.h index 9c6521d..47d4bf2 100644 --- a/src/core/tn_timer.h +++ b/src/core/tn_timer.h @@ -65,7 +65,20 @@ struct TN_Timer; /** - * Prototype of the function that should be called by timer + * Prototype of the function that should be called by timer. + * + * When timer fires, function gets called by the kernel. Be aware of the + * following: + * - Function is called from ISR context (namely, from *system timer* ISR); + * - Function is called with global interrupts disabled; + * + * So, it's legal to call interrupt services from this function, and the + * function should be as fast as possible. + * + * @param timer + * Timer that caused function to be called + * @param p_user_data + * The user-provided pointer given to `tn_timer_create()`. */ typedef void (TN_TimerFunc)(struct TN_Timer *timer, void *p_user_data); @@ -117,7 +130,7 @@ struct TN_Timer { * @param timer * Pointer to already allocated `struct TN_Timer` * @param func - * Function to be called by timer + * Function to be called by timer. See `TN_TimerFunc()` * @param p_user_data * User data pointer that is given to user-provided `func`. * @@ -133,7 +146,7 @@ enum TN_RCode tn_timer_create( ); /** - * Destruct the timer. If the timer is running, it is cancelled first. + * Destruct the timer. If the timer is active, it is cancelled first. * * $(TN_CALL_FROM_TASK) * $(TN_LEGEND_LINK) @@ -148,6 +161,63 @@ enum TN_RCode tn_timer_create( */ enum TN_RCode tn_timer_delete(struct TN_Timer *timer); +/** + * Start the timer, that is, schedule the timer's function (given to + * `tn_timer_create()`) to be called later by the kernel. See `TN_TimerFunc()`. + * + * $(TN_CALL_FROM_TASK) + * $(TN_LEGEND_LINK) + * + * @param timer + * Timer to start + * @param timeout + * Number of system ticks after which timer should fire (i.e. function + * should be called). **Note** that `timeout` can't be `#TN_WAIT_INFINITE` or + * `0`. + * + * @return + * * `#TN_RC_OK` if timer was successfully started; + * * `#TN_RC_WCONTEXT` if called from wrong context; + * * `#TN_RC_WPARAM` if wrong params were given: say, `timeout` is either + * `#TN_WAIT_INFINITE` or `0`. + * * If `#TN_CHECK_PARAM` is non-zero, additional return code + * is available: `#TN_RC_INVALID_OBJ`. + */ +enum TN_RCode tn_timer_start(struct TN_Timer *timer, TN_Timeout timeout); + +/** + * The same as `tn_timer_start()` but for using in the ISR. + * + * $(TN_CALL_FROM_ISR) + * $(TN_LEGEND_LINK) + */ +enum TN_RCode tn_timer_istart(struct TN_Timer *timer, TN_Timeout timeout); + +/** + * If timer is active, cancel it. If timer is already inactive, nothing is + * changed. + * + * $(TN_CALL_FROM_TASK) + * $(TN_LEGEND_LINK) + * + * @param timer + * Timer to cancel + * + * @return + * * `#TN_RC_OK` if timer was successfully cancelled; + * * `#TN_RC_WCONTEXT` if called from wrong context; + * * If `#TN_CHECK_PARAM` is non-zero, additional return codes + * are available: `#TN_RC_WPARAM` and `#TN_RC_INVALID_OBJ`. + */ +enum TN_RCode tn_timer_cancel(struct TN_Timer *timer); + +/** + * The same as `tn_timer_cancel()` but for using in the ISR. + * + * $(TN_CALL_FROM_ISR) + * $(TN_LEGEND_LINK) + */ +enum TN_RCode tn_timer_icancel(struct TN_Timer *timer); #ifdef __cplusplus } /* extern "C" */ diff --git a/stuff/doc_pages/mainpage.dox b/stuff/doc_pages/mainpage.dox index c912fcb..44b13fc 100644 --- a/stuff/doc_pages/mainpage.dox +++ b/stuff/doc_pages/mainpage.dox @@ -40,6 +40,7 @@ API reference: - \ref tn_fmem.h "Fixed-memory blocks pool" - \ref tn_eventgrp.h "Event groups" - \ref tn_dqueue.h "Data queues" + - \ref tn_timer.h "Timers" */ diff --git a/stuff/vimwiki/index.wiki b/stuff/vimwiki/index.wiki index 99a9b2b..c1ed646 100644 --- a/stuff/vimwiki/index.wiki +++ b/stuff/vimwiki/index.wiki @@ -7,6 +7,10 @@ == Routine == + * [ ] Timer + * [ ] what if we manage timers from the function called by timer? The tn_timer_list is altered, so, things may be mixed up + * [ ] probably we should add flags like SINGLE_SHOT and CYCLIC. + * [ ] write 'detailed description' in the tn_timer.h == Plans == From 11abcdc5eb10c9ba467003fa17ba152351bcb120 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Fri, 10 Oct 2014 14:39:53 +0400 Subject: [PATCH 11/42] when 'system timer' is mentioned, it is a link now --- src/core/tn_common.h | 45 ++++++++++++++++----------------- src/core/tn_internal.h | 2 +- src/core/tn_timer.h | 5 ++-- stuff/doc_pages/quick_guide.dox | 4 +-- stuff/doxygen/Makefile | 4 +++ 5 files changed, 32 insertions(+), 28 deletions(-) diff --git a/src/core/tn_common.h b/src/core/tn_common.h index d24dfe0..be3908f 100644 --- a/src/core/tn_common.h +++ b/src/core/tn_common.h @@ -173,33 +173,32 @@ typedef void (TN_TaskBody)(void *param); * So, function can wait or return an error. There are possible `timeout` * values and appropriate behavior of the function: * - * * `timeout` is set to `0`: function doesn't wait at all, no context switch is performed, - * `#TN_RC_TIMEOUT` is returned immediately. - * * `timeout` is set to `#TN_WAIT_INFINITE`: function waits until it eventually **can** perform - * its job. Timeout is not taken in account, so `#TN_RC_TIMEOUT` - * is never returned. - * * `timeout` is set to other value: function waits at most specified number of system ticks. - * Strictly speaking, it waits from `(timeout - 1)` to `timeout` ticks. So, if - * you specify that timeout is 1, be aware that it might actually don't - * wait at all: if system timer interrupt happens just while function is - * putting task to wait (with interrupts disabled), then ISR will be - * executed right after function puts task to wait. Then - * `tn_tick_int_processing()` will immediately remove the task from wait - * queue and make it runnable again. + * - `timeout` is set to `0`: function doesn't wait at all, no context switch + * is performed, `#TN_RC_TIMEOUT` is returned immediately. + * - `timeout` is set to `#TN_WAIT_INFINITE`: function waits until it + * eventually **can** perform its job. Timeout is not taken in account, so + * `#TN_RC_TIMEOUT` is never returned. + * - `timeout` is set to other value: function waits at most specified number + * of system ticks. Strictly speaking, it waits from `(timeout - 1)` to + * `timeout` ticks. So, if you specify that timeout is 1, be aware that it + * might actually don't wait at all: if $(TN_SYS_TIMER_LINK) interrupt + * happens just while function is putting task to wait (with interrupts + * disabled), then ISR will be executed right after function puts task to + * wait. Then `tn_tick_int_processing()` will immediately remove the task + * from wait queue and make it runnable again. * - * So, to guarantee that task waits *at least* 1 system tick, - * you should specify timeout value of `2`. + * So, to guarantee that task waits *at least* 1 system tick, you should + * specify timeout value of `2`. * * **Note** also that there are other possible ways to make task runnable: * - * * if task waits because of call to `tn_task_sleep()`, it may be woken up - * by some other task, by means of `tn_task_wakeup()`. In this case, - * `tn_task_sleep()` returns `#TN_RC_OK`. - * * independently of the wait reason, task may be released from wait - * forcibly, by means of `tn_task_release_wait()`. It this case, - * `#TN_RC_FORCED` is returned by the waiting function. - * (the usage of the `tn_task_release_wait()` function is discouraged - * though) + * - if task waits because of call to `tn_task_sleep()`, it may be woken up by + * some other task, by means of `tn_task_wakeup()`. In this case, + * `tn_task_sleep()` returns `#TN_RC_OK`. + * - independently of the wait reason, task may be released from wait forcibly, + * by means of `tn_task_release_wait()`. It this case, `#TN_RC_FORCED` is + * returned by the waiting function. (the usage of the + * `tn_task_release_wait()` function is discouraged though) */ typedef unsigned long TN_Timeout; diff --git a/src/core/tn_internal.h b/src/core/tn_internal.h index 2a49936..cfeb756 100644 --- a/src/core/tn_internal.h +++ b/src/core/tn_internal.h @@ -430,7 +430,7 @@ static inline void _tn_mutex_on_task_wait_complete(struct TN_Task *task) {} ******************************************************************************/ /** - * Should be called from system timer interrupt for each timer + * Should be called from $(TN_SYS_TIMER_LINK) interrupt for each timer * in the timer queue. * * It decrements timer count, and if it reaches zero, timer is cancelled diff --git a/src/core/tn_timer.h b/src/core/tn_timer.h index 47d4bf2..8a2a32d 100644 --- a/src/core/tn_timer.h +++ b/src/core/tn_timer.h @@ -69,7 +69,8 @@ struct TN_Timer; * * When timer fires, function gets called by the kernel. Be aware of the * following: - * - Function is called from ISR context (namely, from *system timer* ISR); + * - Function is called from ISR context (namely, from $(TN_SYS_TIMER_LINK) + * ISR); * - Function is called with global interrupts disabled; * * So, it's legal to call interrupt services from this function, and the @@ -87,7 +88,7 @@ typedef void (TN_TimerFunc)(struct TN_Timer *timer, void *p_user_data); */ struct TN_Timer { /// - /// A list item to be included in the system timer queue + /// A list item to be included in the $(TN_SYS_TIMER_LINK) queue struct TN_ListItem timer_queue; /// /// Function to be called by timer diff --git a/stuff/doc_pages/quick_guide.dox b/stuff/doc_pages/quick_guide.dox index aaca593..a8dcd3b 100644 --- a/stuff/doc_pages/quick_guide.dox +++ b/stuff/doc_pages/quick_guide.dox @@ -42,8 +42,8 @@ tn_soft_isr(_TIMER_5_VECTOR) - disable interrupts globally by calling `tn_arch_int_dis()`; - perform some essential CPU configuration, such as oscillator settings and similar things. - - setup *system timer* interrupt (from which `tn_tick_int_processing()` - gets called) + - setup $(TN_SYS_TIMER_LINK) interrupt (from which + `tn_tick_int_processing()` gets called) - perform any platform-dependent required actions (say, on PIC32 you should enable core software interrupt 0 with the lowest priority) - call `tn_sys_start()` providing all necessary information: pointers to diff --git a/stuff/doxygen/Makefile b/stuff/doxygen/Makefile index b6108bf..9c13547 100644 --- a/stuff/doxygen/Makefile +++ b/stuff/doxygen/Makefile @@ -29,6 +29,8 @@ TN_CAN_SLEEP = \image html attr_timeout.png ${\n} \ TN_LEGEND_LINK = (refer to \ref legend for details) +TN_SYS_TIMER_LINK = \ref time_ticks "system timer" + TN_TASK_STATE_RUNNABLE = \link TN_TASK_STATE_RUNNABLE RUNNABLE\endlink TN_TASK_STATE_WAIT = \link TN_TASK_STATE_WAIT WAIT\endlink @@ -47,6 +49,8 @@ export TN_CAN_SLEEP export TN_LEGEND_LINK +export TN_SYS_TIMER_LINK + export TN_TASK_STATE_RUNNABLE export TN_TASK_STATE_WAIT export TN_TASK_STATE_SUSPEND From af411a54ad70477d01d43f7141130c134d26927b Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Fri, 10 Oct 2014 15:02:11 +0400 Subject: [PATCH 12/42] timers: little todo added --- stuff/vimwiki/index.wiki | 1 + 1 file changed, 1 insertion(+) diff --git a/stuff/vimwiki/index.wiki b/stuff/vimwiki/index.wiki index c1ed646..3da87ce 100644 --- a/stuff/vimwiki/index.wiki +++ b/stuff/vimwiki/index.wiki @@ -8,6 +8,7 @@ == Routine == * [ ] Timer + * [ ] tn_timer_start() should allow re-starting timer (that is, start timer again if it is already active) * [ ] what if we manage timers from the function called by timer? The tn_timer_list is altered, so, things may be mixed up * [ ] probably we should add flags like SINGLE_SHOT and CYCLIC. * [ ] write 'detailed description' in the tn_timer.h From ea2a794e1cde7e8ade9eed798c83435122793c5f Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Fri, 10 Oct 2014 17:39:48 +0400 Subject: [PATCH 13/42] added self-check: when _tn_task_set_waiting() is called, timer for task should not be active --- src/core/tn_tasks.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/core/tn_tasks.c b/src/core/tn_tasks.c index 51fac18..8478a7f 100644 --- a/src/core/tn_tasks.c +++ b/src/core/tn_tasks.c @@ -924,7 +924,10 @@ void _tn_task_set_waiting( _TN_FATAL_ERROR(""); } else if (timeout == 0){ _TN_FATAL_ERROR(""); + } else if (_tn_timer_is_active(&task->timer)){ + _TN_FATAL_ERROR(""); } + #endif task->task_state |= TN_TASK_STATE_WAIT; @@ -947,7 +950,6 @@ void _tn_task_set_waiting( if (timeout != TN_WAIT_INFINITE){ _tn_timer_start(&task->timer, timeout); - //tn_list_add_tail(&tn_wait_timeout_list, &(task->timer_queue)); } } From f3c00c71d8de038b23fdc045338aaabbca3cb21c Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Fri, 10 Oct 2014 19:44:53 +0400 Subject: [PATCH 14/42] added draft of dedicated timer lists, it seems working already, but needs refactor --- src/core/tn_internal.h | 7 ++- src/core/tn_sys.c | 18 ++++++-- src/core/tn_sys.h | 8 ++++ src/core/tn_timer.c | 100 ++++++++++++++++++++++++++++++++++++++--- 4 files changed, 122 insertions(+), 11 deletions(-) diff --git a/src/core/tn_internal.h b/src/core/tn_internal.h index cfeb756..e143f9f 100644 --- a/src/core/tn_internal.h +++ b/src/core/tn_internal.h @@ -90,7 +90,9 @@ extern volatile int tn_created_tasks_cnt; //extern struct TN_ListItem tn_wait_timeout_list; /// list of all active timers, see tn_timer.h -struct TN_ListItem tn_timer_list; +//extern struct TN_ListItem tn_timer_list; +extern struct TN_ListItem tn_timer_list__gen; +extern struct TN_ListItem tn_timer_list__dedicated[ TN_TIMERS_QUE_CNT ]; /// system state flags extern volatile enum TN_StateFlag tn_sys_state; @@ -429,6 +431,7 @@ static inline void _tn_mutex_on_task_wait_complete(struct TN_Task *task) {} * tn_timer.c ******************************************************************************/ +#if 0 /** * Should be called from $(TN_SYS_TIMER_LINK) interrupt for each timer * in the timer queue. @@ -437,6 +440,8 @@ static inline void _tn_mutex_on_task_wait_complete(struct TN_Task *task) {} * and function is called. */ void _tn_timer_tick_proceed(struct TN_Timer *timer); +#endif +void _tn_timers_tick_proceed(void); /** * TODO diff --git a/src/core/tn_sys.c b/src/core/tn_sys.c index 0fc2a1c..33b646b 100644 --- a/src/core/tn_sys.c +++ b/src/core/tn_sys.c @@ -66,8 +66,10 @@ struct TN_ListItem tn_ready_list[TN_PRIORITIES_CNT]; struct TN_ListItem tn_create_queue; volatile int tn_created_tasks_cnt; -//struct TN_ListItem tn_wait_timeout_list;//TODO: remove -struct TN_ListItem tn_timer_list; + +//struct TN_ListItem tn_timer_list; +struct TN_ListItem tn_timer_list__gen; +struct TN_ListItem tn_timer_list__dedicated[ TN_TIMERS_QUE_CNT ]; unsigned short tn_tslice_ticks[TN_PRIORITIES_CNT]; @@ -154,6 +156,7 @@ static inline void _wait_timeout_list_manage(void) } #endif +#if 0 /** * Manage timer queue. */ @@ -166,6 +169,7 @@ static inline void _timer_queue_manage(void) _tn_timer_tick_proceed(timer); } } +#endif /** * Manage round-robin (if used) @@ -278,7 +282,12 @@ void tn_sys_start( //-- reset wait queue //tn_list_reset(&tn_wait_timeout_list); - tn_list_reset(&tn_timer_list); + + //tn_list_reset(&tn_timer_list); + tn_list_reset(&tn_timer_list__gen); + for (i = 0; i < TN_TIMERS_QUE_CNT; i++){ + tn_list_reset(&tn_timer_list__dedicated[i]); + } /* * NOTE: we need to separate creation of tasks and making them runnable, @@ -339,7 +348,8 @@ enum TN_RCode tn_tick_int_processing(void) //-- manage tn_wait_timeout_list //_wait_timeout_list_manage(); - _timer_queue_manage(); + _tn_timers_tick_proceed(); + //_timer_queue_manage(); //-- increment system timer tn_sys_time_count++; diff --git a/src/core/tn_sys.h b/src/core/tn_sys.h index 6195f4c..82d38f1 100644 --- a/src/core/tn_sys.h +++ b/src/core/tn_sys.h @@ -69,6 +69,14 @@ struct TN_Mutex; +/******************************************************************************* + * DEFINITIONS + ******************************************************************************/ + +#define TN_TIMERS_QUE_CNT 4 +#define TN_TIMERS_QUE_MASK 0x03 + + /******************************************************************************* * PUBLIC TYPES ******************************************************************************/ diff --git a/src/core/tn_timer.c b/src/core/tn_timer.c index d8144da..a27477e 100644 --- a/src/core/tn_timer.c +++ b/src/core/tn_timer.c @@ -53,6 +53,20 @@ + +/******************************************************************************* + * DEFINITIONS + ******************************************************************************/ + +/** + * @param timeout should be < TN_TIMERS_QUE_CNT + */ +#define _DEDICATED_LIST_INDEX(timeout) \ + ((tn_sys_time_count + timeout) & TN_TIMERS_QUE_MASK) + + + + /******************************************************************************* * PRIVATE FUNCTIONS ******************************************************************************/ @@ -102,9 +116,6 @@ static inline enum TN_RCode _check_param_create( - - - /******************************************************************************* * PUBLIC FUNCTIONS ******************************************************************************/ @@ -293,6 +304,7 @@ enum TN_RCode tn_timer_icancel(struct TN_Timer *timer) * PROTECTED FUNCTIONS ******************************************************************************/ +#if 0 /** * See comments in the tn_internal.h file */ @@ -315,6 +327,74 @@ void _tn_timer_tick_proceed(struct TN_Timer *timer) timer->func(timer, timer->p_user_data); } } +#endif + +/** + * See comments in the tn_internal.h file + */ +void _tn_timers_tick_proceed(void) +{ + struct TN_Timer *timer; + struct TN_Timer *tmp_timer; + + int dedicated_list_index = _DEDICATED_LIST_INDEX(0); + + if (dedicated_list_index == 0){ + //-- handle generic timer list {{{ + { + tn_list_for_each_entry_safe( + timer, tmp_timer, &tn_timer_list__gen, timer_queue + ) + { +#if TN_DEBUG + if (timer->timeout_cur == TN_WAIT_INFINITE || timer->timeout_cur < TN_TIMERS_QUE_CNT){ + //-- should never be here + _TN_FATAL_ERROR(); + } +#endif + timer->timeout_cur -= TN_TIMERS_QUE_CNT; + + if (timer->timeout_cur < TN_TIMERS_QUE_CNT){ + //-- it's time to move this timer to the dedicated list + tn_list_remove_entry(&(timer->timer_queue)); + tn_list_add_tail( + &tn_timer_list__dedicated[ _DEDICATED_LIST_INDEX(timer->timeout_cur) ], + &(timer->timer_queue) + ); + } + } + } + //}}} + } + + //-- handle current dedicated timer list {{{ + { + struct TN_ListItem *p_cur_timer_list = + &tn_timer_list__dedicated[ dedicated_list_index ]; + + { + tn_list_for_each_entry_safe( + timer, tmp_timer, p_cur_timer_list, timer_queue + ) + { + //-- first of all, cancel timer, so that + // function could start it again if it wants to. + _tn_timer_cancel(timer); + + //-- call user function + timer->func(timer, timer->p_user_data); + } + } + +#if TN_DEBUG + //-- current dedicated list should be empty now + if (!tn_is_list_empty(p_cur_timer_list)){ + _TN_FATAL_ERROR(""); + } +#endif + } + // }}} +} enum TN_RCode _tn_timer_start(struct TN_Timer *timer, TN_Timeout timeout) { @@ -323,9 +403,17 @@ enum TN_RCode _tn_timer_start(struct TN_Timer *timer, TN_Timeout timeout) if (timeout == TN_WAIT_INFINITE || timeout == 0){ rc = TN_RC_WPARAM; } else { - timer->timeout_cur = timeout; - - tn_list_add_tail(&tn_timer_list, &(timer->timer_queue)); + timer->timeout_cur = timeout + _DEDICATED_LIST_INDEX(0); + //tn_list_add_tail(&tn_timer_list, &(timer->timer_queue)); + + if (timeout < TN_TIMERS_QUE_CNT){ + tn_list_add_tail( + &tn_timer_list__dedicated[ _DEDICATED_LIST_INDEX(timeout) ], + &(timer->timer_queue) + ); + } else { + tn_list_add_tail(&tn_timer_list__gen, &(timer->timer_queue)); + } } return rc; From 8602c34038f854d0a2c236305f5868ae89b0c46d Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Fri, 10 Oct 2014 21:32:02 +0400 Subject: [PATCH 15/42] working on cleaning timers up --- src/core/tn_internal.h | 12 ++--- src/core/tn_sys.c | 55 ++--------------------- src/core/tn_sys.h | 3 -- src/core/tn_timer.c | 95 +++++++++++++++++++++------------------- src/core/tn_timer.h | 73 ++++++++++++++++++++++++++++++ src/tn_cfg_default.h | 19 ++++++++ stuff/vimwiki/index.wiki | 1 + 7 files changed, 149 insertions(+), 109 deletions(-) diff --git a/src/core/tn_internal.h b/src/core/tn_internal.h index e143f9f..e141a08 100644 --- a/src/core/tn_internal.h +++ b/src/core/tn_internal.h @@ -92,7 +92,7 @@ extern volatile int tn_created_tasks_cnt; /// list of all active timers, see tn_timer.h //extern struct TN_ListItem tn_timer_list; extern struct TN_ListItem tn_timer_list__gen; -extern struct TN_ListItem tn_timer_list__dedicated[ TN_TIMERS_QUE_CNT ]; +extern struct TN_ListItem tn_timer_list__tick[ TN_TICK_LISTS_CNT ]; /// system state flags extern volatile enum TN_StateFlag tn_sys_state; @@ -431,16 +431,10 @@ static inline void _tn_mutex_on_task_wait_complete(struct TN_Task *task) {} * tn_timer.c ******************************************************************************/ -#if 0 /** - * Should be called from $(TN_SYS_TIMER_LINK) interrupt for each timer - * in the timer queue. - * - * It decrements timer count, and if it reaches zero, timer is cancelled - * and function is called. + * Should be called from $(TN_SYS_TIMER_LINK) interrupt. It performs all + * necessary timers housekeeping: moving them between lists, firing them, etc. */ -void _tn_timer_tick_proceed(struct TN_Timer *timer); -#endif void _tn_timers_tick_proceed(void); /** diff --git a/src/core/tn_sys.c b/src/core/tn_sys.c index 33b646b..6ef4071 100644 --- a/src/core/tn_sys.c +++ b/src/core/tn_sys.c @@ -69,7 +69,7 @@ volatile int tn_created_tasks_cnt; //struct TN_ListItem tn_timer_list; struct TN_ListItem tn_timer_list__gen; -struct TN_ListItem tn_timer_list__dedicated[ TN_TIMERS_QUE_CNT ]; +struct TN_ListItem tn_timer_list__tick[ TN_TICK_LISTS_CNT ]; unsigned short tn_tslice_ticks[TN_PRIORITIES_CNT]; @@ -127,50 +127,6 @@ static void _idle_task_body(void *par) } } -#if 0 -/** - * Manage tn_wait_timeout_list. - * This job was previously done in tn_timer_task, but now it is preferred - * to call it right from tn_tick_int_processing() - */ -static inline void _wait_timeout_list_manage(void) -{ - volatile struct TN_Task *task; - - tn_list_for_each_entry(task, &tn_wait_timeout_list, timer_queue){ - - if (task->tick_count == TN_WAIT_INFINITE){ - //-- should never be here - _TN_FATAL_ERROR(); - } - - if (task->tick_count > 0) { - task->tick_count--; - - if (task->tick_count == 0){ - //-- Timeout expired - _tn_task_wait_complete((struct TN_Task *)task, TN_RC_TIMEOUT); - } - } - } -} -#endif - -#if 0 -/** - * Manage timer queue. - */ -static inline void _timer_queue_manage(void) -{ - struct TN_Timer *timer; - struct TN_Timer *tmp_timer; - - tn_list_for_each_entry_safe(timer, tmp_timer, &tn_timer_list, timer_queue){ - _tn_timer_tick_proceed(timer); - } -} -#endif - /** * Manage round-robin (if used) */ @@ -285,8 +241,8 @@ void tn_sys_start( //tn_list_reset(&tn_timer_list); tn_list_reset(&tn_timer_list__gen); - for (i = 0; i < TN_TIMERS_QUE_CNT; i++){ - tn_list_reset(&tn_timer_list__dedicated[i]); + for (i = 0; i < TN_TICK_LISTS_CNT; i++){ + tn_list_reset(&tn_timer_list__tick[i]); } /* @@ -345,11 +301,8 @@ enum TN_RCode tn_tick_int_processing(void) //-- manage round-robin (if used) _round_robin_manage(); - //-- manage tn_wait_timeout_list - //_wait_timeout_list_manage(); - + //-- manage timers _tn_timers_tick_proceed(); - //_timer_queue_manage(); //-- increment system timer tn_sys_time_count++; diff --git a/src/core/tn_sys.h b/src/core/tn_sys.h index 82d38f1..f4d753a 100644 --- a/src/core/tn_sys.h +++ b/src/core/tn_sys.h @@ -73,9 +73,6 @@ struct TN_Mutex; * DEFINITIONS ******************************************************************************/ -#define TN_TIMERS_QUE_CNT 4 -#define TN_TIMERS_QUE_MASK 0x03 - /******************************************************************************* * PUBLIC TYPES diff --git a/src/core/tn_timer.c b/src/core/tn_timer.c index a27477e..71ec75b 100644 --- a/src/core/tn_timer.c +++ b/src/core/tn_timer.c @@ -34,7 +34,6 @@ * ******************************************************************************/ - /******************************************************************************* * INCLUDED FILES ******************************************************************************/ @@ -58,11 +57,22 @@ * DEFINITIONS ******************************************************************************/ +#define TN_TICK_LISTS_MASK (TN_TICK_LISTS_CNT - 1) + +//-- configuration check +#if ((TN_TICK_LISTS_MASK & TN_TICK_LISTS_CNT) != 0) +# error TN_TICK_LISTS_CNT must be a power of two +#endif + +#if (TN_TICK_LISTS_CNT < 2) +# error TN_TICK_LISTS_CNT must be >= 2 +#endif + /** - * @param timeout should be < TN_TIMERS_QUE_CNT + * @param timeout should be < TN_TICK_LISTS_CNT */ -#define _DEDICATED_LIST_INDEX(timeout) \ - ((tn_sys_time_count + timeout) & TN_TIMERS_QUE_MASK) +#define _TICK_LIST_INDEX(timeout) \ + ((tn_sys_time_count + timeout) & TN_TICK_LISTS_MASK) @@ -304,61 +314,48 @@ enum TN_RCode tn_timer_icancel(struct TN_Timer *timer) * PROTECTED FUNCTIONS ******************************************************************************/ -#if 0 -/** - * See comments in the tn_internal.h file - */ -void _tn_timer_tick_proceed(struct TN_Timer *timer) -{ - if (timer->timeout_cur == TN_WAIT_INFINITE || timer->timeout_cur == 0){ - //-- should never be here - _TN_FATAL_ERROR(); - } - - timer->timeout_cur--; - - if (timer->timeout_cur == 0){ - //-- it's time to fire. - //-- first of all, cancel timer, so that - // function could start it again if it wants to. - _tn_timer_cancel(timer); - - //-- call user function - timer->func(timer, timer->p_user_data); - } -} -#endif - /** - * See comments in the tn_internal.h file + * See comments in the tn_internal.h file, + * see also \ref timers_implementation. */ void _tn_timers_tick_proceed(void) { struct TN_Timer *timer; struct TN_Timer *tmp_timer; - int dedicated_list_index = _DEDICATED_LIST_INDEX(0); + int tick_list_index = _TICK_LIST_INDEX(0); + + if (tick_list_index == 0){ + //-- it happens each TN_TICK_LISTS_CNT-th system tick: + // now we should walk through all the timers in the "generic" timer + // list, decrement the timeout of each one by TN_TICK_LISTS_CNT, + // and if resulting timeout is less than TN_TICK_LISTS_CNT, + // then move that timer to the appropriate "tick" timer list. - if (dedicated_list_index == 0){ - //-- handle generic timer list {{{ + //-- handle "generic" timer list {{{ { tn_list_for_each_entry_safe( timer, tmp_timer, &tn_timer_list__gen, timer_queue ) { #if TN_DEBUG - if (timer->timeout_cur == TN_WAIT_INFINITE || timer->timeout_cur < TN_TIMERS_QUE_CNT){ - //-- should never be here + if ( timer->timeout_cur == TN_WAIT_INFINITE + || timer->timeout_cur < TN_TICK_LISTS_CNT + ) + { + //-- should never be here: timeout value should always + // be >= TN_TICK_LISTS_CNT here. + // And it should never be TN_WAIT_INFINITE. _TN_FATAL_ERROR(); } #endif - timer->timeout_cur -= TN_TIMERS_QUE_CNT; + timer->timeout_cur -= TN_TICK_LISTS_CNT; - if (timer->timeout_cur < TN_TIMERS_QUE_CNT){ - //-- it's time to move this timer to the dedicated list + if (timer->timeout_cur < TN_TICK_LISTS_CNT){ + //-- it's time to move this timer to the "tick" list tn_list_remove_entry(&(timer->timer_queue)); tn_list_add_tail( - &tn_timer_list__dedicated[ _DEDICATED_LIST_INDEX(timer->timeout_cur) ], + &tn_timer_list__tick[_TICK_LIST_INDEX(timer->timeout_cur)], &(timer->timer_queue) ); } @@ -367,10 +364,17 @@ void _tn_timers_tick_proceed(void) //}}} } - //-- handle current dedicated timer list {{{ + //-- it happens every system tick: + // we should walk through all the timers in the current "tick" timer list, + // and fire them all, unconditionally. + + //-- handle current "tick" timer list {{{ { struct TN_ListItem *p_cur_timer_list = - &tn_timer_list__dedicated[ dedicated_list_index ]; + &tn_timer_list__tick[ tick_list_index ]; + + //-- now, p_cur_timer_list is a list of timers that we should + // fire NOW, unconditionally. { tn_list_for_each_entry_safe( @@ -387,7 +391,7 @@ void _tn_timers_tick_proceed(void) } #if TN_DEBUG - //-- current dedicated list should be empty now + //-- current "tick" list should be empty now if (!tn_is_list_empty(p_cur_timer_list)){ _TN_FATAL_ERROR(""); } @@ -403,12 +407,11 @@ enum TN_RCode _tn_timer_start(struct TN_Timer *timer, TN_Timeout timeout) if (timeout == TN_WAIT_INFINITE || timeout == 0){ rc = TN_RC_WPARAM; } else { - timer->timeout_cur = timeout + _DEDICATED_LIST_INDEX(0); - //tn_list_add_tail(&tn_timer_list, &(timer->timer_queue)); + timer->timeout_cur = timeout + _TICK_LIST_INDEX(0); - if (timeout < TN_TIMERS_QUE_CNT){ + if (timeout < TN_TICK_LISTS_CNT){ tn_list_add_tail( - &tn_timer_list__dedicated[ _DEDICATED_LIST_INDEX(timeout) ], + &tn_timer_list__tick[ _TICK_LIST_INDEX(timeout) ], &(timer->timer_queue) ); } else { diff --git a/src/core/tn_timer.h b/src/core/tn_timer.h index 8a2a32d..13c2757 100644 --- a/src/core/tn_timer.h +++ b/src/core/tn_timer.h @@ -39,8 +39,81 @@ * * A timer * + * \section timers_implementation Implementation of timers + * + * You don't have to understand the implementation of timers to use them, + * but it is probably worth knowing if you want to understand better how the + * kernel works and configure it appropriately for your particular application. + * + * The easiest implementation of timers could be something like this: we + * have just a single list with all active timers, and at every system tick + * we should walk through all the timers in this list, and do the following + * with each timer: + * + * - Decrement timeout by 1 + * - If new timeout is 0, then remove that timer from the list (i.e. make timer + * inactive), and fire the appropriate timer function. + * + * This approach has drawbacks: + * + * - We can't manage timers from the function called by timer. If we do so + * (say, if we start new timer, or cancel one of the active timers), then the + * timer list gets modified. But we are currently iterating through this + * list, so, it's quite easy to mix things up. + * - It is inefficient on rather large amount of timers and/or with large + * timeouts: we should iterate through all of them each system tick. + * + * The latter is probably not so critical in the embedded world since large + * amount of timers is unlikely there; whereas the the former is actually + * notable. + * + * So, different approach was applied. The main idea is taken from the mainline + * Linux kernel source, but the implementation was simplified much because (1) + * embedded systems have much less resources, and (2) the kernel doesn't need + * to scale as well as Linux does. You can read about Linux timers + * implementation in the book "Linux Device Drivers", 3rd edition: + * + * - Time, Delays, and Deferred Work + * - Kernel Timers + * - The Implementation of Kernel Timers + * + * This book is freely available at http://lwn.net/Kernel/LDD3/ . + * + * So, TNeoKernel's implementation: + * + * We have configurable value `N` that is a power of two, typical values are + * `4`, `8` or `16`. + * + * If the timer expires in the next `1` to `(N - 1)` system ticks, it is added + * to one of the `N` lists (the so-called "tick" lists) devoted to short-range + * timers using the least significant bits of the `timeout` value. If it + * expires farther in the future, it is added to the "generic" list. + * + * Each `N`-th system tick, all the timers from "generic" list are walked + * through, and the following is performed with each timer: + * + * - `timeout` value decremented by `N` + * - if resulting `timeout` is less than `N`, timer is moved to the appropriate + * "tick" list. + * + * At *every* system tick, all the timers from current "tick" list are fired + * unconditionally. This is an efficient and nice solution. + * + * The attentive reader may want to ask why do we use `(N - 1)` "tick" lists if + * we actually have `N` lists. That's because, again, we want to be able to + * modify timers from the timer function. If we use `N` lists, and user wants + * to add new timer with `timeout` equal to `N`, then new timer will be added + * to the same list which is iterated through at the moment, and things will be + * mixed up. + * + * If we use `(N - 1)` lists, we are guaranteed that current "tick" list will + * stay untouched while we are iterating through it. + * + * The `N` in the TNeoKernel is configured by the compile-time option + * `#TN_TICK_LISTS_CNT`. */ + #ifndef _TN_TIMER_H #define _TN_TIMER_H diff --git a/src/tn_cfg_default.h b/src/tn_cfg_default.h index 57a5e36..7e9ed5a 100644 --- a/src/tn_cfg_default.h +++ b/src/tn_cfg_default.h @@ -133,6 +133,25 @@ # define TN_MUTEX_DEADLOCK_DETECT 1 #endif +/** + * Number of "tick" lists of timers, must be a power or two; minimum value: + * `2`; typical values: `4`, `8` or `16`. + * + * Refer to the \ref timers_implementation for details. + * + * Shortly: this value represents number of elements in the array of + * `struct TN_ListItem`, on 32-bit system each element takes 8 bytes. + * + * The larger value, the more memory is needed, and the faster + * $(TN_SYS_TIMER_LINK) ISR works. If your application has a lot of timers + * and/or sleeping tasks, consider incrementing this value; otherwise, + * default value should work for you. + */ +#ifndef TN_TICK_LISTS_CNT +# define TN_TICK_LISTS_CNT 4 +#endif + + /** * API option for `MAKE_ALIG()` macro. * diff --git a/stuff/vimwiki/index.wiki b/stuff/vimwiki/index.wiki index 3da87ce..953725f 100644 --- a/stuff/vimwiki/index.wiki +++ b/stuff/vimwiki/index.wiki @@ -12,6 +12,7 @@ * [ ] what if we manage timers from the function called by timer? The tn_timer_list is altered, so, things may be mixed up * [ ] probably we should add flags like SINGLE_SHOT and CYCLIC. * [ ] write 'detailed description' in the tn_timer.h + * [ ] probably move definition of tn_timer_list__gen and tn_timer_list__tick to the `tn_timer.c`, add `_tn_timers_init()`. So that all the timer-related stuff is in the single file. == Plans == From 2c835253f1eef40fda84ebb29bea863e3a83394f Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Sat, 11 Oct 2014 02:32:34 +0400 Subject: [PATCH 16/42] removed tn_sys_time_set(), because system tick counter is now used by the kernel for timers --- src/core/tn_internal.h | 6 +----- src/core/tn_sys.c | 19 ------------------- src/core/tn_sys.h | 21 +-------------------- stuff/doc_pages/changelog.dox | 6 +++++- 4 files changed, 7 insertions(+), 45 deletions(-) diff --git a/src/core/tn_internal.h b/src/core/tn_internal.h index e141a08..9295b38 100644 --- a/src/core/tn_internal.h +++ b/src/core/tn_internal.h @@ -108,12 +108,8 @@ extern struct TN_Task * tn_next_task_to_run; /// since this priority is used by idle task and it is always runnable. extern volatile unsigned int tn_ready_to_run_bmp; -/// system time that is get/set by `tn_sys_time_get()`/`tn_sys_time_set()`, +/// system time that is get/set by `tn_sys_time_get()`, /// respectively. -/// -/// NOTE that these and only these TNeoKernel functions use this counter, -/// it is not used for internal timeout calculation, or anything. -/// Its usage completely depends on user. extern volatile unsigned int tn_sys_time_count; /// current interrupt nesting count. Used by macros diff --git a/src/core/tn_sys.c b/src/core/tn_sys.c index 6ef4071..be30f98 100644 --- a/src/core/tn_sys.c +++ b/src/core/tn_sys.c @@ -352,25 +352,6 @@ unsigned int tn_sys_time_get(void) return tn_sys_time_count; } -/* - * See comments in the header file (tn_sys.h) - */ -void tn_sys_time_set(unsigned int value) -{ - if (_tn_arch_inside_isr()){ - TN_INTSAVE_DATA_INT; - TN_INT_IDIS_SAVE(); - tn_sys_time_count = value; - TN_INT_IRESTORE(); - } else { - TN_INTSAVE_DATA; - TN_INT_DIS_SAVE(); - tn_sys_time_count = value; - TN_INT_RESTORE(); - } - -} - /* * Returns current state flags (tn_sys_state) */ diff --git a/src/core/tn_sys.h b/src/core/tn_sys.h index f4d753a..e5da7d5 100644 --- a/src/core/tn_sys.h +++ b/src/core/tn_sys.h @@ -290,29 +290,10 @@ enum TN_RCode tn_sys_tslice_set(int priority, int ticks); * $(TN_LEGEND_LINK) * * @return - * Current system ticks count. Note that this value does **not** affect any - * of the internal TNeoKernel routines, it is just incremented each system - * tick (i.e. in `tn_tick_int_processing()`) and is returned to user by - * `tn_sys_time_get()`. - * - * It is not used by TNeoKernel itself at all. + * Current system ticks count. */ unsigned int tn_sys_time_get(void); -/** - * Set system ticks count. Note that this value does **not** affect any of the - * internal TNeoKernel routines, it is just incremented each system tick (i.e. - * in `tn_tick_int_processing()`) and is returned to user by - * `tn_sys_time_get()`. - * - * It is not used by TNeoKernel itself at all. - * - * $(TN_CALL_FROM_TASK) - * $(TN_CALL_FROM_ISR) - * $(TN_LEGEND_LINK) - */ -void tn_sys_time_set(unsigned int value); - /** * Set callback function that should be called whenever deadlock occurs or diff --git a/stuff/doc_pages/changelog.dox b/stuff/doc_pages/changelog.dox index 025041e..02aec3c 100644 --- a/stuff/doc_pages/changelog.dox +++ b/stuff/doc_pages/changelog.dox @@ -7,7 +7,11 @@ TNeoKernel changelog \section changelog_current Current development version (BETA) - - *No changes yet* + - Working on \ref tn_timer.h "timers" + - Removed `tn_sys_time_set()` function, because now TNeoKernel uses internal + system tick count for timers, and modifying system tick counter by user + is *really* bad idea. + \section changelog_v1_01 v1.01 From 817a9192928e5cc5f63cad771777e8c79ec9b1cb Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Sat, 11 Oct 2014 13:46:21 +0400 Subject: [PATCH 17/42] timers: tn_timer_start() is now able to re-start timer, added some self-check --- src/core/tn_timer.c | 34 +++++++++++++++++++++++++++++++++- stuff/vimwiki/index.wiki | 6 ++++-- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/core/tn_timer.c b/src/core/tn_timer.c index 71ec75b..ae1745c 100644 --- a/src/core/tn_timer.c +++ b/src/core/tn_timer.c @@ -320,6 +320,13 @@ enum TN_RCode tn_timer_icancel(struct TN_Timer *timer) */ void _tn_timers_tick_proceed(void) { +#if TN_DEBUG + //-- interrupts should be disabled here + if (!TN_IS_INT_DISABLED()){ + _TN_FATAL_ERROR(""); + } +#endif + struct TN_Timer *timer; struct TN_Timer *tmp_timer; @@ -404,17 +411,35 @@ enum TN_RCode _tn_timer_start(struct TN_Timer *timer, TN_Timeout timeout) { enum TN_RCode rc = TN_RC_OK; +#if TN_DEBUG + //-- interrupts should be disabled here + if (!TN_IS_INT_DISABLED()){ + _TN_FATAL_ERROR(""); + } +#endif + if (timeout == TN_WAIT_INFINITE || timeout == 0){ rc = TN_RC_WPARAM; } else { - timer->timeout_cur = timeout + _TICK_LIST_INDEX(0); + + //-- if timer is active, cancel it first + if (_tn_timer_is_active(timer)){ + rc = _tn_timer_cancel(timer); + } if (timeout < TN_TICK_LISTS_CNT){ + //-- timer should be added to the one of "tick" lists. + // Note that we shouldn't set timeout_cur here, + // because it has no effect when timer is in "tick" list. tn_list_add_tail( &tn_timer_list__tick[ _TICK_LIST_INDEX(timeout) ], &(timer->timer_queue) ); } else { + //-- timer should be added to the "generic" list. + // We should set timeout_cur adding current "tick" index to it. + timer->timeout_cur = timeout + _TICK_LIST_INDEX(0); + tn_list_add_tail(&tn_timer_list__gen, &(timer->timer_queue)); } } @@ -426,6 +451,13 @@ enum TN_RCode _tn_timer_cancel(struct TN_Timer *timer) { enum TN_RCode rc = TN_RC_OK; +#if TN_DEBUG + //-- interrupts should be disabled here + if (!TN_IS_INT_DISABLED()){ + _TN_FATAL_ERROR(""); + } +#endif + //-- reset timeout to zero timer->timeout_cur = 0; diff --git a/stuff/vimwiki/index.wiki b/stuff/vimwiki/index.wiki index 953725f..cd01f11 100644 --- a/stuff/vimwiki/index.wiki +++ b/stuff/vimwiki/index.wiki @@ -7,8 +7,8 @@ == Routine == - * [ ] Timer - * [ ] tn_timer_start() should allow re-starting timer (that is, start timer again if it is already active) + * [.] Timer + * [X] tn_timer_start() should allow re-starting timer (that is, start timer again if it is already active) * [ ] what if we manage timers from the function called by timer? The tn_timer_list is altered, so, things may be mixed up * [ ] probably we should add flags like SINGLE_SHOT and CYCLIC. * [ ] write 'detailed description' in the tn_timer.h @@ -19,6 +19,8 @@ * [ ] Timer object * [ ] Event group connection (at least, it would be useful to connect event group to queue) * [ ] Add a destroy callback for each task, this callback (if not NULL) should be called whenever task is terminated with `tn_task_exit()` or `tn_task_terminate()`. + * [ ] Port to ARM + * [ ] Probably port to PIC24/dsPIC From fa0f69130a6804a6c472defd4a944830908b4f23 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Sat, 11 Oct 2014 13:56:07 +0400 Subject: [PATCH 18/42] timer-related data definition is moved to tn_timers.c --- src/core/tn_internal.h | 22 +++++++++++++--------- src/core/tn_sys.c | 14 ++------------ src/core/tn_timer.c | 22 ++++++++++++++++++++++ 3 files changed, 37 insertions(+), 21 deletions(-) diff --git a/src/core/tn_internal.h b/src/core/tn_internal.h index 9295b38..160ac9f 100644 --- a/src/core/tn_internal.h +++ b/src/core/tn_internal.h @@ -85,15 +85,6 @@ extern struct TN_ListItem tn_create_queue; /// count of created tasks extern volatile int tn_created_tasks_cnt; -/// list of all tasks that wait timeout expiration -/// TODO: remove -//extern struct TN_ListItem tn_wait_timeout_list; - -/// list of all active timers, see tn_timer.h -//extern struct TN_ListItem tn_timer_list; -extern struct TN_ListItem tn_timer_list__gen; -extern struct TN_ListItem tn_timer_list__tick[ TN_TICK_LISTS_CNT ]; - /// system state flags extern volatile enum TN_StateFlag tn_sys_state; @@ -427,12 +418,25 @@ static inline void _tn_mutex_on_task_wait_complete(struct TN_Task *task) {} * tn_timer.c ******************************************************************************/ +/// "generic" list of timers, for details, refer to \ref timers_implementation +extern struct TN_ListItem tn_timer_list__gen; +/// "tick" lists of timers, for details, refer to \ref timers_implementation +extern struct TN_ListItem tn_timer_list__tick[ TN_TICK_LISTS_CNT ]; + + + + /** * Should be called from $(TN_SYS_TIMER_LINK) interrupt. It performs all * necessary timers housekeeping: moving them between lists, firing them, etc. */ void _tn_timers_tick_proceed(void); +/** + * TODO + */ +void _tn_timers_init(void); + /** * TODO */ diff --git a/src/core/tn_sys.c b/src/core/tn_sys.c index be30f98..26d1efd 100644 --- a/src/core/tn_sys.c +++ b/src/core/tn_sys.c @@ -67,10 +67,6 @@ struct TN_ListItem tn_ready_list[TN_PRIORITIES_CNT]; struct TN_ListItem tn_create_queue; volatile int tn_created_tasks_cnt; -//struct TN_ListItem tn_timer_list; -struct TN_ListItem tn_timer_list__gen; -struct TN_ListItem tn_timer_list__tick[ TN_TICK_LISTS_CNT ]; - unsigned short tn_tslice_ticks[TN_PRIORITIES_CNT]; volatile enum TN_StateFlag tn_sys_state; @@ -236,14 +232,8 @@ void tn_sys_start( //-- Pre-decrement stack tn_int_sp = &(int_stack[int_stack_size]); - //-- reset wait queue - //tn_list_reset(&tn_wait_timeout_list); - - //tn_list_reset(&tn_timer_list); - tn_list_reset(&tn_timer_list__gen); - for (i = 0; i < TN_TICK_LISTS_CNT; i++){ - tn_list_reset(&tn_timer_list__tick[i]); - } + //-- init timers + _tn_timers_init(); /* * NOTE: we need to separate creation of tasks and making them runnable, diff --git a/src/core/tn_timer.c b/src/core/tn_timer.c index ae1745c..883cfd4 100644 --- a/src/core/tn_timer.c +++ b/src/core/tn_timer.c @@ -53,6 +53,15 @@ +/******************************************************************************* + * PUBLIC DATA + ******************************************************************************/ + +struct TN_ListItem tn_timer_list__gen; +struct TN_ListItem tn_timer_list__tick[ TN_TICK_LISTS_CNT ]; + + + /******************************************************************************* * DEFINITIONS ******************************************************************************/ @@ -494,5 +503,18 @@ BOOL _tn_timer_is_active(struct TN_Timer *timer) return (timer->timeout_cur != 0); } +void _tn_timers_init(void) +{ + int i; + + //-- reset "generic" timers list + tn_list_reset(&tn_timer_list__gen); + + //-- reset all "tick" timer lists + for (i = 0; i < TN_TICK_LISTS_CNT; i++){ + tn_list_reset(&tn_timer_list__tick[i]); + } +} + From 5592cd6e0a3862f4849ce03b6e428859f9a0e8a9 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Sat, 11 Oct 2014 16:44:00 +0400 Subject: [PATCH 19/42] [FIX] in the tn_tick_int_processing(), tn_sys_time_count should be incremented before managing timers --- src/core/tn_sys.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/tn_sys.c b/src/core/tn_sys.c index 26d1efd..fe3d06c 100644 --- a/src/core/tn_sys.c +++ b/src/core/tn_sys.c @@ -288,15 +288,15 @@ enum TN_RCode tn_tick_int_processing(void) TN_INT_IDIS_SAVE(); + //-- increment system timer + tn_sys_time_count++; + //-- manage round-robin (if used) _round_robin_manage(); //-- manage timers _tn_timers_tick_proceed(); - //-- increment system timer - tn_sys_time_count++; - TN_INT_IRESTORE(); out: From bacb1bbc633dc7fe3bb8314c224f30cf99c0b83b Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Sat, 11 Oct 2014 18:30:23 +0400 Subject: [PATCH 20/42] [FIX] timers: now, timer-called function can cancel other timer with the same timeout (i.e. in the same 'tick' list) --- src/core/tn_timer.c | 46 ++++++++++++++++++++++++++++----------------- src/core/tn_timer.h | 5 +++-- 2 files changed, 32 insertions(+), 19 deletions(-) diff --git a/src/core/tn_timer.c b/src/core/tn_timer.c index 883cfd4..0fda1e8 100644 --- a/src/core/tn_timer.c +++ b/src/core/tn_timer.c @@ -336,9 +336,6 @@ void _tn_timers_tick_proceed(void) } #endif - struct TN_Timer *timer; - struct TN_Timer *tmp_timer; - int tick_list_index = _TICK_LIST_INDEX(0); if (tick_list_index == 0){ @@ -350,6 +347,9 @@ void _tn_timers_tick_proceed(void) //-- handle "generic" timer list {{{ { + struct TN_Timer *timer; + struct TN_Timer *tmp_timer; + tn_list_for_each_entry_safe( timer, tmp_timer, &tn_timer_list__gen, timer_queue ) @@ -386,24 +386,36 @@ void _tn_timers_tick_proceed(void) //-- handle current "tick" timer list {{{ { + struct TN_Timer *timer; + struct TN_ListItem *p_cur_timer_list = &tn_timer_list__tick[ tick_list_index ]; //-- now, p_cur_timer_list is a list of timers that we should // fire NOW, unconditionally. - { - tn_list_for_each_entry_safe( - timer, tmp_timer, p_cur_timer_list, timer_queue - ) - { - //-- first of all, cancel timer, so that - // function could start it again if it wants to. - _tn_timer_cancel(timer); + //-- NOTE that we shouldn't use iterators like + // `tn_list_for_each_entry_safe()` here, because timers can be + // removed from the list while we are iterating through it: + // this may happen if user-provided function cancels timer which + // is in the same list. + // + // Although timers could be removed from the list, note that + // new timer can't be added to it + // (because timeout 0 is disallowed, and timer with timeout + // TN_TICK_LISTS_CNT is added to the "generic" list), + // see implementation details in the tn_timer.h file + while (!tn_is_list_empty(p_cur_timer_list)){ + timer = tn_list_first_entry_remove( + p_cur_timer_list, typeof(*timer), timer_queue + ); - //-- call user function - timer->func(timer, timer->p_user_data); - } + //-- first of all, cancel timer, so that + // function could start it again if it wants to. + _tn_timer_cancel(timer); + + //-- call user function + timer->func(timer, timer->p_user_data); } #if TN_DEBUG @@ -467,13 +479,13 @@ enum TN_RCode _tn_timer_cancel(struct TN_Timer *timer) } #endif - //-- reset timeout to zero + //-- reset timeout to zero (but this is actually not necessary) timer->timeout_cur = 0; //-- remove entry from timer queue tn_list_remove_entry(&(timer->timer_queue)); - //-- reset the list (but this is actually not necessary) + //-- reset the list tn_list_reset(&(timer->timer_queue)); return rc; @@ -500,7 +512,7 @@ enum TN_RCode _tn_timer_create( BOOL _tn_timer_is_active(struct TN_Timer *timer) { - return (timer->timeout_cur != 0); + return (!tn_is_list_empty(&(timer->timer_queue))); } void _tn_timers_init(void) diff --git a/src/core/tn_timer.h b/src/core/tn_timer.h index 13c2757..0640d46 100644 --- a/src/core/tn_timer.h +++ b/src/core/tn_timer.h @@ -106,8 +106,9 @@ * to the same list which is iterated through at the moment, and things will be * mixed up. * - * If we use `(N - 1)` lists, we are guaranteed that current "tick" list will - * stay untouched while we are iterating through it. + * If we use `(N - 1)` lists, we are guaranteed that new timers can't be added + * to the current "tick" list while we are iterating through it. + * (although timer can be deleted from that list, but it's ok) * * The `N` in the TNeoKernel is configured by the compile-time option * `#TN_TICK_LISTS_CNT`. From 81b87f1abbdc5a456d7e06b729a2a911ffd20802 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Sat, 11 Oct 2014 18:36:59 +0400 Subject: [PATCH 21/42] _tn_timer_cancel() now checks if the timer is active --- src/core/tn_tasks.c | 4 +--- src/core/tn_timer.c | 30 ++++++++++++------------------ 2 files changed, 13 insertions(+), 21 deletions(-) diff --git a/src/core/tn_tasks.c b/src/core/tn_tasks.c index 8478a7f..a23a568 100644 --- a/src/core/tn_tasks.c +++ b/src/core/tn_tasks.c @@ -996,9 +996,7 @@ void _tn_task_clear_waiting(struct TN_Task *task, enum TN_RCode wait_rc) //-- if timer is active (i.e. task waits for timeout), // cancel that timer - if (_tn_timer_is_active(&task->timer)){ - _tn_timer_cancel(&task->timer); - } + _tn_timer_cancel(&task->timer); //-- remove WAIT state task->task_state &= ~TN_TASK_STATE_WAIT; diff --git a/src/core/tn_timer.c b/src/core/tn_timer.c index 0fda1e8..483c1cc 100644 --- a/src/core/tn_timer.c +++ b/src/core/tn_timer.c @@ -187,9 +187,7 @@ enum TN_RCode tn_timer_delete(struct TN_Timer *timer) TN_INT_DIS_SAVE(); //-- if timer is active, cancel it first - if (_tn_timer_is_active(timer)){ - rc = _tn_timer_cancel(timer); - } + rc = _tn_timer_cancel(timer); //-- now, delete timer timer->id_timer = 0; @@ -276,9 +274,7 @@ enum TN_RCode tn_timer_cancel(struct TN_Timer *timer) TN_INT_DIS_SAVE(); - if (_tn_timer_is_active(timer)){ - rc = _tn_timer_cancel(timer); - } + rc = _tn_timer_cancel(timer); TN_INT_RESTORE(); @@ -306,9 +302,7 @@ enum TN_RCode tn_timer_icancel(struct TN_Timer *timer) TN_INT_IDIS_SAVE(); - if (_tn_timer_is_active(timer)){ - rc = _tn_timer_cancel(timer); - } + rc = _tn_timer_cancel(timer); TN_INT_IRESTORE(); @@ -444,9 +438,7 @@ enum TN_RCode _tn_timer_start(struct TN_Timer *timer, TN_Timeout timeout) } else { //-- if timer is active, cancel it first - if (_tn_timer_is_active(timer)){ - rc = _tn_timer_cancel(timer); - } + rc = _tn_timer_cancel(timer); if (timeout < TN_TICK_LISTS_CNT){ //-- timer should be added to the one of "tick" lists. @@ -479,14 +471,16 @@ enum TN_RCode _tn_timer_cancel(struct TN_Timer *timer) } #endif - //-- reset timeout to zero (but this is actually not necessary) - timer->timeout_cur = 0; + if (_tn_timer_is_active(timer)){ + //-- reset timeout to zero (but this is actually not necessary) + timer->timeout_cur = 0; - //-- remove entry from timer queue - tn_list_remove_entry(&(timer->timer_queue)); + //-- remove entry from timer queue + tn_list_remove_entry(&(timer->timer_queue)); - //-- reset the list - tn_list_reset(&(timer->timer_queue)); + //-- reset the list + tn_list_reset(&(timer->timer_queue)); + } return rc; } From e339d6f899677652dd90b261561f28ed0bd25e50 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Sat, 11 Oct 2014 18:37:55 +0400 Subject: [PATCH 22/42] [FIX] while iterating through 'tick' list, we should use tn_list_first_entry() instead of tn_list_first_entry_remove(), because _tn_timer_cancel() that is called later will remove timer from the list --- src/core/tn_timer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/tn_timer.c b/src/core/tn_timer.c index 483c1cc..1baac38 100644 --- a/src/core/tn_timer.c +++ b/src/core/tn_timer.c @@ -400,7 +400,7 @@ void _tn_timers_tick_proceed(void) // TN_TICK_LISTS_CNT is added to the "generic" list), // see implementation details in the tn_timer.h file while (!tn_is_list_empty(p_cur_timer_list)){ - timer = tn_list_first_entry_remove( + timer = tn_list_first_entry( p_cur_timer_list, typeof(*timer), timer_queue ); From b2e35ab20d97d3b1b2b64c70d4edb05d5d224379 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Sat, 11 Oct 2014 19:04:40 +0400 Subject: [PATCH 23/42] timers: timeout_cur is set even if timer is in the 'tick' list, we will need that if we implement tn_timer_timeout_left() --- src/core/tn_timer.c | 7 ++++--- src/core/tn_timer.h | 3 +++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/core/tn_timer.c b/src/core/tn_timer.c index 1baac38..1fe39ec 100644 --- a/src/core/tn_timer.c +++ b/src/core/tn_timer.c @@ -442,10 +442,11 @@ enum TN_RCode _tn_timer_start(struct TN_Timer *timer, TN_Timeout timeout) if (timeout < TN_TICK_LISTS_CNT){ //-- timer should be added to the one of "tick" lists. - // Note that we shouldn't set timeout_cur here, - // because it has no effect when timer is in "tick" list. + int tick_list_index = _TICK_LIST_INDEX(timeout); + timer->timeout_cur = tick_list_index; + tn_list_add_tail( - &tn_timer_list__tick[ _TICK_LIST_INDEX(timeout) ], + &tn_timer_list__tick[ tick_list_index ], &(timer->timer_queue) ); } else { diff --git a/src/core/tn_timer.h b/src/core/tn_timer.h index 0640d46..7d48522 100644 --- a/src/core/tn_timer.h +++ b/src/core/tn_timer.h @@ -39,6 +39,9 @@ * * A timer * + * TODO: expain that we haven't 'cyclic' timer and why we haven't it. + * Provide usage example. + * * \section timers_implementation Implementation of timers * * You don't have to understand the implementation of timers to use them, From 4adb0f909d06d5efc8b2fce228a2110d017e6937 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Sat, 11 Oct 2014 20:48:12 +0400 Subject: [PATCH 24/42] todo updated --- stuff/vimwiki/index.wiki | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stuff/vimwiki/index.wiki b/stuff/vimwiki/index.wiki index cd01f11..0dd411b 100644 --- a/stuff/vimwiki/index.wiki +++ b/stuff/vimwiki/index.wiki @@ -7,12 +7,12 @@ == Routine == - * [.] Timer + * [o] Timer * [X] tn_timer_start() should allow re-starting timer (that is, start timer again if it is already active) - * [ ] what if we manage timers from the function called by timer? The tn_timer_list is altered, so, things may be mixed up + * [X] what if we manage timers from the function called by timer? The tn_timer_list is altered, so, things may be mixed up * [ ] probably we should add flags like SINGLE_SHOT and CYCLIC. * [ ] write 'detailed description' in the tn_timer.h - * [ ] probably move definition of tn_timer_list__gen and tn_timer_list__tick to the `tn_timer.c`, add `_tn_timers_init()`. So that all the timer-related stuff is in the single file. + * [X] probably move definition of tn_timer_list__gen and tn_timer_list__tick to the `tn_timer.c`, add `_tn_timers_init()`. So that all the timer-related stuff is in the single file. == Plans == From 89df1f227c49f0adfb5585a3bd8b84a6e9f93839 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Sat, 11 Oct 2014 23:40:43 +0400 Subject: [PATCH 25/42] dev.wiki: added 'thoughts' page with some thoughts about waiting for multiple events (say, messages from multiple queues, etc) --- stuff/vimwiki/index.wiki | 2 ++ stuff/vimwiki/thoughts.wiki | 44 +++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 stuff/vimwiki/thoughts.wiki diff --git a/stuff/vimwiki/index.wiki b/stuff/vimwiki/index.wiki index 0dd411b..c17caa2 100644 --- a/stuff/vimwiki/index.wiki +++ b/stuff/vimwiki/index.wiki @@ -2,6 +2,7 @@ = tneokernel dev = [[how_to_release]] +[[thoughts]] @@ -18,6 +19,7 @@ * [ ] Timer object * [ ] Event group connection (at least, it would be useful to connect event group to queue) + * [ ] Event group: probably, add mask of flags that should be cleared automatically when any task `wait`s for it * [ ] Add a destroy callback for each task, this callback (if not NULL) should be called whenever task is terminated with `tn_task_exit()` or `tn_task_terminate()`. * [ ] Port to ARM * [ ] Probably port to PIC24/dsPIC diff --git a/stuff/vimwiki/thoughts.wiki b/stuff/vimwiki/thoughts.wiki new file mode 100644 index 0000000..e1d3e82 --- /dev/null +++ b/stuff/vimwiki/thoughts.wiki @@ -0,0 +1,44 @@ + += tneokernel dev / thoughts= + +== waiting for multiple events == + +By waiting for multiple events I mean waiting for, say, messages from multiple queues (really, feature that is sometimes needed), or things like that. + +=== by hand === + +NOTE: this is buggy approach, see *cons* section below. + +We can just follow the convention: right after the producer puts new message to the queue, it sets particular flag. So, all the consumers need is to wait for that flag. We may want to set this flag to be auto-cleared (TODO: implement it in the eventgroup), so that just one task will be woken up when message comes + +==== cons ==== + + * what if consumer has no chance to receive the message before the second one comes? So, when first message is received, flag is cleared, but there is still some message in the queue. So, we can use it if only we have one element in the queue, but why do we need queue at all then? + + + +=== event connection === + +We can implement event connection, so that queue will maintain particular flag(s) in the event group: when there are message(s) in the queue, flag is automatically set by the queue (and the flag keeps being set while at least one message is in the queue). When last message was read from the queue, flag is automatically cleared by the queue. Producer need not set the flag, consumer need not clear flag. Consumer(s) just need to wait for the flag. + +NOTE that we shouldn't set this flag as auto-cleared by event group, because it is already auto-cleared by the queue. + +==== pros ==== + + * relatively easy to implement + +==== cons ==== + + * if multiple tasks are waiting for the event, they all will become runnable, but we actually need to wake up just one of them. So, one of them should receive (polling) message from the queue, but others just notices that there are no messages, and go waiting for the flag again + * non universal way: we should implement event connection to all the possible events we want to be able to combine. + + +==== dev notes ==== + + * we should NOT add the event into the same receive_queue together with tasks: then, when new message appears, queue will take first object from the queue (it is connected event group), and what if nobody waits for this event at the moment? + +=== EventCollector === + +What about some object like EventCollector? (not sure how to implement it) + + From f47fa32678a039a26690ed43ae08335e440d002a Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Sun, 12 Oct 2014 00:41:45 +0400 Subject: [PATCH 26/42] timers documentation improved --- .../nbproject/configurations.xml | 2 +- src/core/tn_timer.h | 50 +++++++++++++------ stuff/vimwiki/index.wiki | 3 ++ 3 files changed, 39 insertions(+), 16 deletions(-) diff --git a/examples/pic32/basic/tn_pic32_example_basic.X/nbproject/configurations.xml b/examples/pic32/basic/tn_pic32_example_basic.X/nbproject/configurations.xml index 98b5230..9fe8435 100644 --- a/examples/pic32/basic/tn_pic32_example_basic.X/nbproject/configurations.xml +++ b/examples/pic32/basic/tn_pic32_example_basic.X/nbproject/configurations.xml @@ -33,7 +33,7 @@ PIC32MX440F512H - PICkit3PlatformTool + ICD3PlatformTool XC32 1.21 2 diff --git a/src/core/tn_timer.h b/src/core/tn_timer.h index 7d48522..80da10b 100644 --- a/src/core/tn_timer.h +++ b/src/core/tn_timer.h @@ -37,16 +37,34 @@ /** * \file * - * A timer + * Timer: a kernel object that is used to ask the kernel to call some + * user-provided function at a particular time in the future, based on the + * $(TN_SYS_TIMER_LINK) tick. * - * TODO: expain that we haven't 'cyclic' timer and why we haven't it. - * Provide usage example. + * In the spirit of TNeoKernel, timers are as lightweight as possible. That's + * why there is only one type of timer: the single-shot timer. If you need your + * timer to fire repeatedly, you can easily restart it from the timer function + * by the `tn_timer_istart()`, so it's not a problem. + * + * When timer fires, the user-provided function is called. Be aware of the + * following: + * - Function is called from ISR context (namely, from $(TN_SYS_TIMER_LINK) + * ISR, by the `tn_tick_int_processing()`); + * - Function is called with global interrupts disabled. + * + * Consequently: + * + * - It's legal to call interrupt services from this function; + * - The function should be as fast as possible. + * + * See `#TN_TimerFunc` for the prototype of the function that could be + * scheduled. * * \section timers_implementation Implementation of timers * - * You don't have to understand the implementation of timers to use them, - * but it is probably worth knowing if you want to understand better how the - * kernel works and configure it appropriately for your particular application. + * Although you don't have to understand the implementation of timers to use + * them, it is probably worth knowing, particularly because the kernel have an + * option to customize the balance between memory usage and performance. * * The easiest implementation of timers could be something like this: we * have just a single list with all active timers, and at every system tick @@ -144,14 +162,16 @@ struct TN_Timer; /** * Prototype of the function that should be called by timer. * - * When timer fires, function gets called by the kernel. Be aware of the + * When timer fires, the user-provided function is called. Be aware of the * following: * - Function is called from ISR context (namely, from $(TN_SYS_TIMER_LINK) - * ISR); - * - Function is called with global interrupts disabled; + * ISR, by the `tn_tick_int_processing()`); + * - Function is called with global interrupts disabled. * - * So, it's legal to call interrupt services from this function, and the - * function should be as fast as possible. + * Consequently: + * + * - It's legal to call interrupt services from this function; + * - The function should be as fast as possible. * * @param timer * Timer that caused function to be called @@ -173,9 +193,6 @@ struct TN_Timer { /// /// User data pointer that is given to user-provided `func`. void *p_user_data; - // - // Timeout value that is set by user - //TN_Timeout timeout_base; /// /// Current (left) timeout value TN_Timeout timeout_cur; @@ -240,9 +257,12 @@ enum TN_RCode tn_timer_create( enum TN_RCode tn_timer_delete(struct TN_Timer *timer); /** - * Start the timer, that is, schedule the timer's function (given to + * Start the timer: that is, schedule the timer's function (given to * `tn_timer_create()`) to be called later by the kernel. See `TN_TimerFunc()`. * + * It is legal to restart already active timer. In this case, the timer will be + * cancelled first. + * * $(TN_CALL_FROM_TASK) * $(TN_LEGEND_LINK) * diff --git a/stuff/vimwiki/index.wiki b/stuff/vimwiki/index.wiki index c17caa2..24a559d 100644 --- a/stuff/vimwiki/index.wiki +++ b/stuff/vimwiki/index.wiki @@ -21,6 +21,9 @@ * [ ] Event group connection (at least, it would be useful to connect event group to queue) * [ ] Event group: probably, add mask of flags that should be cleared automatically when any task `wait`s for it * [ ] Add a destroy callback for each task, this callback (if not NULL) should be called whenever task is terminated with `tn_task_exit()` or `tn_task_terminate()`. + * [ ] Examples + * [ ] Separate platform-dependent code from the real example project code + * [ ] Provide examples for all common system services: timers, queues, etc. * [ ] Port to ARM * [ ] Probably port to PIC24/dsPIC From cc5ae126fa58877ca1deb11336d48e1d9c0935e5 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Sun, 12 Oct 2014 01:14:43 +0400 Subject: [PATCH 27/42] timers documentation improved --- src/core/tn_sys.h | 3 +++ src/core/tn_timer.h | 36 ++++++++++++++++++++++-------------- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/src/core/tn_sys.h b/src/core/tn_sys.h index e5da7d5..eea4a15 100644 --- a/src/core/tn_sys.h +++ b/src/core/tn_sys.h @@ -250,6 +250,9 @@ void tn_sys_start( * The period of this timer is determined by user * (typically 1 ms, but user is free to set different value) * + * Among other things, expired \ref tn_timer.h "timers" are fired from this + * function. + * * For further information, refer to \ref quick_guide "Quick guide". * * $(TN_CALL_FROM_ISR) diff --git a/src/core/tn_timer.h b/src/core/tn_timer.h index 80da10b..b77ec7b 100644 --- a/src/core/tn_timer.h +++ b/src/core/tn_timer.h @@ -48,14 +48,17 @@ * * When timer fires, the user-provided function is called. Be aware of the * following: - * - Function is called from ISR context (namely, from $(TN_SYS_TIMER_LINK) - * ISR, by the `tn_tick_int_processing()`); - * - Function is called with global interrupts disabled. + * + * - Function is called from ISR context (namely, from $(TN_SYS_TIMER_LINK) + * ISR, by the `tn_tick_int_processing()`); + * - Function is called with global interrupts disabled. * * Consequently: * - * - It's legal to call interrupt services from this function; - * - The function should be as fast as possible. + * - It's legal to call interrupt services from this function; + * - The function should be as fast as possible. + * - The function should not enable interrupts unconditionally. Consider using + * `tn_arch_sr_save_int_dis()` and `tn_arch_sr_restore()` if you need. * * See `#TN_TimerFunc` for the prototype of the function that could be * scheduled. @@ -64,7 +67,8 @@ * * Although you don't have to understand the implementation of timers to use * them, it is probably worth knowing, particularly because the kernel have an - * option to customize the balance between memory usage and performance. + * option `#TN_TICK_LISTS_CNT` to customize the balance between performance of + * `tn_tick_int_processing()` and memory occupied by timers. * * The easiest implementation of timers could be something like this: we * have just a single list with all active timers, and at every system tick @@ -78,15 +82,14 @@ * This approach has drawbacks: * * - We can't manage timers from the function called by timer. If we do so - * (say, if we start new timer, or cancel one of the active timers), then the - * timer list gets modified. But we are currently iterating through this - * list, so, it's quite easy to mix things up. + * (say, if we start new timer), then the timer list gets modified. But we + * are currently iterating through this list, so, it's quite easy to mix + * things up. * - It is inefficient on rather large amount of timers and/or with large * timeouts: we should iterate through all of them each system tick. * * The latter is probably not so critical in the embedded world since large - * amount of timers is unlikely there; whereas the the former is actually - * notable. + * amount of timers is unlikely there; whereas the former is actually notable. * * So, different approach was applied. The main idea is taken from the mainline * Linux kernel source, but the implementation was simplified much because (1) @@ -257,7 +260,7 @@ enum TN_RCode tn_timer_create( enum TN_RCode tn_timer_delete(struct TN_Timer *timer); /** - * Start the timer: that is, schedule the timer's function (given to + * Start or restart the timer: that is, schedule the timer's function (given to * `tn_timer_create()`) to be called later by the kernel. See `TN_TimerFunc()`. * * It is legal to restart already active timer. In this case, the timer will be @@ -284,7 +287,9 @@ enum TN_RCode tn_timer_delete(struct TN_Timer *timer); enum TN_RCode tn_timer_start(struct TN_Timer *timer, TN_Timeout timeout); /** - * The same as `tn_timer_start()` but for using in the ISR. + * The same as `tn_timer_start()` but for using in the ISR. Can be used from + * the `#TN_TimerFunc` function called by a timer, to start or restart *any* + * timer. * * $(TN_CALL_FROM_ISR) * $(TN_LEGEND_LINK) @@ -310,7 +315,10 @@ enum TN_RCode tn_timer_istart(struct TN_Timer *timer, TN_Timeout timeout); enum TN_RCode tn_timer_cancel(struct TN_Timer *timer); /** - * The same as `tn_timer_cancel()` but for using in the ISR. + * The same as `tn_timer_cancel()` but for using in the ISR. Can be used from + * the `#TN_TimerFunc` function called by a timer, to cancel *any* timer. Note + * that you don't need to cancel the timer that caused the function to be + * called, although nothing will break if you do so. * * $(TN_CALL_FROM_ISR) * $(TN_LEGEND_LINK) From fc4a39548183935c6689c44064906ae7805b79dd Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Sun, 12 Oct 2014 01:51:57 +0400 Subject: [PATCH 28/42] added tn_timer_set_func() / tn_timer_iset_func() --- src/core/tn_internal.h | 9 ++++ src/core/tn_timer.c | 99 ++++++++++++++++++++++++++++++++++++++---- src/core/tn_timer.h | 41 +++++++++++++++-- 3 files changed, 136 insertions(+), 13 deletions(-) diff --git a/src/core/tn_internal.h b/src/core/tn_internal.h index 160ac9f..5af60b5 100644 --- a/src/core/tn_internal.h +++ b/src/core/tn_internal.h @@ -456,6 +456,15 @@ enum TN_RCode _tn_timer_create( void *p_user_data ); +/** + * TODO + */ +enum TN_RCode _tn_timer_set_func( + struct TN_Timer *timer, + TN_TimerFunc *func, + void *p_user_data + ); + /** * TODO */ diff --git a/src/core/tn_timer.c b/src/core/tn_timer.c index 1fe39ec..3cce500 100644 --- a/src/core/tn_timer.c +++ b/src/core/tn_timer.c @@ -116,11 +116,7 @@ static inline enum TN_RCode _check_param_create( if (timer == NULL){ rc = TN_RC_WPARAM; - } else if (0 - || timer->id_timer == TN_ID_TIMER - || func == NULL - ) - { + } else if (timer->id_timer == TN_ID_TIMER){ rc = TN_RC_WPARAM; } @@ -310,6 +306,71 @@ enum TN_RCode tn_timer_icancel(struct TN_Timer *timer) return rc; } +/* + * See comments in the header file (tn_timer.h) + */ +enum TN_RCode tn_timer_set_func( + struct TN_Timer *timer, + TN_TimerFunc *func, + void *p_user_data + ) +{ + TN_INTSAVE_DATA; + enum TN_RCode rc = TN_RC_OK; + + rc = _check_param_generic(timer); + if (rc != TN_RC_OK){ + goto out; + } + + if (!tn_is_task_context()){ + rc = TN_RC_WCONTEXT; + goto out; + } + + TN_INT_DIS_SAVE(); + + rc = _tn_timer_set_func(timer, func, p_user_data); + + TN_INT_RESTORE(); + +out: + return rc; +} + +/* + * See comments in the header file (tn_timer.h) + */ +enum TN_RCode tn_timer_iset_func( + struct TN_Timer *timer, + TN_TimerFunc *func, + void *p_user_data + ) +{ + TN_INTSAVE_DATA_INT; + enum TN_RCode rc = TN_RC_OK; + + rc = _check_param_generic(timer); + if (rc != TN_RC_OK){ + goto out; + } + + if (!tn_is_isr_context()){ + rc = TN_RC_WCONTEXT; + goto out; + } + + TN_INT_IDIS_SAVE(); + + rc = _tn_timer_set_func(timer, func, p_user_data); + + TN_INT_IRESTORE(); + +out: + return rc; +} + + @@ -492,16 +553,36 @@ enum TN_RCode _tn_timer_create( void *p_user_data ) { - enum TN_RCode rc = TN_RC_OK; + enum TN_RCode rc = _tn_timer_set_func(timer, func, p_user_data); + + if (rc != TN_RC_OK){ + goto out; + } tn_list_reset(&(timer->timer_queue)); - timer->func = func; - timer->p_user_data = p_user_data; timer->timeout_cur = 0; - timer->id_timer = TN_ID_TIMER; +out: + return rc; +} + +enum TN_RCode _tn_timer_set_func( + struct TN_Timer *timer, + TN_TimerFunc *func, + void *p_user_data + ) +{ + enum TN_RCode rc = TN_RC_OK; + + if (func == NULL){ + rc = TN_RC_WPARAM; + } else { + timer->func = func; + timer->p_user_data = p_user_data; + } + return rc; } diff --git a/src/core/tn_timer.h b/src/core/tn_timer.h index b77ec7b..e1a5761 100644 --- a/src/core/tn_timer.h +++ b/src/core/tn_timer.h @@ -228,14 +228,13 @@ struct TN_Timer { * @param timer * Pointer to already allocated `struct TN_Timer` * @param func - * Function to be called by timer. See `TN_TimerFunc()` + * Function to be called by timer, can't be `NULL`. See `TN_TimerFunc()` * @param p_user_data * User data pointer that is given to user-provided `func`. * * @return * * `#TN_RC_OK` if timer was successfully created; - * * If `#TN_CHECK_PARAM` is non-zero, additional return code - * is available: `#TN_RC_WPARAM`. + * * `#TN_RC_WPARAM` if wrong params were given. */ enum TN_RCode tn_timer_create( struct TN_Timer *timer, @@ -289,7 +288,8 @@ enum TN_RCode tn_timer_start(struct TN_Timer *timer, TN_Timeout timeout); /** * The same as `tn_timer_start()` but for using in the ISR. Can be used from * the `#TN_TimerFunc` function called by a timer, to start or restart *any* - * timer. + * timer; typically used to start again the timer from the function called + * by the timer itself. * * $(TN_CALL_FROM_ISR) * $(TN_LEGEND_LINK) @@ -325,6 +325,39 @@ enum TN_RCode tn_timer_cancel(struct TN_Timer *timer); */ enum TN_RCode tn_timer_icancel(struct TN_Timer *timer); +/** + * Set user-provided function and pointer to user data for the timer. + * Can be called if timer is either active or inactive. + * + * @param timer + * Pointer to timer + * @param func + * Function to be called by timer, can't be `NULL`. See `TN_TimerFunc()` + * @param p_user_data + * User data pointer that is given to user-provided `func`. + * + * @return + * * `#TN_RC_OK` if timer was successfully created; + * * `#TN_RC_WPARAM` if wrong params were given. + */ +enum TN_RCode tn_timer_set_func( + struct TN_Timer *timer, + TN_TimerFunc *func, + void *p_user_data + ); + +/** + * The same as `tn_timer_set_func()` but for using in the ISR. + * + * $(TN_CALL_FROM_ISR) + * $(TN_LEGEND_LINK) + */ +enum TN_RCode tn_timer_iset_func( + struct TN_Timer *timer, + TN_TimerFunc *func, + void *p_user_data + ); + #ifdef __cplusplus } /* extern "C" */ #endif From a827f208fe52c1284aee44668ae7fecbb56a5c8f Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Sun, 12 Oct 2014 03:44:34 +0400 Subject: [PATCH 29/42] all timer services can be called from either task or ISR. Added tn_timer_is_active(), tn_timer_time_left() --- src/arch/pic32/tn_arch_pic32.h | 11 +- src/core/tn_internal.h | 20 +-- src/core/tn_timer.c | 233 ++++++++++++--------------------- src/core/tn_timer.h | 68 ++++++---- stuff/vimwiki/index.wiki | 1 + 5 files changed, 137 insertions(+), 196 deletions(-) diff --git a/src/arch/pic32/tn_arch_pic32.h b/src/arch/pic32/tn_arch_pic32.h index 0651c11..682cf33 100644 --- a/src/arch/pic32/tn_arch_pic32.h +++ b/src/arch/pic32/tn_arch_pic32.h @@ -207,10 +207,15 @@ typedef unsigned int TN_UWord; #ifdef __mips16 # define TN_INT_DIS_SAVE() tn_save_status_reg = tn_arch_sr_save_int_dis() -# define TN_INT_RESTORE() _TN_PIC32_INTSAVE_CHECK(); tn_arch_sr_restore(tn_save_status_reg) +# define TN_INT_RESTORE() _TN_PIC32_INTSAVE_CHECK(); \ + tn_arch_sr_restore(tn_save_status_reg) #else -# define TN_INT_DIS_SAVE() __asm__ __volatile__("di %0; ehb" : "=d" (tn_save_status_reg)) -# define TN_INT_RESTORE() _TN_PIC32_INTSAVE_CHECK(); __builtin_mtc0(12, 0, tn_save_status_reg) +# define TN_INT_DIS_SAVE() __asm__ __volatile__( \ + "di %0; ehb" \ + : "=d" (tn_save_status_reg) \ + ) +# define TN_INT_RESTORE() _TN_PIC32_INTSAVE_CHECK(); \ + __builtin_mtc0(12, 0, tn_save_status_reg) #endif /** diff --git a/src/core/tn_internal.h b/src/core/tn_internal.h index 5af60b5..25a0f7b 100644 --- a/src/core/tn_internal.h +++ b/src/core/tn_internal.h @@ -432,44 +432,28 @@ extern struct TN_ListItem tn_timer_list__tick[ TN_TICK_LISTS_CNT ]; */ void _tn_timers_tick_proceed(void); -/** - * TODO - */ void _tn_timers_init(void); -/** - * TODO - */ enum TN_RCode _tn_timer_start(struct TN_Timer *timer, TN_Timeout timeout); -/** - * TODO - */ enum TN_RCode _tn_timer_cancel(struct TN_Timer *timer); -/** - * TODO - */ enum TN_RCode _tn_timer_create( struct TN_Timer *timer, TN_TimerFunc *func, void *p_user_data ); -/** - * TODO - */ enum TN_RCode _tn_timer_set_func( struct TN_Timer *timer, TN_TimerFunc *func, void *p_user_data ); -/** - * TODO - */ BOOL _tn_timer_is_active(struct TN_Timer *timer); +TN_Timeout _tn_timer_time_left(struct TN_Timer *timer); + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/src/core/tn_timer.c b/src/core/tn_timer.c index 3cce500..8fe21c4 100644 --- a/src/core/tn_timer.c +++ b/src/core/tn_timer.c @@ -151,11 +151,6 @@ enum TN_RCode tn_timer_create( goto out; } - if (!tn_is_task_context()){ - rc = TN_RC_WCONTEXT; - goto out; - } - rc = _tn_timer_create(timer, func, p_user_data); out: @@ -167,30 +162,19 @@ enum TN_RCode tn_timer_create( */ enum TN_RCode tn_timer_delete(struct TN_Timer *timer) { - TN_INTSAVE_DATA; - enum TN_RCode rc = TN_RC_OK; + int sr_saved; + enum TN_RCode rc = _check_param_generic(timer); - rc = _check_param_generic(timer); - if (rc != TN_RC_OK){ - goto out; - } + if (rc == TN_RC_OK){ + sr_saved = tn_arch_sr_save_int_dis(); + //-- if timer is active, cancel it first + rc = _tn_timer_cancel(timer); - if (!tn_is_task_context()){ - rc = TN_RC_WCONTEXT; - goto out; + //-- now, delete timer + timer->id_timer = 0; + tn_arch_sr_restore(sr_saved); } - TN_INT_DIS_SAVE(); - - //-- if timer is active, cancel it first - rc = _tn_timer_cancel(timer); - - //-- now, delete timer - timer->id_timer = 0; - - TN_INT_RESTORE(); - -out: return rc; } @@ -199,54 +183,15 @@ enum TN_RCode tn_timer_delete(struct TN_Timer *timer) */ enum TN_RCode tn_timer_start(struct TN_Timer *timer, TN_Timeout timeout) { - TN_INTSAVE_DATA; - enum TN_RCode rc = TN_RC_OK; + int sr_saved; + enum TN_RCode rc = _check_param_generic(timer); - rc = _check_param_generic(timer); - if (rc != TN_RC_OK){ - goto out; - } - - if (!tn_is_task_context()){ - rc = TN_RC_WCONTEXT; - goto out; + if (rc == TN_RC_OK){ + sr_saved = tn_arch_sr_save_int_dis(); + rc = _tn_timer_start(timer, timeout); + tn_arch_sr_restore(sr_saved); } - TN_INT_DIS_SAVE(); - - rc = _tn_timer_start(timer, timeout); - - TN_INT_RESTORE(); - -out: - return rc; -} - -/* - * See comments in the header file (tn_timer.h) - */ -enum TN_RCode tn_timer_istart(struct TN_Timer *timer, TN_Timeout timeout) -{ - TN_INTSAVE_DATA_INT; - enum TN_RCode rc = TN_RC_OK; - - rc = _check_param_generic(timer); - if (rc != TN_RC_OK){ - goto out; - } - - if (!tn_is_isr_context()){ - rc = TN_RC_WCONTEXT; - goto out; - } - - TN_INT_IDIS_SAVE(); - - rc = _tn_timer_start(timer, timeout); - - TN_INT_IRESTORE(); - -out: return rc; } @@ -255,118 +200,73 @@ enum TN_RCode tn_timer_istart(struct TN_Timer *timer, TN_Timeout timeout) */ enum TN_RCode tn_timer_cancel(struct TN_Timer *timer) { - TN_INTSAVE_DATA; - enum TN_RCode rc = TN_RC_OK; + int sr_saved; + enum TN_RCode rc = _check_param_generic(timer); - rc = _check_param_generic(timer); - if (rc != TN_RC_OK){ - goto out; - } - - if (!tn_is_task_context()){ - rc = TN_RC_WCONTEXT; - goto out; + if (rc == TN_RC_OK){ + sr_saved = tn_arch_sr_save_int_dis(); + rc = _tn_timer_cancel(timer); + tn_arch_sr_restore(sr_saved); } - TN_INT_DIS_SAVE(); - - rc = _tn_timer_cancel(timer); - - TN_INT_RESTORE(); - -out: return rc; } /* * See comments in the header file (tn_timer.h) */ -enum TN_RCode tn_timer_icancel(struct TN_Timer *timer) +enum TN_RCode tn_timer_set_func( + struct TN_Timer *timer, + TN_TimerFunc *func, + void *p_user_data + ) { - TN_INTSAVE_DATA_INT; - enum TN_RCode rc = TN_RC_OK; + int sr_saved; + enum TN_RCode rc = _check_param_generic(timer); - rc = _check_param_generic(timer); - if (rc != TN_RC_OK){ - goto out; - } - - if (!tn_is_isr_context()){ - rc = TN_RC_WCONTEXT; - goto out; + if (rc == TN_RC_OK){ + sr_saved = tn_arch_sr_save_int_dis(); + rc = _tn_timer_set_func(timer, func, p_user_data); + tn_arch_sr_restore(sr_saved); } - TN_INT_IDIS_SAVE(); - - rc = _tn_timer_cancel(timer); - - TN_INT_IRESTORE(); - -out: return rc; } /* * See comments in the header file (tn_timer.h) */ -enum TN_RCode tn_timer_set_func( - struct TN_Timer *timer, - TN_TimerFunc *func, - void *p_user_data - ) +enum TN_RCode tn_timer_is_active(struct TN_Timer *timer, BOOL *p_is_active) { - TN_INTSAVE_DATA; - enum TN_RCode rc = TN_RC_OK; + int sr_saved; + enum TN_RCode rc = _check_param_generic(timer); - rc = _check_param_generic(timer); - if (rc != TN_RC_OK){ - goto out; + if (rc == TN_RC_OK){ + sr_saved = tn_arch_sr_save_int_dis(); + *p_is_active = _tn_timer_is_active(timer); + tn_arch_sr_restore(sr_saved); } - if (!tn_is_task_context()){ - rc = TN_RC_WCONTEXT; - goto out; - } - - TN_INT_DIS_SAVE(); - - rc = _tn_timer_set_func(timer, func, p_user_data); - - TN_INT_RESTORE(); - -out: return rc; } /* * See comments in the header file (tn_timer.h) */ -enum TN_RCode tn_timer_iset_func( - struct TN_Timer *timer, - TN_TimerFunc *func, - void *p_user_data +enum TN_RCode tn_timer_time_left( + struct TN_Timer *timer, + TN_Timeout *p_time_left ) { - TN_INTSAVE_DATA_INT; - enum TN_RCode rc = TN_RC_OK; - - rc = _check_param_generic(timer); - if (rc != TN_RC_OK){ - goto out; - } + int sr_saved; + enum TN_RCode rc = _check_param_generic(timer); - if (!tn_is_isr_context()){ - rc = TN_RC_WCONTEXT; - goto out; + if (rc == TN_RC_OK){ + sr_saved = tn_arch_sr_save_int_dis(); + *p_time_left = _tn_timer_time_left(timer); + tn_arch_sr_restore(sr_saved); } - TN_INT_IDIS_SAVE(); - - rc = _tn_timer_set_func(timer, func, p_user_data); - - TN_INT_IRESTORE(); - -out: return rc; } @@ -588,6 +488,13 @@ enum TN_RCode _tn_timer_set_func( BOOL _tn_timer_is_active(struct TN_Timer *timer) { +#if TN_DEBUG + //-- interrupts should be disabled here + if (!TN_IS_INT_DISABLED()){ + _TN_FATAL_ERROR(""); + } +#endif + return (!tn_is_list_empty(&(timer->timer_queue))); } @@ -604,5 +511,35 @@ void _tn_timers_init(void) } } +TN_Timeout _tn_timer_time_left(struct TN_Timer *timer) +{ +#if TN_DEBUG + //-- interrupts should be disabled here + if (!TN_IS_INT_DISABLED()){ + _TN_FATAL_ERROR(""); + } +#endif + + TN_Timeout time_left = 0; + + if (_tn_timer_is_active(timer)){ + int tick_list_index = _TICK_LIST_INDEX(0); + + if (timer->timeout_cur > tick_list_index){ + time_left = timer->timeout_cur - tick_list_index; + } else if (timer->timeout_cur < tick_list_index){ + time_left = timer->timeout_cur + TN_TICK_LISTS_CNT - tick_list_index; + } else { +#if TN_DEBUG + //-- timer->timeout_cur should never be equal to tick_list_index here + // (if it is, timer is inactive, so, we don't get here) + _TN_FATAL_ERROR(); +#endif + } + } + + return time_left; +} + diff --git a/src/core/tn_timer.h b/src/core/tn_timer.h index e1a5761..74066d8 100644 --- a/src/core/tn_timer.h +++ b/src/core/tn_timer.h @@ -44,7 +44,7 @@ * In the spirit of TNeoKernel, timers are as lightweight as possible. That's * why there is only one type of timer: the single-shot timer. If you need your * timer to fire repeatedly, you can easily restart it from the timer function - * by the `tn_timer_istart()`, so it's not a problem. + * by the `tn_timer_start()`, so it's not a problem. * * When timer fires, the user-provided function is called. Be aware of the * following: @@ -246,6 +246,7 @@ enum TN_RCode tn_timer_create( * Destruct the timer. If the timer is active, it is cancelled first. * * $(TN_CALL_FROM_TASK) + * $(TN_CALL_FROM_ISR) * $(TN_LEGEND_LINK) * * @param timer timer to destruct @@ -266,6 +267,7 @@ enum TN_RCode tn_timer_delete(struct TN_Timer *timer); * cancelled first. * * $(TN_CALL_FROM_TASK) + * $(TN_CALL_FROM_ISR) * $(TN_LEGEND_LINK) * * @param timer @@ -285,22 +287,12 @@ enum TN_RCode tn_timer_delete(struct TN_Timer *timer); */ enum TN_RCode tn_timer_start(struct TN_Timer *timer, TN_Timeout timeout); -/** - * The same as `tn_timer_start()` but for using in the ISR. Can be used from - * the `#TN_TimerFunc` function called by a timer, to start or restart *any* - * timer; typically used to start again the timer from the function called - * by the timer itself. - * - * $(TN_CALL_FROM_ISR) - * $(TN_LEGEND_LINK) - */ -enum TN_RCode tn_timer_istart(struct TN_Timer *timer, TN_Timeout timeout); - /** * If timer is active, cancel it. If timer is already inactive, nothing is * changed. * * $(TN_CALL_FROM_TASK) + * $(TN_CALL_FROM_ISR) * $(TN_LEGEND_LINK) * * @param timer @@ -315,19 +307,12 @@ enum TN_RCode tn_timer_istart(struct TN_Timer *timer, TN_Timeout timeout); enum TN_RCode tn_timer_cancel(struct TN_Timer *timer); /** - * The same as `tn_timer_cancel()` but for using in the ISR. Can be used from - * the `#TN_TimerFunc` function called by a timer, to cancel *any* timer. Note - * that you don't need to cancel the timer that caused the function to be - * called, although nothing will break if you do so. + * Set user-provided function and pointer to user data for the timer. + * Can be called if timer is either active or inactive. * + * $(TN_CALL_FROM_TASK) * $(TN_CALL_FROM_ISR) * $(TN_LEGEND_LINK) - */ -enum TN_RCode tn_timer_icancel(struct TN_Timer *timer); - -/** - * Set user-provided function and pointer to user data for the timer. - * Can be called if timer is either active or inactive. * * @param timer * Pointer to timer @@ -347,15 +332,44 @@ enum TN_RCode tn_timer_set_func( ); /** - * The same as `tn_timer_set_func()` but for using in the ISR. + * Returns whether given timer is active or inactive. + * + * $(TN_CALL_FROM_TASK) + * $(TN_CALL_FROM_ISR) + * $(TN_LEGEND_LINK) + * + * @param timer + * Pointer to timer + * @param p_is_active + * Pointer to `#BOOL` variable in which resulting value should be stored + * + * @return + * * `#TN_RC_OK` if timer was successfully created; + * * `#TN_RC_WPARAM` if wrong params were given. + */ +enum TN_RCode tn_timer_is_active(struct TN_Timer *timer, BOOL *p_is_active); + +/** + * Returns how many $(TN_SYS_TIMER_LINK) ticks (at most) is left for the timer + * to expire. If timer is inactive, 0 is returned. * + * $(TN_CALL_FROM_TASK) * $(TN_CALL_FROM_ISR) * $(TN_LEGEND_LINK) + * + * @param timer + * Pointer to timer + * @param p_time_left + * Pointer to `#TN_Timeout` variable in which resulting value should be + * stored + * + * @return + * * `#TN_RC_OK` if timer was successfully created; + * * `#TN_RC_WPARAM` if wrong params were given. */ -enum TN_RCode tn_timer_iset_func( - struct TN_Timer *timer, - TN_TimerFunc *func, - void *p_user_data +enum TN_RCode tn_timer_time_left( + struct TN_Timer *timer, + TN_Timeout *p_time_left ); #ifdef __cplusplus diff --git a/stuff/vimwiki/index.wiki b/stuff/vimwiki/index.wiki index 24a559d..d8d86e8 100644 --- a/stuff/vimwiki/index.wiki +++ b/stuff/vimwiki/index.wiki @@ -26,6 +26,7 @@ * [ ] Provide examples for all common system services: timers, queues, etc. * [ ] Port to ARM * [ ] Probably port to PIC24/dsPIC + * [ ] Test how much is it slower to use tn_arch_sr_save_int_dis()/tn_arch_sr_restore() instead of TN_INT_DIS_SAVE()/TN_INT_RESTORE() From c5c305382c7a92e69f96ceae23f0f2499055b383 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Sun, 12 Oct 2014 03:46:12 +0400 Subject: [PATCH 30/42] [FIX] tn_sem_create() returned TN_RC_WCONTEXT if called from ISR, but actually it should be legal to call it from ISR --- src/core/tn_sem.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/core/tn_sem.c b/src/core/tn_sem.c index 2129685..19ee77e 100644 --- a/src/core/tn_sem.c +++ b/src/core/tn_sem.c @@ -241,11 +241,6 @@ enum TN_RCode tn_sem_create( goto out; } - if (!tn_is_task_context()){ - rc = TN_RC_WCONTEXT; - goto out; - } - tn_list_reset(&(sem->wait_queue)); sem->count = start_count; From 1f8d2d8fed15d5c2841383e4752f121566cfb530 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Sun, 12 Oct 2014 14:56:28 +0400 Subject: [PATCH 31/42] little docs change --- src/core/tn_timer.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/core/tn_timer.h b/src/core/tn_timer.h index 74066d8..a6f35a9 100644 --- a/src/core/tn_timer.h +++ b/src/core/tn_timer.h @@ -56,7 +56,9 @@ * Consequently: * * - It's legal to call interrupt services from this function; - * - The function should be as fast as possible. + * - You should make sure that your interrupt stack is enough for this + * function; + * - The function should be as fast as possible; * - The function should not enable interrupts unconditionally. Consider using * `tn_arch_sr_save_int_dis()` and `tn_arch_sr_restore()` if you need. * From ddafe63c14b9b6c510638ef47c0009120ee8cd1c Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Sun, 12 Oct 2014 15:30:36 +0400 Subject: [PATCH 32/42] dev wiki updated --- stuff/vimwiki/index.wiki | 7 ------- stuff/vimwiki/thoughts.wiki | 2 ++ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/stuff/vimwiki/index.wiki b/stuff/vimwiki/index.wiki index d8d86e8..8eae7c4 100644 --- a/stuff/vimwiki/index.wiki +++ b/stuff/vimwiki/index.wiki @@ -8,16 +8,9 @@ == Routine == - * [o] Timer - * [X] tn_timer_start() should allow re-starting timer (that is, start timer again if it is already active) - * [X] what if we manage timers from the function called by timer? The tn_timer_list is altered, so, things may be mixed up - * [ ] probably we should add flags like SINGLE_SHOT and CYCLIC. - * [ ] write 'detailed description' in the tn_timer.h - * [X] probably move definition of tn_timer_list__gen and tn_timer_list__tick to the `tn_timer.c`, add `_tn_timers_init()`. So that all the timer-related stuff is in the single file. == Plans == - * [ ] Timer object * [ ] Event group connection (at least, it would be useful to connect event group to queue) * [ ] Event group: probably, add mask of flags that should be cleared automatically when any task `wait`s for it * [ ] Add a destroy callback for each task, this callback (if not NULL) should be called whenever task is terminated with `tn_task_exit()` or `tn_task_terminate()`. diff --git a/stuff/vimwiki/thoughts.wiki b/stuff/vimwiki/thoughts.wiki index e1d3e82..525731f 100644 --- a/stuff/vimwiki/thoughts.wiki +++ b/stuff/vimwiki/thoughts.wiki @@ -41,4 +41,6 @@ NOTE that we shouldn't set this flag as auto-cleared by event group, because it What about some object like EventCollector? (not sure how to implement it) +Well, we want to be able to wait for multiple events atomically: say, we want to wait for message from the queue and for the semaphore. It seems really hard to do that in single call to something like `tn_collector_wait()`, because then `tn_collector_wait()` should take all the possible arguments for all possible waiting events: void *p_data for sending the data to queue, void **pp_data for receiving, the same for fmem, etc. So, "event connection" approach seems much better. + From bd8936e2798d628f7a5f57f28ac78456ce8b77a4 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Sun, 12 Oct 2014 16:12:44 +0400 Subject: [PATCH 33/42] TN_TICK_LISTS_CNT default is now 8 --- src/tn_cfg_default.h | 2 +- stuff/vimwiki/index.wiki | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tn_cfg_default.h b/src/tn_cfg_default.h index 7e9ed5a..c88e70e 100644 --- a/src/tn_cfg_default.h +++ b/src/tn_cfg_default.h @@ -148,7 +148,7 @@ * default value should work for you. */ #ifndef TN_TICK_LISTS_CNT -# define TN_TICK_LISTS_CNT 4 +# define TN_TICK_LISTS_CNT 8 #endif diff --git a/stuff/vimwiki/index.wiki b/stuff/vimwiki/index.wiki index 8eae7c4..aabffa2 100644 --- a/stuff/vimwiki/index.wiki +++ b/stuff/vimwiki/index.wiki @@ -11,6 +11,7 @@ == Plans == + * [ ] Make TN_PRIORITIES_CNT customizable. For 32-bit systems, TN_PRIORITIES_CNT is 32, and since we have struct TN_ListItem tn_ready_list[TN_PRIORITIES_CNT] and unsigned short tn_tslice_ticks[TN_PRIORITIES_CNT], it takes 10 bytes for each priority. So, overhead is 320 bytes, which is notable. We usually need about 5 priorities, so, it would take 50 bytes. * [ ] Event group connection (at least, it would be useful to connect event group to queue) * [ ] Event group: probably, add mask of flags that should be cleared automatically when any task `wait`s for it * [ ] Add a destroy callback for each task, this callback (if not NULL) should be called whenever task is terminated with `tn_task_exit()` or `tn_task_terminate()`. From 1f9ec5800c559202fdadcaa36bd53a37ab48a17a Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Sun, 12 Oct 2014 16:22:40 +0400 Subject: [PATCH 34/42] docs updated --- stuff/doc_pages/changelog.dox | 6 ++++-- stuff/doc_pages/plans.dox | 6 ------ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/stuff/doc_pages/changelog.dox b/stuff/doc_pages/changelog.dox index 02aec3c..3514a00 100644 --- a/stuff/doc_pages/changelog.dox +++ b/stuff/doc_pages/changelog.dox @@ -7,10 +7,12 @@ TNeoKernel changelog \section changelog_current Current development version (BETA) - - Working on \ref tn_timer.h "timers" + - Added \ref tn_timer.h "timers": kernel objects that are used to ask the + kernel to call some user-provided function at a particular time in the + future; - Removed `tn_sys_time_set()` function, because now TNeoKernel uses internal system tick count for timers, and modifying system tick counter by user - is *really* bad idea. + is a *really* bad idea. \section changelog_v1_01 v1.01 diff --git a/stuff/doc_pages/plans.dox b/stuff/doc_pages/plans.dox index 164017f..c1135b1 100644 --- a/stuff/doc_pages/plans.dox +++ b/stuff/doc_pages/plans.dox @@ -5,12 +5,6 @@ I have plans to implement some goodies not yet present in the kernel. -\section plans_timer Timer object - -It would be sometimes useful to have ability to ask the kernel to call custom function in the future, or even call it periodically. This is what Timer object is for. The overhead of it would be the same as if we put some task to wait for specified timeout, i.e. it's really little overhead. - -If we have no registered Timer objects, no extra overhead involved at all. - \section plans_event_connect Event connecting Sometimes we need to wait, say, for messages from several queues simultaneously; currently, the kernel does not have built-in support of it. How I plan to implement it: there should be a way to *connect* an event group and custom events mask to the data queue. Then, data queue will maintain flag(s) specified by mask: when queue is non-empty, it will set flag(s) by mask, when queue becomes empty, it will clear these flag(s). From 3beec7f4ce6f83047e55957160549bf96dcc00fb Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Sun, 12 Oct 2014 21:10:52 +0400 Subject: [PATCH 35/42] timer internals commented --- src/core/tn_internal.h | 37 +++++++++++++++++++++++++++---- src/core/tn_sys.c | 4 ++-- src/core/tn_timer.c | 50 +++++++++++++++++++++++++++++------------- 3 files changed, 70 insertions(+), 21 deletions(-) diff --git a/src/core/tn_internal.h b/src/core/tn_internal.h index 25a0f7b..ce4d066 100644 --- a/src/core/tn_internal.h +++ b/src/core/tn_internal.h @@ -37,8 +37,7 @@ /** * \file * - * Internal TNeoKernel header - * + * Internal TNeoKernel header, should not be included by the application. */ @@ -418,40 +417,70 @@ static inline void _tn_mutex_on_task_wait_complete(struct TN_Task *task) {} * tn_timer.c ******************************************************************************/ +/// /// "generic" list of timers, for details, refer to \ref timers_implementation extern struct TN_ListItem tn_timer_list__gen; +/// /// "tick" lists of timers, for details, refer to \ref timers_implementation extern struct TN_ListItem tn_timer_list__tick[ TN_TICK_LISTS_CNT ]; +/** + * Should be called once at system startup (from `#tn_sys_start()`). + * It merely resets all timer lists. + */ +void _tn_timers_init(void); + /** * Should be called from $(TN_SYS_TIMER_LINK) interrupt. It performs all * necessary timers housekeeping: moving them between lists, firing them, etc. + * + * See \ref timers_implementation for details. */ void _tn_timers_tick_proceed(void); -void _tn_timers_init(void); - +/** + * Actual worker function that is called by `#tn_timer_start()`. + * Interrupts should be disabled when calling it. + */ enum TN_RCode _tn_timer_start(struct TN_Timer *timer, TN_Timeout timeout); +/** + * Actual worker function that is called by `#tn_timer_cancel()`. + * Interrupts should be disabled when calling it. + */ enum TN_RCode _tn_timer_cancel(struct TN_Timer *timer); +/** + * Actual worker function that is called by `#tn_timer_create()`. + */ enum TN_RCode _tn_timer_create( struct TN_Timer *timer, TN_TimerFunc *func, void *p_user_data ); +/** + * Actual worker function that is called by `#tn_timer_set_func()`. + */ enum TN_RCode _tn_timer_set_func( struct TN_Timer *timer, TN_TimerFunc *func, void *p_user_data ); +/** + * Actual worker function that is called by `#tn_timer_is_active()`. + * Interrupts should be disabled when calling it. + */ BOOL _tn_timer_is_active(struct TN_Timer *timer); +/** + * Actual worker function that is called by `#tn_timer_time_left()`. + * Interrupts should be disabled when calling it. + */ TN_Timeout _tn_timer_time_left(struct TN_Timer *timer); #ifdef __cplusplus diff --git a/src/core/tn_sys.c b/src/core/tn_sys.c index fe3d06c..cdb6c45 100644 --- a/src/core/tn_sys.c +++ b/src/core/tn_sys.c @@ -71,8 +71,8 @@ unsigned short tn_tslice_ticks[TN_PRIORITIES_CNT]; volatile enum TN_StateFlag tn_sys_state; -struct TN_Task * tn_next_task_to_run; -struct TN_Task * tn_curr_run_task; +struct TN_Task *tn_next_task_to_run; +struct TN_Task *tn_curr_run_task; volatile unsigned int tn_ready_to_run_bmp; diff --git a/src/core/tn_timer.c b/src/core/tn_timer.c index 8fe21c4..d39e307 100644 --- a/src/core/tn_timer.c +++ b/src/core/tn_timer.c @@ -279,8 +279,23 @@ enum TN_RCode tn_timer_time_left( ******************************************************************************/ /** - * See comments in the tn_internal.h file, - * see also \ref timers_implementation. + * See comments in the tn_internal.h file. + */ +void _tn_timers_init(void) +{ + int i; + + //-- reset "generic" timers list + tn_list_reset(&tn_timer_list__gen); + + //-- reset all "tick" timer lists + for (i = 0; i < TN_TICK_LISTS_CNT; i++){ + tn_list_reset(&tn_timer_list__tick[i]); + } +} + +/** + * See comments in the tn_internal.h file. */ void _tn_timers_tick_proceed(void) { @@ -383,6 +398,9 @@ void _tn_timers_tick_proceed(void) // }}} } +/** + * See comments in the tn_internal.h file. + */ enum TN_RCode _tn_timer_start(struct TN_Timer *timer, TN_Timeout timeout) { enum TN_RCode rc = TN_RC_OK; @@ -422,6 +440,9 @@ enum TN_RCode _tn_timer_start(struct TN_Timer *timer, TN_Timeout timeout) return rc; } +/** + * See comments in the tn_internal.h file. + */ enum TN_RCode _tn_timer_cancel(struct TN_Timer *timer) { enum TN_RCode rc = TN_RC_OK; @@ -447,6 +468,9 @@ enum TN_RCode _tn_timer_cancel(struct TN_Timer *timer) return rc; } +/** + * See comments in the tn_internal.h file. + */ enum TN_RCode _tn_timer_create( struct TN_Timer *timer, TN_TimerFunc *func, @@ -468,6 +492,9 @@ enum TN_RCode _tn_timer_create( return rc; } +/** + * See comments in the tn_internal.h file. + */ enum TN_RCode _tn_timer_set_func( struct TN_Timer *timer, TN_TimerFunc *func, @@ -486,6 +513,9 @@ enum TN_RCode _tn_timer_set_func( return rc; } +/** + * See comments in the tn_internal.h file. + */ BOOL _tn_timer_is_active(struct TN_Timer *timer) { #if TN_DEBUG @@ -498,19 +528,9 @@ BOOL _tn_timer_is_active(struct TN_Timer *timer) return (!tn_is_list_empty(&(timer->timer_queue))); } -void _tn_timers_init(void) -{ - int i; - - //-- reset "generic" timers list - tn_list_reset(&tn_timer_list__gen); - - //-- reset all "tick" timer lists - for (i = 0; i < TN_TICK_LISTS_CNT; i++){ - tn_list_reset(&tn_timer_list__tick[i]); - } -} - +/** + * See comments in the tn_internal.h file. + */ TN_Timeout _tn_timer_time_left(struct TN_Timer *timer) { #if TN_DEBUG From efcd1e37137ef85d43a2cc6972473ef8e6ff4b51 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Sun, 12 Oct 2014 22:04:54 +0400 Subject: [PATCH 36/42] _tn_arch_stack_start_get() -> _tn_arch_stack_top_get(), and now it returns the top of the stack, that is, not the first usable address, but the previous one --- src/arch/pic32/tn_arch_pic32.c | 84 ++++++++++++++++------------------ src/arch/tn_arch.h | 25 ++++++---- src/core/tn_sys.c | 22 +++++++-- src/core/tn_tasks.c | 19 +++++--- src/core/tn_tasks.h | 8 ++-- 5 files changed, 91 insertions(+), 67 deletions(-) diff --git a/src/arch/pic32/tn_arch_pic32.c b/src/arch/pic32/tn_arch_pic32.c index a5f74b7..6bb3576 100644 --- a/src/arch/pic32/tn_arch_pic32.c +++ b/src/arch/pic32/tn_arch_pic32.c @@ -75,12 +75,12 @@ extern unsigned long _gp; // SP + 00 lo //---------------------------------------------------------------------------- -TN_UWord *_tn_arch_stack_start_get( +TN_UWord *_tn_arch_stack_top_get( TN_UWord *stack_low_address, int stack_size ) { - return stack_low_address + stack_size - 1; + return stack_low_address + stack_size; } @@ -91,53 +91,49 @@ TN_UWord *_tn_arch_stack_start_get( //---------------------------------------------------------------------------- unsigned int *_tn_arch_stack_init( TN_TaskBody *task_func, - TN_UWord *stack_start, + TN_UWord *stack_top, void *param ) { - unsigned int *p_stk; - //-- filling register's position in the stack - for debugging only + *(--stack_top) = 0; //-- ABI argument area + *(--stack_top) = 0; + *(--stack_top) = 0; + *(--stack_top) = 0; + *(--stack_top) = (unsigned int)task_func; //-- EPC + *(--stack_top) = 3; //-- Status: EXL and IE bits are set + *(--stack_top) = (unsigned int)tn_task_exit; //-- ra + *(--stack_top) = 0x30303030L; //-- fp + *(--stack_top) = (unsigned int)&_gp; //-- gp - provided by linker + *(--stack_top) = 0x25252525L; //-- t9 + *(--stack_top) = 0x24242424L; //-- t8 + *(--stack_top) = 0x23232323L; //-- s7 + *(--stack_top) = 0x22222222L; //-- s6 + *(--stack_top) = 0x21212121L; //-- s5 + *(--stack_top) = 0x20202020L; //-- s4 + *(--stack_top) = 0x19191919L; //-- s3 + *(--stack_top) = 0x18181818L; //-- s2 + *(--stack_top) = 0x17171717L; //-- s1 + *(--stack_top) = 0x16161616L; //-- s0 + *(--stack_top) = 0x15151515L; //-- t7 + *(--stack_top) = 0x14141414L; //-- t6 + *(--stack_top) = 0x13131313L; //-- t5 + *(--stack_top) = 0x12121212L; //-- t4 + *(--stack_top) = 0x11111111L; //-- t3 + *(--stack_top) = 0x10101010L; //-- t2 + *(--stack_top) = 0x09090909L; //-- t1 + *(--stack_top) = 0x08080808L; //-- t0 + *(--stack_top) = 0x07070707L; //-- a3 + *(--stack_top) = 0x06060606L; //-- a2 + *(--stack_top) = 0x05050505L; //-- a1 + *(--stack_top) = (unsigned int)param; //-- a0 - task's function argument + *(--stack_top) = 0x03030303L; //-- v1 + *(--stack_top) = 0x02020202L; //-- v0 + *(--stack_top) = 0x01010101L; //-- at + *(--stack_top) = 0x33333333L; //-- hi + *(--stack_top) = 0x32323232L; //-- lo - p_stk = (unsigned int *)stack_start; //-- Load stack pointer - *p_stk-- = 0; //-- ABI argument area - *p_stk-- = 0; - *p_stk-- = 0; - *p_stk-- = 0; - *p_stk-- = (unsigned int)task_func; //-- EPC - *p_stk-- = 3; //-- Status: EXL and IE bits are set - *p_stk-- = (unsigned int)tn_task_exit; //-- ra - *p_stk-- = 0x30303030L; //-- fp - *p_stk-- = (unsigned int)&_gp; //-- gp - provided by linker - *p_stk-- = 0x25252525L; //-- t9 - *p_stk-- = 0x24242424L; //-- t8 - *p_stk-- = 0x23232323L; //-- s7 - *p_stk-- = 0x22222222L; //-- s6 - *p_stk-- = 0x21212121L; //-- s5 - *p_stk-- = 0x20202020L; //-- s4 - *p_stk-- = 0x19191919L; //-- s3 - *p_stk-- = 0x18181818L; //-- s2 - *p_stk-- = 0x17171717L; //-- s1 - *p_stk-- = 0x16161616L; //-- s0 - *p_stk-- = 0x15151515L; //-- t7 - *p_stk-- = 0x14141414L; //-- t6 - *p_stk-- = 0x13131313L; //-- t5 - *p_stk-- = 0x12121212L; //-- t4 - *p_stk-- = 0x11111111L; //-- t3 - *p_stk-- = 0x10101010L; //-- t2 - *p_stk-- = 0x09090909L; //-- t1 - *p_stk-- = 0x08080808L; //-- t0 - *p_stk-- = 0x07070707L; //-- a3 - *p_stk-- = 0x06060606L; //-- a2 - *p_stk-- = 0x05050505L; //-- a1 - *p_stk-- = (unsigned int)param; //-- a0 - task's function argument - *p_stk-- = 0x03030303L; //-- v1 - *p_stk-- = 0x02020202L; //-- v0 - *p_stk-- = 0x01010101L; //-- at - *p_stk-- = 0x33333333L; //-- hi - *p_stk = 0x32323232L; //-- lo - - return p_stk; + return stack_top; } //_____________________________________________________________________________ diff --git a/src/arch/tn_arch.h b/src/arch/tn_arch.h index 983459b..8e12c59 100644 --- a/src/arch/tn_arch.h +++ b/src/arch/tn_arch.h @@ -105,13 +105,22 @@ void tn_arch_sr_restore(unsigned int sr); /** - * Should return start stack address, which may be either the lowest address of - * the stack array or the highest one, depending on the architecture. + * Should return top of the stack, which may be either: * - * @param stack_low_address start address of the stack array. - * @param stack_size size of the stack in `int`-s, not in bytes. + * - `(stack_low_address - 1)` + * - `(stack_low_address + stack_size)` + * + * (depending on the architecture) + * + * **NOTE** that returned *top of the stack* is NOT the address which may be + * used for storing the new data. Instead, it is the *previous* address. + * + * @param stack_low_address + * start address of the stack array. + * @param stack_size + * size of the stack in `#TN_UWord`-s, not in bytes. */ -TN_UWord *_tn_arch_stack_start_get( +TN_UWord *_tn_arch_stack_top_get( TN_UWord *stack_low_address, int stack_size ); @@ -121,8 +130,8 @@ TN_UWord *_tn_arch_stack_start_get( * * @param task_func * Pointer to task body function. - * @param stack_start - * Start address of the stack, returned by `_tn_arch_stack_start_get()`. + * @param stack_top + * Top of the stack, returned by `_tn_arch_stack_top_get()`. * @param param * User-provided parameter for task body function. * @@ -130,7 +139,7 @@ TN_UWord *_tn_arch_stack_start_get( */ unsigned int *_tn_arch_stack_init( TN_TaskBody *task_func, - TN_UWord *stack_start, + TN_UWord *stack_top, void *param ); diff --git a/src/core/tn_sys.c b/src/core/tn_sys.c index cdb6c45..2cf67ae 100644 --- a/src/core/tn_sys.c +++ b/src/core/tn_sys.c @@ -202,22 +202,31 @@ void tn_sys_start( int i; enum TN_RCode rc; - //-- Clear/set all globals (vars, lists, etc) - + //-- for each priority: + // - reset list of runnable tasks with this priority + // - reset time slice to `#TN_NO_TIME_SLICE` for (i = 0; i < TN_PRIORITIES_CNT; i++){ tn_list_reset(&(tn_ready_list[i])); tn_tslice_ticks[i] = TN_NO_TIME_SLICE; } + //-- reset generic task queue and task count to 0 tn_list_reset(&tn_create_queue); tn_created_tasks_cnt = 0; - tn_sys_state = (0); //-- no flags set + //-- initial system flags: no flags set (see enum TN_StateFlag) + tn_sys_state = (0); + //-- reset bitmask of prioritis with runnable tasks tn_ready_to_run_bmp = 0; + + //-- reset system time tn_sys_time_count = 0; + + //-- reset interrupt nesting count tn_int_nest_count = 0; + //-- reset pointers to currently running task and next task to run tn_next_task_to_run = NULL; tn_curr_run_task = NULL; @@ -229,8 +238,11 @@ void tn_sys_start( int_stack[i] = TN_FILL_STACK_VAL; } - //-- Pre-decrement stack - tn_int_sp = &(int_stack[int_stack_size]); + //-- pre-decrement stack + tn_int_sp = _tn_arch_stack_top_get( + int_stack, + int_stack_size + ); //-- init timers _tn_timers_init(); diff --git a/src/core/tn_tasks.c b/src/core/tn_tasks.c index a23a568..b177730 100644 --- a/src/core/tn_tasks.c +++ b/src/core/tn_tasks.c @@ -378,7 +378,6 @@ enum TN_RCode tn_task_create( enum TN_RCode rc; enum TN_Context context; - TN_UWord *ptr_stack; int i; //-- Lightweight checking of system tasks recreation @@ -422,11 +421,11 @@ enum TN_RCode tn_task_create( //--- Init task TCB task->task_func_addr = task_func; task->task_func_param = param; - task->stk_start = _tn_arch_stack_start_get( + task->base_stack_top = _tn_arch_stack_top_get( task_stack_low_addr, task_stack_size ); - task->stk_size = task_stack_size; + task->stack_size = task_stack_size; task->base_priority = priority; task->task_state = TN_TASK_STATE_NONE; task->id_task = TN_ID_TASK; @@ -437,8 +436,16 @@ enum TN_RCode tn_task_create( task->pwait_queue = NULL; //-- fill all task stack space by #TN_FILL_STACK_VAL - for (ptr_stack = task->stk_start, i = 0; i < task->stk_size; i++){ - *ptr_stack-- = TN_FILL_STACK_VAL; + { + TN_UWord *ptr_stack; + for ( + i = 0, ptr_stack = task_stack_low_addr; + i < task_stack_size; + i++ + ) + { + *ptr_stack++ = TN_FILL_STACK_VAL; + } } //-- reset task_queue (the queue used to include task to runqueue or @@ -1066,7 +1073,7 @@ void _tn_task_clear_dormant(struct TN_Task *task) // when not running task->task_stk = _tn_arch_stack_init( task->task_func_addr, - task->stk_start, + task->base_stack_top, task->task_func_param ); diff --git a/src/core/tn_tasks.h b/src/core/tn_tasks.h index 49322af..85357ba 100644 --- a/src/core/tn_tasks.h +++ b/src/core/tn_tasks.h @@ -208,11 +208,11 @@ struct TN_Task { #endif #endif - /// base address of task's stack space - TN_UWord *stk_start; + /// base top of the stack for this task + TN_UWord *base_stack_top; /// - /// size of task's stack (in `sizeof(unsigned int)`, not bytes) - int stk_size; + /// size of task's stack (in `sizeof(TN_UWord)`, not bytes) + int stack_size; /// /// pointer to task's body function given to `tn_task_create()` TN_TaskBody *task_func_addr; From 74c54b68b9af777cd2a4a1683abe9e24fb68b26a Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Mon, 13 Oct 2014 03:29:21 +0400 Subject: [PATCH 37/42] little fix in timer docs --- src/core/tn_timer.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/tn_timer.h b/src/core/tn_timer.h index a6f35a9..2bb5639 100644 --- a/src/core/tn_timer.h +++ b/src/core/tn_timer.h @@ -324,7 +324,7 @@ enum TN_RCode tn_timer_cancel(struct TN_Timer *timer); * User data pointer that is given to user-provided `func`. * * @return - * * `#TN_RC_OK` if timer was successfully created; + * * `#TN_RC_OK` if operation was successfull; * * `#TN_RC_WPARAM` if wrong params were given. */ enum TN_RCode tn_timer_set_func( @@ -346,7 +346,7 @@ enum TN_RCode tn_timer_set_func( * Pointer to `#BOOL` variable in which resulting value should be stored * * @return - * * `#TN_RC_OK` if timer was successfully created; + * * `#TN_RC_OK` if operation was successfull; * * `#TN_RC_WPARAM` if wrong params were given. */ enum TN_RCode tn_timer_is_active(struct TN_Timer *timer, BOOL *p_is_active); @@ -366,7 +366,7 @@ enum TN_RCode tn_timer_is_active(struct TN_Timer *timer, BOOL *p_is_active); * stored * * @return - * * `#TN_RC_OK` if timer was successfully created; + * * `#TN_RC_OK` if operation was successfull; * * `#TN_RC_WPARAM` if wrong params were given. */ enum TN_RCode tn_timer_time_left( From fffbeb8955f8501194297362ddc5ee9973f2ff53 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Tue, 14 Oct 2014 14:26:19 +0400 Subject: [PATCH 38/42] docs of timer and semaphore improved --- src/core/tn_sem.h | 14 +++++++++----- src/core/tn_timer.h | 15 +++++++++++++-- stuff/doc_pages/plans.dox | 4 +--- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/core/tn_sem.h b/src/core/tn_sem.h index acd5f21..55a4374 100644 --- a/src/core/tn_sem.h +++ b/src/core/tn_sem.h @@ -162,11 +162,13 @@ enum TN_RCode tn_sem_delete(struct TN_Sem *sem); /** * Signal the semaphore. * - * If current semaphore counter (`count`) is less than `max_count`, - * counter is incremented by one; otherwise, `#TN_RC_OVERFLOW` is returned. + * If current semaphore counter (`count`) is less than `max_count`, counter is + * incremented by one, and first task (if any) that \ref tn_sem_wait() "waits" + * for the semaphore becomes runnable with `#TN_RC_OK` returned from + * `tn_sem_wait()`. * - * If wait queue is not empty, the first task from the queue becomes runnable - * with `#TN_RC_OK` returned from `tn_sem_wait()`. + * if semaphore counter is already has its max value, no action performed and + * `#TN_RC_OVERFLOW` is returned * * $(TN_CALL_FROM_TASK) * $(TN_CAN_SWITCH_CONTEXT) @@ -198,7 +200,9 @@ enum TN_RCode tn_sem_isignal(struct TN_Sem *sem); * * If the current semaphore counter (`count`) is non-zero, it is decremented * and `#TN_RC_OK` is returned. Otherwise, behavior depends on `timeout` value: - * refer to `#TN_Timeout`. + * task might switch to $(TN_TASK_STATE_WAIT) state until someone \ref + * tn_sem_signal "signaled" the semaphore or until the `timeout` expired. refer + * to `#TN_Timeout`. * * $(TN_CALL_FROM_TASK) * $(TN_CAN_SWITCH_CONTEXT) diff --git a/src/core/tn_timer.h b/src/core/tn_timer.h index 2bb5639..d786ba4 100644 --- a/src/core/tn_timer.h +++ b/src/core/tn_timer.h @@ -38,9 +38,20 @@ * \file * * Timer: a kernel object that is used to ask the kernel to call some - * user-provided function at a particular time in the future, based on the + * user-provided function at a particular time in the future, based on the * $(TN_SYS_TIMER_LINK) tick. * + * If you need to repeatedly wake up particular task, you can create semaphore + * which you should \ref tn_sem_wait() "wait for" in the task, and \ref + * tn_sem_isignal() "signal" in the timer callback (remember that you should + * use `tn_sem_isignal()` in this callback, since it is called from an ISR). + * + * If you need to perform rather fast action (such as toggle some pin, or the + * like), consider doing that right in the timer callback, in order to avoid + * context switch overhead. + * + * The timer callback approach provides ultimate flexibility. + * * In the spirit of TNeoKernel, timers are as lightweight as possible. That's * why there is only one type of timer: the single-shot timer. If you need your * timer to fire repeatedly, you can easily restart it from the timer function @@ -49,7 +60,7 @@ * When timer fires, the user-provided function is called. Be aware of the * following: * - * - Function is called from ISR context (namely, from $(TN_SYS_TIMER_LINK) + * - Function is called from an ISR context (namely, from $(TN_SYS_TIMER_LINK) * ISR, by the `tn_tick_int_processing()`); * - Function is called with global interrupts disabled. * diff --git a/stuff/doc_pages/plans.dox b/stuff/doc_pages/plans.dox index c1135b1..50ceb6d 100644 --- a/stuff/doc_pages/plans.dox +++ b/stuff/doc_pages/plans.dox @@ -9,8 +9,6 @@ I have plans to implement some goodies not yet present in the kernel. Sometimes we need to wait, say, for messages from several queues simultaneously; currently, the kernel does not have built-in support of it. How I plan to implement it: there should be a way to *connect* an event group and custom events mask to the data queue. Then, data queue will maintain flag(s) specified by mask: when queue is non-empty, it will set flag(s) by mask, when queue becomes empty, it will clear these flag(s). -Then, we can connect single event group to several queues passing different flags, and wait for the messages from all of these queues with just a single call to `tn_eventgrp_wait()` and friends. When event happened, we just check which flags are set, and get message from appropriate queue. - -Probably it makes sense to connect event group to other objects as well, I haven't thought about this feature in detail yet. But, queue support is definitely must have. +Then, we can connect single event group to several queues passing different flags, and wait for the messages from all of these queues with just a single call to `tn_eventgrp_wait()`. When event happened, we just check which flags are set, and get message from appropriate queue. */ From 8c4d2ca9c6293db36fc1491b6481ab2502fb4d5f Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Tue, 14 Oct 2014 14:38:28 +0400 Subject: [PATCH 39/42] doxyfile improved: added images to LATEX_EXTRA_FILES tag, so that images are copied to the latex output dir --- stuff/doxygen/Makefile | 53 ++++++++++++++++++++++++++------------- stuff/doxygen/tn_doxyfile | 6 ++++- 2 files changed, 40 insertions(+), 19 deletions(-) diff --git a/stuff/doxygen/Makefile b/stuff/doxygen/Makefile index 9c13547..558b82a 100644 --- a/stuff/doxygen/Makefile +++ b/stuff/doxygen/Makefile @@ -4,58 +4,75 @@ define \n endef +# --- Images for bot html and latex documents +# +# NOTE: to make it work, images should be copied to the latex output directory: +# in your doxyfile, use the tag LATEX_EXTRA_FILES for that. + +export TN_CALL_FROM_TASK TN_CALL_FROM_TASK = \image html attr_call_task.png ${\n}\ \latexonly \ - \includegraphics{../../images/attr_call_task.png} \ + \includegraphics{attr_call_task.png} \ \endlatexonly \ +export TN_CALL_FROM_ISR TN_CALL_FROM_ISR = \image html attr_call_int.png ${\n} \ \latexonly \ - \includegraphics{../../images/attr_call_int.png} \ + \includegraphics{attr_call_int.png} \ \endlatexonly \ +export TN_CALL_FROM_MAIN TN_CALL_FROM_MAIN = #{call from main()} +export TN_CAN_SWITCH_CONTEXT TN_CAN_SWITCH_CONTEXT = \image html attr_call_ct_sw.png ${\n} \ \latexonly \ - \includegraphics{../../images/attr_call_ct_sw.png} \ + \includegraphics{attr_call_ct_sw.png} \ \endlatexonly \ - +export TN_CAN_SLEEP TN_CAN_SLEEP = \image html attr_timeout.png ${\n} \ \latexonly \ - \includegraphics{../../images/attr_timeout.png} \ + \includegraphics{attr_timeout.png} \ \endlatexonly \ + + +# --- Link to the legend + +export TN_LEGEND_LINK TN_LEGEND_LINK = (refer to \ref legend for details) + +# --- Link to the explanation of system timer + +export TN_SYS_TIMER_LINK TN_SYS_TIMER_LINK = \ref time_ticks "system timer" +# --- Links to task states + +export TN_TASK_STATE_RUNNABLE +export TN_TASK_STATE_WAIT +export TN_TASK_STATE_SUSPEND +export TN_TASK_STATE_WAITSUSP +export TN_TASK_STATE_DORMANT + TN_TASK_STATE_RUNNABLE = \link TN_TASK_STATE_RUNNABLE RUNNABLE\endlink TN_TASK_STATE_WAIT = \link TN_TASK_STATE_WAIT WAIT\endlink TN_TASK_STATE_SUSPEND = \link TN_TASK_STATE_SUSPEND SUSPEND\endlink TN_TASK_STATE_WAITSUSP = \link TN_TASK_STATE_WAITSUSP WAIT+SUSPEND\endlink TN_TASK_STATE_DORMANT = \link TN_TASK_STATE_DORMANT DORMANT\endlink -TN_VERSION = `bash ./hg_ver_echo.sh` -export TN_CALL_FROM_TASK -export TN_CALL_FROM_ISR -export TN_CALL_FROM_MAIN -export TN_CAN_SWITCH_CONTEXT -export TN_CAN_SLEEP +# --- Kernel version +# (we don't export it since it is included in the make "all" target, see below) + +TN_VERSION = `bash ./hg_ver_echo.sh` -export TN_LEGEND_LINK -export TN_SYS_TIMER_LINK -export TN_TASK_STATE_RUNNABLE -export TN_TASK_STATE_WAIT -export TN_TASK_STATE_SUSPEND -export TN_TASK_STATE_WAITSUSP -export TN_TASK_STATE_DORMANT all: ( cat tn_doxyfile ; echo "PROJECT_NUMBER=\"$(TN_VERSION)\"" ) | doxygen - diff --git a/stuff/doxygen/tn_doxyfile b/stuff/doxygen/tn_doxyfile index 4b2d86e..183090b 100644 --- a/stuff/doxygen/tn_doxyfile +++ b/stuff/doxygen/tn_doxyfile @@ -1659,7 +1659,11 @@ LATEX_FOOTER = # markers available. # This tag requires that the tag GENERATE_LATEX is set to YES. -LATEX_EXTRA_FILES = +LATEX_EXTRA_FILES = images/attr_call_task.png \ + images/attr_call_int.png \ + images/attr_call_ct_sw.png \ + images/attr_timeout.png + # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is # prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will From 3f8c8af7a1be93efbfd2a8a0b8f3be2f0e71536d Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Tue, 14 Oct 2014 18:15:09 +0400 Subject: [PATCH 40/42] create_version_archive.sh improved: files ignored by mercurial aren't included in the archive, and added bin folder --- stuff/doxygen/create_version_archive.sh | 54 ++++++++++++++++++++++--- stuff/vimwiki/how_to_release.wiki | 3 +- 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/stuff/doxygen/create_version_archive.sh b/stuff/doxygen/create_version_archive.sh index f7e43f8..3a60539 100644 --- a/stuff/doxygen/create_version_archive.sh +++ b/stuff/doxygen/create_version_archive.sh @@ -9,7 +9,50 @@ if [[ "$target_tag_name" == "" ]]; then exit 0 fi -# update to this revision +# go to root of the repo +cd ../.. + +# remember path to the repo +repo_path="$(pwd)" + +# generate path to temp repo dir +tmp_repo_path="/tmp/tneokernel_tmp_$target_tag_name" + +# remove it if it already exists +if test -d "$tmp_repo_path"; then + echo "removing $tmp_repo_path.." + rm -r "$tmp_repo_path" +fi + +# clone it from the current one +hg clone $repo_path $tmp_repo_path + +# generate pic32 hex {{{ + +pic32_bin="$tmp_repo_path/bin/pic32" + +pushd src/arch/pic32/tneokernel.X +make + +# in the target temp dir, create "bin" dir +mkdir -p "$pic32_bin" + +# copy hex there +cp dist/default/production/tneokernel.X.a "$pic32_bin" + +# cd back +popd + +# }}} + + +# cd to temp dir +cd $tmp_repo_path + +# cd to doxygen dir +cd stuff/doxygen + +# update the needed revision hg up $target_tag_name # get version string @@ -50,7 +93,7 @@ if test -d "$archive_full_name"; then fi # make directory that will be packed soon -mkdir "$archive_full_name" +mkdir -p "$archive_full_name" # copy data there rsync -av --exclude=".hg*" --exclude=".vimprj" . "$archive_full_name" @@ -59,7 +102,8 @@ rsync -av --exclude=".hg*" --exclude=".vimprj" . "$archive_full_name" cd "$archive_full_name" # remove tn_cfg.h -rm "$archive_full_name/src/tn_cfg.h" +# COMMENTED because repo is now cloned, and there's no tn_cfg.h +# rm "$archive_full_name/src/tn_cfg.h" # create doc dirs mkdir -p doc/{html,pdf} @@ -98,9 +142,9 @@ echo " resulting archive is saved as $archive_full_name.zip" echo "--------------------------------------------------------------------------" # go back -popd +#popd # update repo to the tip -hg up tip +#hg up tip diff --git a/stuff/vimwiki/how_to_release.wiki b/stuff/vimwiki/how_to_release.wiki index 095323e..c547a25 100644 --- a/stuff/vimwiki/how_to_release.wiki +++ b/stuff/vimwiki/how_to_release.wiki @@ -28,8 +28,9 @@ `$ bash ./publish_doc_dev.sh` * add link to docs index.html: - * `$ cd dfrank.bitbucket.org/tnkernel_api` + * `$ cd dfrank.bitbucket.org/tneokernel_api` * in the index.html, add link to new vX.XX dir docs + * `$ hg ci -m"index.html : added link to docs vX.XX"` * push docs: `$ hg push` * cd back to tneokernel repo: `$ cd ../..` From c7c1bda0427f04ad0cc9f1f370e575e36cd3300e Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Tue, 14 Oct 2014 18:31:57 +0400 Subject: [PATCH 41/42] The page 'differences from TNKernel API' updated: added timers there --- src/core/tn_timer.h | 2 +- stuff/doc_pages/tnkernel_diff.dox | 45 ++++++++++++++++++++++--------- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/src/core/tn_timer.h b/src/core/tn_timer.h index d786ba4..aeabe30 100644 --- a/src/core/tn_timer.h +++ b/src/core/tn_timer.h @@ -37,7 +37,7 @@ /** * \file * - * Timer: a kernel object that is used to ask the kernel to call some + * Timer is a kernel object that is used to ask the kernel to call some * user-provided function at a particular time in the future, based on the * $(TN_SYS_TIMER_LINK) tick. * diff --git a/stuff/doc_pages/tnkernel_diff.dox b/stuff/doc_pages/tnkernel_diff.dox index 5ec5be3..ead221c 100644 --- a/stuff/doc_pages/tnkernel_diff.dox +++ b/stuff/doc_pages/tnkernel_diff.dox @@ -130,6 +130,38 @@ For detailed API reference, refer to the `tn_eventgrp.h`. In original TNKernel, system functions refused to perform job and returned `#TERR_WRONG_PARAM` if `timeout` is 0, but it is actually neither convenient nor intuitive: it is much better if the function behaves just like `...polling()` version of the function. All TNeoKernel system functions allows timeout to be zero: in this case, function doesn't wait. +\section tnkernel_new_features New features + +\subsection tnkernel_new_features__timer Timers + +Support of timers was added since TNeoKernel 1.02. + +Timer is a kernel object that is used to ask the kernel to call some user-provided function at a particular time in the future, based on the $(TN_SYS_TIMER_LINK) tick. + +If you need to repeatedly wake up particular task, you can create semaphore which you should \ref tn_sem_wait() "wait for" in the task, and \ref tn_sem_isignal() "signal" in the timer callback. + +If you need to perform rather fast action (such as toggle some pin, or the like), consider doing that right in the timer callback, in order to avoid context switch overhead. + +The timer callback approach provides ultimate flexibility. + +For details, refer to the \ref tn_timer.h "timers documentation". + +\subsection tnkernel_diff_mutex_rec Recursive mutexes + +Sometimes I feel lack of mutexes that allow recursive locking. I know there are developers who believe that recursive locking leads to the code of lower quality, and I understand it. Even Linux kernel doesn't have recursive mutexes. + +Sometimes they are really useful though (say, if you want to use some third-party library that requires locking primitives to be recursive), so I decided to implement an option for that: `#TN_MUTEX_REC`. If it is non-zero, mutexes allow recursive locking; otherwise you get `#TN_RC_ILLEGAL_USE` when you try to lock mutex that is already locked by this task. Default value: `1`. + +\subsection tnkernel_diff_new_functions New system services added + +Several system services were added: + + - `tn_cur_task_get()` : return a pointer to the `struct TN_Task` of the currently running task; + - `tn_cur_task_body_get()` : return pointer to the currently running task body function; + - `tn_task_state_get()` : get state of the task. + + + \section tnkernel_new_api Compatible API changes \subsection tnkernel_diff_make_alig Macro MAKE_ALIG() @@ -152,12 +184,6 @@ But for compatibility with messy `MAKE_ALIG()` from original TNKernel, there is By the way, I wrote to the author of TNKernel (Yuri Tiomkin) about this mess, but he didn't answer anything. It's a pity of course, but we have what we have. -\subsection tnkernel_diff_mutex_rec Recursive mutexes - -Sometimes I feel lack of mutexes that allow recursive locking. Yeah I know there are developers who believe that recursive locking leads to the code of lower quality, and I understand it. Even Linux kernel doesn't have recursive mutexes. - -Sometimes they are really useful though (say, if you want to use some third-party library that requires locking primitives to be recursive), so I decided to implement an option for that: `#TN_MUTEX_REC`. If it is non-zero, mutexes allow recursive locking; otherwise you get `#TN_RC_ILLEGAL_USE` when you try to lock mutex that is already locked by this task. Default value: `1`. - \subsection tnkernel_new_api__convenience_macros_stack Convenience macros for stack arrays definition You can still use "manual" definition of stack arrays, like that: @@ -193,13 +219,6 @@ Actually, there's a lot of confusion about usage of mutexes/semaphores, so it's Old names (`tn_sem_acquire()` and friends) are still available through `tn_oldsymbols.h`. -\subsection tnkernel_diff_new_functions New system services added - -Several system services were added: - - - `tn_cur_task_get()` : return a pointer to the `struct TN_Task` of the currently running task; - - `tn_cur_task_body_get()` : return pointer to the currently running task body function; - - `tn_task_state_get()` : get state of the task. \section tnkernel_diff_other Changes that do not affect API directly From eab73b8e5f5483a009103fbe761a75333b38f9fa Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Tue, 14 Oct 2014 18:34:09 +0400 Subject: [PATCH 42/42] changelog is updated for release v1.02 --- stuff/doc_pages/changelog.dox | 4 +++- stuff/doc_pages/tnkernel_diff.dox | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/stuff/doc_pages/changelog.dox b/stuff/doc_pages/changelog.dox index 3514a00..7846f84 100644 --- a/stuff/doc_pages/changelog.dox +++ b/stuff/doc_pages/changelog.dox @@ -5,7 +5,9 @@ TNeoKernel changelog -\section changelog_current Current development version (BETA) +\section changelog_v1_02 v1.02 + +Release date: 2014-10-14. - Added \ref tn_timer.h "timers": kernel objects that are used to ask the kernel to call some user-provided function at a particular time in the diff --git a/stuff/doc_pages/tnkernel_diff.dox b/stuff/doc_pages/tnkernel_diff.dox index ead221c..90dce55 100644 --- a/stuff/doc_pages/tnkernel_diff.dox +++ b/stuff/doc_pages/tnkernel_diff.dox @@ -134,7 +134,7 @@ In original TNKernel, system functions refused to perform job and returned `#TER \subsection tnkernel_new_features__timer Timers -Support of timers was added since TNeoKernel 1.02. +Support of timers was added since TNeoKernel \ref changelog_v1_02. Timer is a kernel object that is used to ask the kernel to call some user-provided function at a particular time in the future, based on the $(TN_SYS_TIMER_LINK) tick.