Working with threads is hard.

Ecore helps to do so a bit easier, but as the example in \hyperlink{ecore_thread_example_8c-example}{ecore\+\_\+thread\+\_\+example.c} shows, there\textquotesingle{}s a lot to consider even when doing the most simple things.

We\textquotesingle{}ll be going through this thorough example now, showing how the differents aspects of \hyperlink{group__Ecore__Thread__Group_ga0c2e1f8d562ccf80ecc0009ada72496f}{Ecore\+\_\+\+Thread} are used, but users are encourage to avoid threads unless it\textquotesingle{}s really the only option, as they always add more complexity than the program usually requires.

Ecore Threads come in two flavors, short jobs and feedback jobs. Short jobs just run the given function and are more commonly used for small tasks where the main loop does not need to know how the work is going in between. The short job in our example is so short we had to artificially enlarge it with {\ttfamily sleep()}. Other than that, it also uses threads local data to keep the data we are working with persistent across different jobs ran by the same system thread. This data will be freed when the no more jobs are pending and the thread is terminated. If the data doesn\textquotesingle{}t exist in the thread\textquotesingle{}s storage, we create it and save it there for future jobs to find it. If creation fails, we cancel ourselves, so the main loop knows that we didn\textquotesingle{}t just exit normally, meaning the job could not be done. The main part of the function checks in each iteration if it was canceled by the main loop, and if it was, it stops processing and clears the data from the storage (we assume {\ttfamily cancel} means no one else will need this, but this is really application dependent).  
\begin{DoxyCodeInclude}
\textcolor{keyword}{static} \textcolor{keywordtype}{void}
\_local\_data\_free(\textcolor{keywordtype}{void} *data)
\{
   Thread\_Data *td = data;
   \textcolor{keywordtype}{char} *str;

   \hyperlink{group__Eina__List__Group_gae27a2c686a0a11693f9b06957c9bba83}{EINA\_LIST\_FREE}(td->list, str)
     \{
        printf(\textcolor{stringliteral}{"Freeing string: %s\(\backslash\)n"}, str);
        free(str);
     \}
   free(td);
\}

\textcolor{keyword}{static} \textcolor{keywordtype}{void}
\_short\_job(\textcolor{keywordtype}{void} *data EINA\_UNUSED, \hyperlink{group__Ecore__Thread__Group_ga0c2e1f8d562ccf80ecc0009ada72496f}{Ecore\_Thread} *th)
\{
   Thread\_Data *td;
   \textcolor{keywordtype}{int} i;

   td = \hyperlink{group__Ecore__Thread__Group_ga1a937d674f1a20162b31b95d0ed100ad}{ecore\_thread\_local\_data\_find}(th, \textcolor{stringliteral}{"data"});
   \textcolor{keywordflow}{if} (!td)
     \{
        td = calloc(1, \textcolor{keyword}{sizeof}(Thread\_Data));
        \textcolor{keywordflow}{if} (!td)
          \{
             \hyperlink{group__Ecore__Thread__Group_gae9b9b20e1ed53c62c51617b55aed2511}{ecore\_thread\_cancel}(th);
             \textcolor{keywordflow}{return};
          \}
        \hyperlink{group__Ecore__Thread__Group_ga6878f550a9bcc8002c0c709b4b4dca66}{ecore\_thread\_local\_data\_add}(th, \textcolor{stringliteral}{"data"}, td, \_local\_data\_free,
                                    \hyperlink{group__Eina__Types__Group_ga1feb115f8e9913e806e090d9bd5a7301}{EINA\_FALSE});
     \}

   \textcolor{keywordflow}{for} (i = 0; i < 10; i++)
     \{
        \textcolor{keywordtype}{char} buf[200];

        \textcolor{keywordflow}{if} (\hyperlink{group__Ecore__Thread__Group_ga792471a7ed00718b71d58b791ddbd08d}{ecore\_thread\_check}(th))
          \{
             \hyperlink{group__Ecore__Thread__Group_gad3c1d51a24772b885b26486be86c2779}{ecore\_thread\_local\_data\_del}(th, \textcolor{stringliteral}{"data"});
             \textcolor{keywordflow}{break};
          \}

        snprintf(buf, \textcolor{keyword}{sizeof}(buf), \textcolor{stringliteral}{"Thread %p: String number %d"}, th, i);
        td->list = \hyperlink{group__Eina__List__Group_ga92ec14d1a1040a361c4d8cd0ab7b0ecc}{eina\_list\_append}(td->list, strdup(buf));
        sleep(1);
     \}
\}

\end{DoxyCodeInclude}


Feedback jobs, on the other hand, run tasks that will inform back to the main loop its progress, send partial data as is processed, just ping saying it\textquotesingle{}s still alive and processing, or anything that needs the thread to talk back to the main loop. 
\begin{DoxyCodeInclude}
\textcolor{keyword}{static} \textcolor{keywordtype}{void}
\_feedback\_job(\textcolor{keywordtype}{void} *data EINA\_UNUSED, \hyperlink{group__Ecore__Thread__Group_ga0c2e1f8d562ccf80ecc0009ada72496f}{Ecore\_Thread} *th)
\{
   time\_t t;
   \textcolor{keywordtype}{int} i, count;
   Feedback\_Thread\_Data *ftd = NULL;
\textcolor{preprocessor}{#ifdef \_WIN32}
   HANDLE h;
   WIN32\_FIND\_DATA fd;
\textcolor{preprocessor}{#else}
   \hyperlink{group__Evil__Dirent__Group_ga49eb6ac4643553d04b85c70646c6786d}{DIR} *dir;
\textcolor{preprocessor}{#endif}
   App\_Msg *msg;

   count = (int)(uintptr\_t)\hyperlink{group__Ecore__Thread__Group_ga011eeb2f99aaa950089ddc6ea989da29}{ecore\_thread\_global\_data\_find}(\textcolor{stringliteral}{"count"});
   \textcolor{keywordflow}{for} (i = 0; i < count; i++)
     \{
        \textcolor{keywordtype}{char} buf[32];
        snprintf(buf, \textcolor{keyword}{sizeof}(buf), \textcolor{stringliteral}{"data%d"}, i);
        ftd = \hyperlink{group__Ecore__Thread__Group_ga011eeb2f99aaa950089ddc6ea989da29}{ecore\_thread\_global\_data\_find}(buf);
        \textcolor{keywordflow}{if} (!ftd)
          \textcolor{keywordflow}{continue};
        \textcolor{keywordflow}{if} (\hyperlink{group__Eina__Lock__Group_ga13c8270c1589bfd4770ee4cd43977bc5}{eina\_lock\_take\_try}(&ftd->mutex))
          \textcolor{keywordflow}{break};
        \textcolor{keywordflow}{else}
          ftd = NULL;
     \}
   \textcolor{keywordflow}{if} (!ftd)
     \textcolor{keywordflow}{return};

\textcolor{preprocessor}{#ifdef \_WIN32}
   h = FindFirstFile(ftd->base, &fd);
   \textcolor{keywordflow}{if} (h == INVALID\_HANDLE\_VALUE)
     \textcolor{keywordflow}{goto} the\_end;
\textcolor{preprocessor}{#else}
   dir = \hyperlink{group__Evil__Dirent__Group_ga0bfba238260bb94aa679f4aa63ec5e16}{opendir}(ftd->base);
   \textcolor{keywordflow}{if} (!dir)
     \textcolor{keywordflow}{goto} the\_end;
\textcolor{preprocessor}{#endif}

   msg = calloc(1, \textcolor{keyword}{sizeof}(App\_Msg));

   t = time(NULL);
   \textcolor{keywordflow}{while} (time(NULL) < t + 2)
     \{
\textcolor{preprocessor}{#ifdef \_WIN32}
        \textcolor{keywordflow}{if} (strlen(fd.cFileName) >= 10)
          msg->list = \hyperlink{group__Eina__List__Group_ga92ec14d1a1040a361c4d8cd0ab7b0ecc}{eina\_list\_append}(msg->list,
                                       strdup(fd.cFileName));
        FindNextFile(h, &fd);
\textcolor{preprocessor}{#else}
        \textcolor{keyword}{struct }\hyperlink{structdirent}{dirent} entry, *result;

        \textcolor{keywordflow}{if} (readdir\_r(dir, &entry, &result))
          \textcolor{keywordflow}{break};
        \textcolor{keywordflow}{if} (!result)
          \textcolor{keywordflow}{break};

        \textcolor{keywordflow}{if} (strlen(result->\hyperlink{structdirent_a3a1b19d864bfcb427dc05410c1a64721}{d\_name}) >= 10)
          msg->list = \hyperlink{group__Eina__List__Group_ga92ec14d1a1040a361c4d8cd0ab7b0ecc}{eina\_list\_append}(msg->list,
                                       strdup(result->\hyperlink{structdirent_a3a1b19d864bfcb427dc05410c1a64721}{d\_name}));
\textcolor{preprocessor}{#endif}
\textcolor{preprocessor}{     \}}

\end{DoxyCodeInclude}


Finally, one more feedback job, but this one will be running outside of Ecore\textquotesingle{}s pool, so we can use the pool for real work and keep this very light function unchecked. All it does is check if some condition is met and send a message to the main loop telling it it\textquotesingle{}s time to close. 
\begin{DoxyCodeInclude}
\textcolor{keyword}{static} \textcolor{keywordtype}{void}
\_out\_of\_pool\_job(\textcolor{keywordtype}{void} *data, \hyperlink{group__Ecore__Thread__Group_ga0c2e1f8d562ccf80ecc0009ada72496f}{Ecore\_Thread} *th)
\{
   App\_Data *ad = data;
   App\_Msg *msg;

   \textcolor{keywordflow}{while} (1)
     \{
        \textcolor{keywordtype}{int} msgs;
        \hyperlink{group__Eina__Lock__Group_ga149f387d5f18c469c365147ffbd4dc2c}{eina\_condition\_wait}(&ad->condition);
        msgs = ad->msgs\_received;
        \hyperlink{group__Eina__Lock__Group_ga469476cfe8d8ca4dfdc46469fb9e31e5}{eina\_lock\_release}(&ad->mutex);
        \textcolor{keywordflow}{if} (msgs == ad->max\_msgs)
          \{
             msg = calloc(1, \textcolor{keyword}{sizeof}(App\_Msg));
             msg->all\_done = 1;
             \hyperlink{group__Ecore__Thread__Group_ga8fadd19caae011ab1483665f09ce4d91}{ecore\_thread\_feedback}(th, msg);
             \textcolor{keywordflow}{return};
          \}
     \}
\}

\end{DoxyCodeInclude}


Every now and then the program prints its status, counting threads running and pending jobs. 
\begin{DoxyCodeInclude}
\textcolor{keyword}{static} \textcolor{keywordtype}{void}
\_print\_status(\textcolor{keywordtype}{void})
\{
   \textcolor{keywordtype}{int} active, pending\_total, pending\_feedback, pending\_short, available;

   active = \hyperlink{group__Ecore__Thread__Group_gad7a28588dc26d6af8b6db8abc3ef37ce}{ecore\_thread\_active\_get}();
   pending\_total = \hyperlink{group__Ecore__Thread__Group_gaeaa014287d578a200d41cca861bceac3}{ecore\_thread\_pending\_total\_get}();
   pending\_feedback = \hyperlink{group__Ecore__Thread__Group_gaa5c56445198f7b03c1f080039499689a}{ecore\_thread\_pending\_feedback\_get}();
   pending\_short = \hyperlink{group__Ecore__Thread__Group_ga3e32f650cfe811e69bba7825c71b9070}{ecore\_thread\_pending\_get}();
   available = \hyperlink{group__Ecore__Thread__Group_ga6e7703b3013d9c872449f6e2ec093b53}{ecore\_thread\_available\_get}();

   printf(\textcolor{stringliteral}{"Status:\(\backslash\)n\(\backslash\)t* Active threads: %d\(\backslash\)n"}
          \textcolor{stringliteral}{"\(\backslash\)t* Available threads: %d\(\backslash\)n"}
          \textcolor{stringliteral}{"\(\backslash\)t* Pending short jobs: %d\(\backslash\)n"}
          \textcolor{stringliteral}{"\(\backslash\)t* Pending feedback jobs: %d\(\backslash\)n"}
          \textcolor{stringliteral}{"\(\backslash\)t* Pending total: %d\(\backslash\)n"}, active, available, pending\_short,
          pending\_feedback, pending\_total);
\}

\end{DoxyCodeInclude}


In our main loop, we\textquotesingle{}ll be receiving messages from our feedback jobs using the same callback for both of them. 
\begin{DoxyCodeInclude}
\textcolor{keyword}{static} \textcolor{keywordtype}{void}
\_feedback\_job\_msg\_cb(\textcolor{keywordtype}{void} *data, \hyperlink{group__Ecore__Thread__Group_ga0c2e1f8d562ccf80ecc0009ada72496f}{Ecore\_Thread} *th, \textcolor{keywordtype}{void} *msg\_data)
\{
   App\_Data *ad = data;
   App\_Msg *msg = msg\_data;
   \textcolor{keywordtype}{char} *str;

\end{DoxyCodeInclude}


The light job running out of the pool will let us know when we can exit our program. 
\begin{DoxyCodeInclude}

   \textcolor{keywordflow}{if} (msg->all\_done)
     \{
        \hyperlink{group__Ecore__Main__Loop__Group_ga95cf8e97dff0716433c2c5474d606a98}{ecore\_main\_loop\_quit}();
        free(msg);
        \textcolor{keywordflow}{return};
     \}

\end{DoxyCodeInclude}


Next comes the handling of data sent from the actual worker threads, always remembering that the data belongs to us now, and not the thread, so it\textquotesingle{}s our responsibility to free it. 
\begin{DoxyCodeInclude}

   \_print\_status();

   \textcolor{keywordflow}{if} (!msg->list)
     printf(\textcolor{stringliteral}{"Received an empty list from thread %p\(\backslash\)n"}, th);
   \textcolor{keywordflow}{else}
     \{
        \textcolor{keywordtype}{int} i = 0;
        printf(\textcolor{stringliteral}{"Received %d elements from threads %p (printing first 5):\(\backslash\)n"},
               \hyperlink{group__Eina__List__Group_ga301729054a498a2df09a5f1dd760697e}{eina\_list\_count}(msg->list), th);
        \hyperlink{group__Eina__List__Group_gae27a2c686a0a11693f9b06957c9bba83}{EINA\_LIST\_FREE}(msg->list, str)
          \{
             \textcolor{keywordflow}{if} (i <= 5)
               printf(\textcolor{stringliteral}{"\(\backslash\)t%s\(\backslash\)n"}, str);
             free(str);
             i++;
          \}
     \}

\end{DoxyCodeInclude}


Last, the condition to exit is given by how many messages we want to handle, so we need to count them and inform the condition checking thread that the value changed. 
\begin{DoxyCodeInclude}

   \hyperlink{group__Eina__Lock__Group_ga2ad27b83cf8562a41fe67557d8d2d753}{eina\_lock\_take}(&ad->mutex);
   ad->msgs\_received++;
   \hyperlink{group__Eina__Lock__Group_ga42cfdc4920e74fd7ba99527a6a99cd7d}{eina\_condition\_signal}(&ad->condition);
   \hyperlink{group__Eina__Lock__Group_ga469476cfe8d8ca4dfdc46469fb9e31e5}{eina\_lock\_release}(&ad->mutex);

   free(msg);
\}

\end{DoxyCodeInclude}


When a thread finishes its job or gets canceled, the main loop is notified through the callbacks set when creating the task. In this case, we just print what happen and keep track of one of them used to exemplify canceling. Here we are pretending one of our short jobs has a timeout, so if it doesn\textquotesingle{}t finish before a timer is triggered, it will be canceled. 
\begin{DoxyCodeInclude}
\textcolor{keyword}{static} \textcolor{keywordtype}{void}
\_thread\_end\_cb(\textcolor{keywordtype}{void} *data, \hyperlink{group__Ecore__Thread__Group_ga0c2e1f8d562ccf80ecc0009ada72496f}{Ecore\_Thread} *th)
\{
   App\_Data *ad = data;

   printf(\textcolor{stringliteral}{"Normal termination for thread %p.\(\backslash\)n"}, th);
   \textcolor{keywordflow}{if} (th == ad->thread\_3)
     ad->thread\_3 = NULL;
\}

\textcolor{keyword}{static} \textcolor{keywordtype}{void}
\_thread\_cancel\_cb(\textcolor{keywordtype}{void} *data, \hyperlink{group__Ecore__Thread__Group_ga0c2e1f8d562ccf80ecc0009ada72496f}{Ecore\_Thread} *th)
\{
   App\_Data *ad = data;

   printf(\textcolor{stringliteral}{"Thread %p got cancelled.\(\backslash\)n"}, th);
   \textcolor{keywordflow}{if} (th == ad->thread\_3)
     ad->thread\_3 = NULL;
\}

\textcolor{keyword}{static} \hyperlink{group__Eina__Types__Group_ga3fe0caf72e93b1bab1ca8ee3ccf3f226}{Eina\_Bool}
\_cancel\_timer\_cb(\textcolor{keywordtype}{void} *data)
\{
   App\_Data *ad = data;

   \textcolor{keywordflow}{if} (ad->thread\_3 && !\hyperlink{group__Ecore__Thread__Group_ga792471a7ed00718b71d58b791ddbd08d}{ecore\_thread\_check}(ad->thread\_3))
     \hyperlink{group__Ecore__Thread__Group_gae9b9b20e1ed53c62c51617b55aed2511}{ecore\_thread\_cancel}(ad->thread\_3);

   \textcolor{keywordflow}{return} \hyperlink{group__Eina__Types__Group_ga1feb115f8e9913e806e090d9bd5a7301}{EINA\_FALSE};
\}

\end{DoxyCodeInclude}


The main function does some setup that includes reading parameters from the command line to change its behaviour and test different results. These are\+: \begin{DoxyItemize}
\item -\/t $<$some\+\_\+num$>$ maximum number of threads to run at the same time. \item -\/p $<$some\+\_\+path$>$ adds {\ttfamily some\+\_\+path} to the list used by the feedback jobs. This parameter can be used multiple times. \item -\/m $<$some\+\_\+num$>$ the number of messages to process before the program is signalled to exit.\end{DoxyItemize}
Skipping some bits, we init Ecore and our application data. 
\begin{DoxyCodeInclude}
   \hyperlink{group__Ecore__Init__Group_ga77757609684a2c922dc5ec398274751b}{ecore\_init}();

   i = \hyperlink{group__Ecore__Thread__Group_gabad7972818b7fb1346ae7a098cd47220}{ecore\_thread\_max\_get}();
   printf(\textcolor{stringliteral}{"Initial max threads: %d\(\backslash\)n"}, i);

   memset(&appdata, 0, \textcolor{keyword}{sizeof}(App\_Data));
   appdata.max\_msgs = 1;

\end{DoxyCodeInclude}


If any paths for the feedback jobs were given, we use them, otherwise we fallback to some defaults. Always initializing the proper mutexes used by the threaded job. 
\begin{DoxyCodeInclude}
   \textcolor{keywordflow}{if} (!path\_list)
     \{
        Feedback\_Thread\_Data *ftd;
        \hyperlink{group__Ecore__Thread__Group_ga4e9de7492f8400e19ddd157d8cc21811}{ecore\_thread\_global\_data\_add}(\textcolor{stringliteral}{"count"}, (\textcolor{keywordtype}{void} *)3, NULL, 
      \hyperlink{group__Eina__Types__Group_ga1feb115f8e9913e806e090d9bd5a7301}{EINA\_FALSE});
        ftd = calloc(1, \textcolor{keyword}{sizeof}(Feedback\_Thread\_Data));
        ftd->name = strdup(\textcolor{stringliteral}{"data0"});
        ftd->base = strdup(\textcolor{stringliteral}{"/usr/bin"});
        \hyperlink{group__Eina__Lock__Group_ga8dda398c1db315582a80640c7f78f648}{eina\_lock\_new}(&ftd->mutex);
        \hyperlink{group__Ecore__Thread__Group_ga4e9de7492f8400e19ddd157d8cc21811}{ecore\_thread\_global\_data\_add}(ftd->name, ftd, NULL, 
      \hyperlink{group__Eina__Types__Group_ga05c12dacc8b4058994df842b41be85fc}{EINA\_TRUE});
        ftd = calloc(1, \textcolor{keyword}{sizeof}(Feedback\_Thread\_Data));
        ftd->name = strdup(\textcolor{stringliteral}{"data1"});
        ftd->base = strdup(\textcolor{stringliteral}{"/usr/lib"});
        \hyperlink{group__Eina__Lock__Group_ga8dda398c1db315582a80640c7f78f648}{eina\_lock\_new}(&ftd->mutex);
        \hyperlink{group__Ecore__Thread__Group_ga4e9de7492f8400e19ddd157d8cc21811}{ecore\_thread\_global\_data\_add}(ftd->name, ftd, NULL, 
      \hyperlink{group__Eina__Types__Group_ga05c12dacc8b4058994df842b41be85fc}{EINA\_TRUE});
        ftd = calloc(1, \textcolor{keyword}{sizeof}(Feedback\_Thread\_Data));
        ftd->name = strdup(\textcolor{stringliteral}{"data2"});
        ftd->base = strdup(\textcolor{stringliteral}{"/usr/share"});
        \hyperlink{group__Eina__Lock__Group_ga8dda398c1db315582a80640c7f78f648}{eina\_lock\_new}(&ftd->mutex);
        \hyperlink{group__Ecore__Thread__Group_ga4e9de7492f8400e19ddd157d8cc21811}{ecore\_thread\_global\_data\_add}(ftd->name, ftd, NULL, 
      \hyperlink{group__Eina__Types__Group_ga05c12dacc8b4058994df842b41be85fc}{EINA\_TRUE});
     \}
   \textcolor{keywordflow}{else}
     \{
        Feedback\_Thread\_Data *ftd;
        \textcolor{keywordtype}{char} *str;
        \hyperlink{group__Ecore__Thread__Group_ga4e9de7492f8400e19ddd157d8cc21811}{ecore\_thread\_global\_data\_add}(\textcolor{stringliteral}{"count"},
                                     (\textcolor{keywordtype}{void} *)(uintptr\_t)\hyperlink{group__Eina__List__Group_ga301729054a498a2df09a5f1dd760697e}{eina\_list\_count}(path\_list), NULL,
                                     \hyperlink{group__Eina__Types__Group_ga1feb115f8e9913e806e090d9bd5a7301}{EINA\_FALSE});
        i = 0;
        \hyperlink{group__Eina__List__Group_gae27a2c686a0a11693f9b06957c9bba83}{EINA\_LIST\_FREE}(path\_list, str)
          \{
             \textcolor{keywordtype}{char} buf[32];
             snprintf(buf, \textcolor{keyword}{sizeof}(buf), \textcolor{stringliteral}{"data%d"}, i);
             ftd = calloc(1, \textcolor{keyword}{sizeof}(Feedback\_Thread\_Data));
             ftd->name = strdup(buf);
             ftd->base = strdup(str);
             \hyperlink{group__Eina__Lock__Group_ga8dda398c1db315582a80640c7f78f648}{eina\_lock\_new}(&ftd->mutex);
             \hyperlink{group__Ecore__Thread__Group_ga4e9de7492f8400e19ddd157d8cc21811}{ecore\_thread\_global\_data\_add}(ftd->name, ftd, NULL, 
      \hyperlink{group__Eina__Types__Group_ga05c12dacc8b4058994df842b41be85fc}{EINA\_TRUE});
             free(str);
             i++;
          \}
     \}

\end{DoxyCodeInclude}


Initialize the mutex needed for the condition checking thread 
\begin{DoxyCodeInclude}
   \hyperlink{group__Eina__Lock__Group_ga8dda398c1db315582a80640c7f78f648}{eina\_lock\_new}(&appdata.mutex);
   \hyperlink{group__Eina__Lock__Group_gaab2b734db1cbdaffe79559f6716ac793}{eina\_condition\_new}(&appdata.condition, &appdata.mutex);

\end{DoxyCodeInclude}


And start our tasks. 
\begin{DoxyCodeInclude}

   \hyperlink{group__Ecore__Thread__Group_ga4bde6b50e177f26ab5b2688aa99e3878}{ecore\_thread\_feedback\_run}(\_out\_of\_pool\_job, \_feedback\_job\_msg\_cb, NULL,
                             NULL, &appdata, \hyperlink{group__Eina__Types__Group_ga05c12dacc8b4058994df842b41be85fc}{EINA\_TRUE});

   \hyperlink{group__Ecore__Thread__Group_gaac9c9933ef0a90ba86f8fd0247e9c7ef}{ecore\_thread\_run}(\_short\_job, \_thread\_end\_cb, \_thread\_cancel\_cb, &appdata);
   \hyperlink{group__Ecore__Thread__Group_ga4bde6b50e177f26ab5b2688aa99e3878}{ecore\_thread\_feedback\_run}(\_feedback\_job, \_feedback\_job\_msg\_cb,
                             \_thread\_end\_cb, \_thread\_cancel\_cb, &appdata,
                             \hyperlink{group__Eina__Types__Group_ga1feb115f8e9913e806e090d9bd5a7301}{EINA\_FALSE});
   appdata.thread\_3 = \hyperlink{group__Ecore__Thread__Group_gaac9c9933ef0a90ba86f8fd0247e9c7ef}{ecore\_thread\_run}(\_short\_job, \_thread\_end\_cb,
                                       \_thread\_cancel\_cb, &appdata);
   \hyperlink{group__Ecore__Thread__Group_ga4bde6b50e177f26ab5b2688aa99e3878}{ecore\_thread\_feedback\_run}(\_feedback\_job, \_feedback\_job\_msg\_cb,
                             \_thread\_end\_cb, \_thread\_cancel\_cb, &appdata,
                             \hyperlink{group__Eina__Types__Group_ga1feb115f8e9913e806e090d9bd5a7301}{EINA\_FALSE});

\end{DoxyCodeInclude}


To finalize, set a timer to cancel one of the tasks if it doesn\textquotesingle{}t end before the timeout, one more timer for status report and get into the main loop. Once we are out, destroy our mutexes and finish the program. 
\begin{DoxyCodeInclude}

   \hyperlink{group__Ecore__Timer__Group_ga7b9cb9d24ecebfdbb957436e2e669402}{ecore\_timer\_add}(1.0, \_cancel\_timer\_cb, &appdata);
   \hyperlink{group__Ecore__Timer__Group_ga7b9cb9d24ecebfdbb957436e2e669402}{ecore\_timer\_add}(2.0, \_status\_timer\_cb, NULL);

   \_print\_status();

   \hyperlink{group__Ecore__Main__Loop__Group_gaf103b9f668bb3e4fed12e52c6180132d}{ecore\_main\_loop\_begin}();

   \hyperlink{group__Eina__Lock__Group_ga838e1e7dfc8bbf114634a7de67de8956}{eina\_condition\_free}(&appdata.condition);
   \hyperlink{group__Eina__Lock__Group_gaf8d1c4e3aff3bd26fc85d81ed8c4e71a}{eina\_lock\_free}(&appdata.mutex);

   \hyperlink{group__Ecore__Init__Group_ga768298b932f18d7e7593a447493e5cde}{ecore\_shutdown}();

   \textcolor{keywordflow}{return} 0;
\}

\end{DoxyCodeInclude}
 