3
gcc 4.7.2
c89
apr utility 1.4

Hello,

I am using a thread pool to start threads. However, I can't see any apr function that allows me to wait for the threads to join.

The code sippet, removed all error checking and non-essential parts:

int main(void)
{
    /* Initialize apr internal structures */
    apr_initialize();

    /* Create memory pool */
    rv = apr_pool_create(&mem_pool, NULL);

    /* Create thread pool */
    memset(&buf, 0, sizeof buf);
    rv = apr_thread_pool_create(&thd_pool,
                                init_threads,
                                max_threads,
                                mem_pool);

    /* Process the number of jobs */
#define NUMBER_JOBS 1
    for(i = 0; i < NUMBER_JOBS; i++) {
        rv = apr_thread_pool_schedule(thd_pool,
                                      timeout_duration,
                                      (void*)channel,
                                      (apr_interval_time_t)flash_timeout,
                                      NULL);

    }

    /* 
     * Join all threads here 
     */

    /* Destroy resources */
    apr_thread_pool_destroy(thd_pool);
    apr_pool_destroy(mem_pool);
    apr_terminate();

    return 0;
error:
    apr_thread_pool_destroy(thd_pool);
    apr_pool_destroy(mem_pool);
    apr_terminate();

    return 1;
}

void* timeout_duration(apr_thread_t *thd, void *data)
{
    channel_t *channel = (channel_t*)data;

    LOG_DEBUG("Channel timeout notification [ %zu ]", channel->id);
}

I couldn't see any apr utity functions that join threads.

However, I did find this function apr_thread_join(apr_status_t *retval, apr_thread_t *thd) However, it takes a apr_thread_t as an argument.

The function timeout_duration takes a apr_thread_t but how can I manage to pass it back, if I need to use it for joining?

Just a side note question. Is there any sample projects that use the apr and I can reference. The documentation is very limited.

Many thanks for any suggestions,

ant2009
  • 27,094
  • 154
  • 411
  • 609
  • Wouldn't the largely used [pthreads](https://computing.llnl.gov/tutorials/pthreads/) be more convenient? It seems the APR threads library (pool) is lacking some important features, like a *join* of the pool active threads, or even to access the threads individually from the pool, and *wait* for them in a *for* loop. Pthreads are reliable, and a *pool* is easily implemented. – Déjà vu Feb 03 '13 at 08:39
  • @ring0 if I read the code correctly that it exactly what `apr_thread_pool_destroy` is doing. Maybe the name is misleading (and the documentation). – Patrick B. Feb 03 '13 at 08:50
  • @Patrick Oh indeed, you did a good work analyzing that code. I also had a look: considering how the documentation is (misleadingly)written and (not)helpful, how the library lacks obvious features, I'd keep *pthreads* for threading, [and *nginx* for webserving... but I'm digressing!] – Déjà vu Feb 03 '13 at 09:21

1 Answers1

4

Short anser

You don't need to join the threads in the thread-pool. When you call apr_thread_pool_destroy the function will block, until all threads have finished their current task.

To answer your last question first: I didn't find an example but the libapr and libapr-util are open-source, you can read the source and this is what I did: (I checked the SVN-trunk here rev 1441871)

Long answer

The interesting files:

First check in apr_thread_pool.c:394. Here we find the implementation of apr_thread_pool_destroy. We can see the it calls a function called apr_pool_cleanup_run with three arguments, one is the pool-storage, one is the thread-pool-context and the last one is a function-pointer to the function thread_pool_cleanup.

If we follow apr_pool_cleanup_run we will get to apr_pools.c:2453 and see that apr_pool_cleanup_kill is called. Reading this last function shows us, that here in several loops over the elements (the threads) are cleaned by (what we will see later) calling the cleanup_fn-function-argument.

Now back in function apr_pool_cleanup_run there is a final call to cleanup_fn.

The real action is going on the function-pointer passed to apr_pool_cleanup_run. So, if we go back to apr_thread_pool.c:329 we find the function thread_pool_cleanup.

In it the context-variable terminated is set to 1 and then the function is "sleeping" until _myself->thd_cnt has become 0.

Searching for the usage of terminated we find that thread_pool_func is exiting its loop when terminated is not 0. It turns out that thread_pool_func is the function each thread in the threadpool is using. In the loop a task is fetched and executed. When the loop has terminated (because terminated has become 1) the following code is executed:

 /* idle thread been asked to stop, will be joined */
 --me->thd_cnt;

This will ultimately lead to the thd_cnt==0 which is the terminate-condition for the loop in thread_pool_cleanup.

When you call apr_thread_pool_destroy all threads are stopped cleanly before the function returns.

Patrick B.
  • 11,773
  • 8
  • 58
  • 101