Saying Hi, and looking for help in re-starting a thread

Saubona and Hello,
My first post here, so be gentle please. I am still feeling my way about ARM, Mbed and this environment, site and forum.
I have set up some blurb as an intro in my profile. I am trying to get a little up the ARm learning curve using MBed Studio 1.1.0, and I got a Nucleo-F446ZE to play with a few days ago.
I have some basic stuff working, blinky, ADC, buttons etc, but now I am experimenting with threads, but hit my head in trying to re-start a thread that ended.
The full code is appended, but the issue looks like it is here:-
printf (“status A: %d \n”, thread.get_state());

    if (thread.get_state() == Thread::Deleted)

    {

        printf ("status B: %d \n", thread.get_state());

        thread.start(led2_thread); // kick off the thread again 

    }

oops - I seem to post before I was ready to. As I said I am starting out here . . .

. . . to continue
I am trying to use if (thread.get_state() == Thread::Deleted) to see if the thread has ended, and it seems to work.
I then use
thread.start(led2_thread); // kick off the thread again to try to kick it off again but that seems to be the bit that does not work, It does not seem to restart.

I am wondering the best way to check if a thread has ‘died’ and then kick it off again.

Beginner question ?

Peter

Full code :-
#include “mbed.h”

// ============= global definitions ============

// create two LED objects

DigitalOut led1(LED1);

DigitalOut led2(LED2);

// create a thread

Thread thread;

// create a procedure that will be run as a thread

void led2_thread()

{

// loop 20 times and then die 

for (int i = 0; i < 21  i++)

{

    led2 = !led2; // toggle the LED  

    ThisThread::sleep_for(2500); // take a nap

}

}

// main entry point - will run as a thread

int main()

{

Timer t                        // create a timer to terminate 

t.start()                      // start the timer 

thread.start(led2_thread);      // kick off the thread first time

while (true) 

{

    led1 = !led1;               // toggle the LED 

    ThisThread::sleep_for(1000)  

    printf ("status A: %d \n", thread.get_state());

    if (thread.get_state() == Thread::Deleted)

    {

        printf ("status B: %d \n", thread.get_state());

        thread.start(led2_thread); // kick off the thread again 

    }

    

    if ( chrono::duration_cast<chrono::seconds>(t.elapsed_time()).count() > 100 ) // die with dignity

    {

        printf ("status C: %d \n", thread.get_state());

        led1.write(0)  

        led2.write(0)  

        break  

    }

}

}

Hello Peter,

  • already published post can be still edited :slight_smile:
  • for better reading of your code, please be so kind and use following method
```
   //your code
```

I am not professional but I think that is normal behavior. Once thread ends , it can’t be started again. You must create new one instead or make a rules inside the thread.

BR, Jan

Thanks for the help, my post was a bit of a mess - still coming to grips with this forum !!
Thanks for the posting tips - needed :grinning:
Okay - I will try to re-create a thread in main and see what happens.

Aah - so that’s how to edit.

But it seems, from some research, that threads are more complex that I thought. More research needed - but that’s why I am here

Creating and deleting threads is expensive, I wouldn’t do it. You can add a loop and wait for a semaphore. After the action inside the thread has finished, it will wait at the semaphore for the next trigger. This blocking will not consume cpu power.
Another way is to schedule functions as events in an EventQueue, this will work even in bare-metal. With the limit that you have only one thread to handle the queue events.

Thanks for this.
I did some research and what I found agrees with this.
I am still trying to come to grips with all these things to assist with threading like semaphores flags and queues. Looks like good stuff to know
In the meantime I experimented with just using volatile variables to do the same thing, but I am concerned that even the use of volatile global variables may not be thread safe.

But my next iteration looked as included.

Thanks

Now to work out how to post code properly . . . .

What was meant by … and … ? I tried but it was a bit of a mess !!

And on the topic of being new to this forum, and ARM and Mbed, I have been compiling a few documents on the background to why I am trying to get up the ARM/Mbed learning curve, and my experiences so far.

My attempts at threading are discussed in Doc 09 - First steps in threading.

The docs were started to try to get a bit of interest in this environment at the Durban Makerspace, but there is not much going on there thanks to nasty Covid-19

I have just dumped the stuff here. Not sure if there is benefit or interest in this, and if there is what to do.

https://drive.google.com/drive/folders/1TE2zPuoRhSrlRVjCaqT6ffAX3j7rIHkm?usp=sharing

Hello Peter,

Now to work out how to post code properly . . . .
What was meant by … and … ? I tried but it was a bit of a mess !!

Those are back-tick characters rather than dots. On my keyboard the related key is located in the top-left corner below the Esc. But you can also copy them from Jan’s post and paste into yours.

Best regards, Zoltan

Ngiyabonga uZoltan - Thanks !

I think I may have it . . This was my attempt at interacting with a thread, although I am still not sure if using volatile variables makes it safe.


   #include "mbed.h"
// ============= global definitions ============ 

volatile bool toToggle ;        // flag to enable or disable toggling of LED2  
volatile uint8_t toggleCount ;  // toggle count of LED2 

// procedure that will be run  as a thread 
// toggle toggleCount times and then stop 
void led2_thread()
{
    DigitalOut led2(LED2);      // create a LED object for this thread 

    while (true)                // this thread will run forever
    {
        if (toToggle)           // is toggling enabled ? 
        {
            if (toggleCount  > 0)  // are there still toggles to do ? 
            {
                printf ("status led2_thread: %d \n", toggleCount); // diagnostic output to console 
                led2 = !led2;           // toggle the LED  
                toggleCount--  ;        // update toggle count 
                if (toggleCount < 1 )   // has it done it's toggling ? 
                    toToggle = false ; 
            }
        } 
        ThisThread::sleep_for(1000) ;       
    }
}

// main entry point - will run as a thread 
int main()
{
    Thread thread;              // create a thread 
    DigitalOut led1(LED1);      // create a LED object for this thread 

    uint8_t loopCount = 0 ;     // used to stop the process 
    toggleCount = 20 ;          // initial count 
    toToggle = true ;           // enable toggling 

    thread.start(led2_thread);  // kick off the thread 

    while (true) 
    {
        led1 = !led1;           // toggle the LED 
        if (toToggle == false)  // this will be set false in the thread 
        {
            toggleCount = 20 ;  // reset counter  
            toToggle = true ;   // enable toggling in the thread  
        }
        ThisThread::sleep_for(1200) ; 
        loopCount++  ; 
        if (loopCount > 75)
            return 0;           // stop the whole thing - rest the board !! 
    }
}

I need to learn about the various semaphores and those things.

Hello Peter,

A common scenario when writing multithreaded code is to protect shared resources, for example global volatile variables shared by threads, with a mutex (or semaphore) and then release that mutex to wait for a change of that data as suggested by Johannes. If you do not do this carefully, this can lead to a race condition in the code. A Condition Variable provides a safe solution to this problem by handling the wait for a state change, along with releasing and acquiring the mutex automatically during this waiting period.

#include "mbed.h"
// ============= global definitions ============
Mutex               mutex;
ConditionVariable   cond(mutex);

// volatile shared variables
volatile bool       toToggle;       // flag to enable or disable toggling of LED2
volatile uint8_t    toggleCount;    // toggle count of LED2

// procedure that will be run  as a thread

// toggle toggleCount times and then stop
void led2_thread()
{
    DigitalOut  led2(LED2);             // create a LED object for this thread

    while (true) {
        // this thread will run forever
        mutex.lock();                   // about to access shared variables -> lock the mutex
        if (toToggle) {
            // is toggling enabled ?
            if (toggleCount > 0) {
                // are there still toggles to do ?
                printf("status led2_thread: %d \n", toggleCount);
                led2 = !led2;           // toggle the LED
                toggleCount--;          // update toggle count
                if (toggleCount < 1)    // has it done it's toggling ?
                    toToggle = false;
            }
        }

        cond.notify_all();              // notify all waiters on this condition variable that a condition changed.
                                        // unblocks all of the threads waiting for the condition variable.
        mutex.unlock();                 // unlock the mutex

        ThisThread::sleep_for(1000);
    }
}

// main entry point - will run as a thread
int main()
{
    Thread      thread;         // create a thread
    DigitalOut  led1(LED1);     // create a LED object for this thread
    uint8_t     loopCount = 0;  // used to stop the process

    toggleCount = 20;           // initial count
    toToggle = true;            // enable toggling
    thread.start(led2_thread);  // kick off the thread
    mutex.lock();               // about to access shared variables -> lock the mutex        
    while (true) {
        led1 = !led1;           // toggle the LED
        cond.wait();            // wait for a condition to change
                                // Wait causes the current thread to block until the condition variable receives a notification from another thread.
                                // The current thread releases the mutex while inside the wait function and reacquires it upon exiting the function.
        if (toToggle == false) {// this will be set false in the thread
            toggleCount = 20;   // reset counter
            toToggle = true;    // enable toggling in the thread
        }

        ThisThread::sleep_for(1200);
        loopCount++;
        if (loopCount > 75)
            return 0;           // stop the whole thing - rest the board !!
    }
}

Best regards, Zoltan

Thanks so much for this.
I must research these concepts - It’s great to know they exist - now to learn them
Peter

Hi Peter

welcome to the forum! I’m sure your experiences of getting to grips with Arm and Mbed could be of use to other people new to both, so please don’t be afraid to share your musings.

Regards
Anna

Thank you,

My “musings” have come to a bit of a standstill. I am arm-wrestling with i2c and a PCA9685 module, and at the moment the module is winning hands-down.

But if, and when, I get my “servo-farm” seedlings wiggling I will continue with the musings - I was having fun.

And I am being distracted by some people who want me to do some work they say will make me rich and famous. :rofl:

Thanks

Peter
:south_africa: :south_africa: :south_africa: :south_africa: :south_africa: