Passing Arguments to a Thread

From Nutwiki
Revision as of 12:20, 2 August 2010 by AdrianPyka (Talk) (Test Environments)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Test Environments

Hardware Comments Nut/OS
4.8.7
Ethernut 1.3 H OK
Binaries
Compiler: AVR-GCC 4.3.2
Ethernut 2.1 B OK
Binaries
Compiler: AVR-GCC 4.3.2
Ethernut 3.0 E OK
Binaries
Compiler: ARM-GCC 4.3.3

Overview

In this example, we will discuss how to pass multiple arguments to a thread. This is especially useful if you have a thread that has to run in multiple instances with different parameters.

Source Code

<source lang="c">

  1. include <dev/board.h>
  2. include <stdio.h>
  3. include <io.h>
  1. include <sys/thread.h>
  2. include <sys/timer.h>

typedef struct {

   char *name;
   int some_number;
   int sleeptime;

} thread_args;

THREAD(MyThread, arg) {

   thread_args *my_args = (thread_args *) arg;
   for (;;) {
       printf("%s: The answer to the Ultimate Question of Life, the Universe, and Everything is %d.\n\n", my_args->name,
              my_args->some_number);
       my_args->some_number++;
       NutSleep(my_args->sleeptime);
   }

}

int main(void) {

   unsigned long baud = 115200;
   NutRegisterDevice(&DEV_DEBUG, 0, 0);
   freopen(DEV_DEBUG_NAME, "w", stdout);
   _ioctl(_fileno(stdout), UART_SETSPEED, &baud);
   puts("\nNut/OS Thread Argument Demo");
   puts("Creating first thread...");
   thread_args *arguments = malloc(sizeof(thread_args));
   arguments->name = "First Thread";
   arguments->some_number = 42;
   arguments->sleeptime = 10000;
   NutThreadCreate(arguments->name, MyThread, arguments, 512);
   puts("Creating second thread...");
   arguments = malloc(sizeof(thread_args));
   arguments->name = "Second Thread";
   arguments->some_number = 23;
   arguments->sleeptime = 7000;
   NutThreadCreate(arguments->name, MyThread, arguments, 512);
   for (;;) {
       NutSleep(1000);
   }
   return 0;

} </source>

Details

<source lang="c"> typedef struct {

   char *name;
   int some_number;
   int sleeptime;

} thread_args; </source>

The first thing you'll notice in this example is the structure thread_args which is used to pass arguments to a thread later on. It is important to note that this structure can be just about anything you may need to pass to the thread.

We fill it with a thread name, a number we want it to process and a sleeptime so the thread knows how long to sleep after "processing" the number. You can add other datatypes if you want to, the only limit here is your RAM.

Now before we to the thread function, let's first dissect the main function because that is where the real work in this example gets done.

<source lang="c"> int main(void) {

   unsigned long baud = 115200;
   NutRegisterDevice(&DEV_DEBUG, 0, 0);
   freopen(DEV_DEBUG_NAME, "w", stdout);
   _ioctl(_fileno(stdout), UART_SETSPEED, &baud);
   puts("\nNut/OS Thread Argument Demo");

</source>

As always, we register and open the debug device and output some name for our application.

<source lang="c">

   puts("Creating first thread...");
   thread_args *arguments = malloc(sizeof(thread_args));
   arguments->name = "First Thread";
   arguments->some_number = 42;
   arguments->sleeptime = 10000;
   NutThreadCreate(arguments->name, MyThread, arguments, 512);

</source>

Now let's create the first thread. First we allocate memory for a thread_args structure. Then we fill that structure with arguments we want the thread to work with, in this example there are 3 arguments in here:

The name, a number that will be output and then incremented by one and a time that the thread will wait after outputting its number.

Now we create a new thread. The first argument is the thread name, since we already have that in our structure we pull it from there. No need for entering the name in 2 places, right?

The second argument is the pointer for the function the thread should consist of. This is, of course, the MyThread function declared further above.

The third argument is the pointer to the structure with the thread arguments. Now this is the major difference to the normal multithreading example, where we just passed NULL. This time we pass the pointer to the structure we just allocated and filled with arguments. Lucky for us a variable that contains a structure is in reality only a pointer to the beginning of the structure, as this function can only work with pointers. The final argument is the stack size. We don't need too much here, 512 should be fine.

<source lang="c">

   puts("Creating second thread...");
   arguments = malloc(sizeof(thread_args));
   arguments->name = "Second Thread";
   arguments->some_number = 23;
   arguments->sleeptime = 7000;
   NutThreadCreate(arguments->name, MyThread, arguments, 512);

</source>

Alright, we have one thread but what good would a parameter example be without showing off different parameters? Right. So we allocate a NEW spot in memory for ANOTHER thread_args structure and store that in the arguments variable.

The other thread already knows where to expect its arguments and it stores a copy of that pointer so we can just store something differnt in the arguments variable.

We fill it with some other data, construct a second thread and, again, point it to the arguments structure.

<source lang="c">

   for (;;) {
       NutSleep(1000);
   }
   return 0;

</source>

The rest of the main function is pretty dull, it just loops endlessly through 1 second sleeps. Real applications may want to serve a watchdog here or something, we don't.

<source lang="c"> THREAD(MyThread, arg) {

   thread_args *my_args = (thread_args *) arg;
   for (;;) {
       printf("%s: The answer to the Ultimate Question of Life, the Universe, and Everything is %d.\n\n", my_args->name,
              my_args->some_number);
       my_args->some_number++;
       NutSleep(my_args->sleeptime);
   }

} </source>

Now this is the thread function which will be running inside both of our threads. Notice how it takes an argument named arg. This argument is a void pointer, meaning it can be ANYTHING and you, the programmer, have to take care of handling this data directly.

Lucky for us, we know exactly what we will pass. We declare a local variable my_args for our thread and cast the void* to a thread_args* in order for C to know how to handle it. Now we can access it like a structure.

Now in the threads main loop we output the data in the structure, increment its number by one and then send the thread to sleep for a set time.

Note: Do not pass thread arguments from local variables! They will not work outside of the function they were used in and threads are always outside of those functions. That's why we allocate the memory for the arguments. This way they will be on the heap and thereby globally accessible.

Output

<source lang="text">

Nut/OS Thread Argument Demo Creating first thread... Creating second thread... First Thread: The answer to the Ultimate Question of Life, the Universe, and Eve rything is 42.

Second Thread: The answer to the Ultimate Question of Life, the Universe, and Ev erything is 23.

Second Thread: The answer to the Ultimate Question of Life, the Universe, and Ev erything is 24.

First Thread: The answer to the Ultimate Question of Life, the Universe, and Eve rything is 43. </source>

See also