Author Topic: the timers created at task creation time do not send messages upon fire  (Read 31193 times)

Offline RickSustek

  • Newbie
  • *
  • Posts: 2
    • View Profile
Is that correct?

The documentation implies that all timer fires generate message queue content.
But examination and step-through of the code in utasker.c clearly shows that a timer created in uTaskerStart() has an event of '0', which will not cause message generation within uTaskerSchedule().

Thanks!
-Rick

Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3244
    • View Profile
    • uTasker
Hi Rick

Monostable timers and global software timers (based on monostable timers) always work with timer events.
Delayed and repetitive tasks do not work with timer events, then simply schedule the task.

Could you point me to the documentation that suggests that repetitive tasks generate timer events so that I can ensure that it is not misleading?

I am however wondering whether it may be useful to specify a timer event for a repetitive task. Repetitive tasks are usually very simple tasks (like polling a keyboard and not needing to know why scheduled) - optionally it may be possible to define a timer event specifically stating that the task has been scheduled due to this, making a mix of simple repetitive function and more complicated operations simpler: At the moment this is done by using uTaskerMonoTimer() to start the next period.

Regards

Mark



Offline RickSustek

  • Newbie
  • *
  • Posts: 2
    • View Profile
Thanks for the confirmation!

Re: documentation,
In the file "uTaskerV1.3.pdf", on page 9, it says:

"
Each task in the system can have one mono-stable timer. This mono-stable timer is automatically allocated when the task is defined to start delayed, or to run periodically, since the ?Tasker uses this mono-stable timer to achieve this task. Should the task code want to make use of a mono-stable timer during normal operation it needs to ensure that the task receives the timer resources by defining a start delay or else by setting the delay value NO_DELAY_RESERVE_MONO (maximum possible value), which will cause the resource to be allocated without actually performing a delay.
The timer can then be used as a mono-stable timer, which is practical for most applications and especially for monitoring protocol timeouts. The timer can be started and restarted (retriggered) and stopped. Should it time out, it will cause the task to be woken with a timer event message to recognise the cause.
"


Since it does not explicitly say that no event message is sent when a "start delay" or "periodic" timer goes off, I felt that it implied there would always be a message.

The main reason to receive a message from a timer, is to allow a task to distinguish why it was allowed to run, IF there are multiple reasons. I agree that for many simple "periodic" tasks, there may be no need.

As it works now, you can still implement a periodic task that also receives messages. If your message queue was empty, you could assume that your periodic timer had fired. So, I don't see any big need to change the design.

Just for fun, I added the following lines into the uTaskerStart() function, to set an event code to associate with the timer:

#ifdef INITIAL_TIMERS_GENERATE_EVENT_MESSAGE        // Rick's custom config option
tTimerList->ucEvent = -1;
#endif

This does cause a message to be sent. But, a problem with this idea is that the event code is normally a user-specified code, when a timer is explicitly created. A '-1' is not likely to be expected by any task... but you never know!

Thx!
-Rick

Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3244
    • View Profile
    • uTasker
Hi Rick

The only problem with the assumption is when a message is received at the same time; in this case the periodic scheduling would be missed (since there is something in the queue).

I was thinking that 0xff could be reserved as a timer event when a task is started with a delay and 0xfe when the task is periodically scheduled (as long as a task is configured with queue it will be sent - if no queue of course no event so still suitable for the simplest of tasks).
These values could be reserved for these uses, with corresponding event names - I don't think that this will cause any problems since one usually starts local timer defines with 0x01 and up. I've never seen a single task needing more than a handfull of different timer events -even when doing complicated stuff. I'll have a look and then there may be a document update anyway...

Regards

Mark



Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3244
    • View Profile
    • uTasker
Rick

I am presently testing the following:

Code: [Select]
// This is normally called from within a forever loop in main()
//
#ifdef MULTISTART
    extern MULTISTART_TABLE *uTaskerSchedule(void)
#else
    extern void uTaskerSchedule(void)
#endif
{
    TTASKTABLE *ptTaskTable = tTaskTable;                                // set at start and work down to bottom
    while (ptTaskTable->pcTaskName) {                                    // for each task in the task table
        if (ptTaskTable->ucTaskState & (UTASKER_GO | UTASKER_ACTIVATE)) {// if the task is to be scheduled
#ifdef MONITOR_PERFORMANCE                                               // {15}
            fnTimeCheck(ptTaskTable);                                    // start measuring the duration of the next task
#endif
            uDisable_Interrupt();                                        // protect from interrupts during this check
            if ((ptTaskTable->TaskRepetition) || (!(ptTaskTable->ucTaskState & UTASKER_GO))) {
#ifdef PERIODIC_TIMER_EVENT                                              // {16}
                unsigned char timer_message[HEADER_LENGTH];              // timer event message space
                if (ptTaskTable->ucEvent != 0) {                         // if woken from monostable timer delay, set write event to queue if there is one
                    timer_message[MSG_TIMER_EVENT] = ptTaskTable->ucEvent;// event number
                    ptTaskTable->ucEvent = 0;                            // event handled so reset it
                }
                else {                                                   // periodic timer or initial task delay
                    if (ptTaskTable->TaskDelay != 0) {                   // initial delay
                        timer_message[MSG_TIMER_EVENT] = TIMER_EVENT_INITIAL_DELAY;
                        ptTaskTable->TaskDelay = 0;                      // this happens only once so clear the delay
                    }
                    else if (ptTaskTable->TaskRepetition != 0) {
                        timer_message[MSG_TIMER_EVENT] = TIMER_EVENT_PERIODIC;
                    }
                }
                if (ptTaskTable->QueLength != 0) {                       // ignore if task has no queue
                    timer_message[MSG_DESTINATION_NODE] = INTERNAL_ROUTE;// internal event
                    timer_message[MSG_SOURCE_NODE] = INTERNAL_ROUTE;
                    timer_message[MSG_DESTINATION_TASK] = *(ptTaskTable->pcTaskName); // event to this task
                    timer_message[MSG_SOURCE_TASK] = TIMER_EVENT;        // event type
    #ifdef DELAYED_TIMER_EVENT
                    fnWrite(INTERNAL_ROUTE, timer_message, HEADER_LENGTH); // queue timer event in task queue
    #else
                    if (TIMER_EVENT_INITIAL_DELAY != timer_message[MSG_TIMER_EVENT]) {
                        fnWrite(INTERNAL_ROUTE, timer_message, HEADER_LENGTH); // queue timer event in task queue
                    }
    #endif
                }
#else
                if (ptTaskTable->ucEvent != 0) {                         // if woken from monostable timer delay, set write event to queue if there is one
                    unsigned char timer_message[HEADER_LENGTH];          // timer event message space
                    timer_message[MSG_DESTINATION_NODE] = INTERNAL_ROUTE;// internal event
                    timer_message[MSG_SOURCE_NODE] = INTERNAL_ROUTE;
                    timer_message[MSG_DESTINATION_TASK] = *(ptTaskTable->pcTaskName); // event to this task
                    timer_message[MSG_SOURCE_TASK] = TIMER_EVENT;        // event type
                    timer_message[MSG_TIMER_EVENT] = ptTaskTable->ucEvent;// event number
                    fnWrite(INTERNAL_ROUTE, timer_message, HEADER_LENGTH);// queue timer event in task queue
                    ptTaskTable->ucEvent = 0;                            // event handled so reset it
                }
#endif
                if (!(ptTaskTable->ucTaskState & UTASKER_SUSPENDED)) {   // check whether this task is suspended
                    ptTaskTable->ucTaskState = UTASKER_STOP;             // as long as it is not suspended, we stop it so that it is only scheduled this once
                }
            }
            uEnable_Interrupt();                                         // enable interrupts before calling task
            ptTaskTable->ptrTaskEntry(ptTaskTable);                      // call the task
        }
#ifdef MONITOR_PERFORMANCE                                               // {15}
        fnTimeCheck(0);                                                  // stop measuring the duration of last task and start measuring the idle phase
#endif
        ptTaskTable++;
    }
#ifdef MULTISTART
    return ptMultiStartTable;                                            // return the pointer to next configuration
#endif
}

In uTasker.h I added two defines:
Code: [Select]
#define TIMER_EVENT_PERIODIC      0xff                                   // timer event sent to task when periodicall scheduled (when PERIODIC_TIMER_EVENT is enabled and task has queue)
#define TIMER_EVENT_INITIAL_DELAY 0xfe                                   // timer event sent to task when scheduled for first time after a delay (when PERIODIC_TIMER_EVENT and DELAYED_TIMER_EVENT are enabled and task has queue)

To activate this there are two defines:
PERIODIC_TIMER_EVENT
DELAYED_TIMER_EVENT


When a task has an input queue the timer event TIMER_EVENT_INITIAL_DELAY is sent once when a delayed start takes place and TIMER_EVENT_PERIODIC on each periodic scheduling.

DELAYED_TIMER_EVENT is a sub-option since I found that some tasks (like tcp.c in its present form) will make an error when scheduled with TIMER_EVENT_INITIAL_DELAY (it assumes that it needs to poll and will crash since it hasn't initialised its sessions yet). With a few changes all tasks can however be made compatibile to also accept this.

Maybe you can use this too (?)

Regards

Mark