1

I'm testing an idea for detailed error handling, and want to enable a thread to have the ability to call a 'getlasterror' function when it needs to work with the error. I'm using a cheap and simple pointer-to-pointers for the structs, but also make use of the pthread_t id to overwrite a previous entry (if the error info was not needed or has been processed).

From the stackoverflow posts How do you query a pthread to see if it is still running? and How do I determine if a pthread is alive?, it seems using pthread_kill to send a fake signal is potentially unsafe. Is there really no alternative mechanism to check if a pthread with an id exists or not? Or can I disable the ability for thread ids to be reused at runtime? (I'm aware the latter may be a security issue...)

I'd not previously written any code, but I whipped up roughly what my plan would look like below in leafpad (so ignore any syntax errors, if any!). Point of interest is naturally the dynamic cleanup, there's no problem if the application is closing. Any other alternative ideas would also be welcome :)

If applicable, this will be a client/server program, hence a new thread will exist with each accept().

struct error_info_structs
{
    struct error_info** errs;   // error_info struct with details
    pthread_t**         tids;   // thread ids for each struct
    uint32_t            num;    // number of error_info structs and thread ids
    pthread_mutex_lock  lock;   // runtime locker
};
struct error_info_structs   g_errs;
// assume we've done necessary initialization...

struct error_info*
get_last_runtime_error()
{
    struct error_info*  retval = NULL;
    pthread_t           tid = pthread_self();

    pthread_mutex_lock(&g_errs.lock);

    for ( uint32_t i = 0; i < g_errs.num; i++ )
    {
        if ( pthread_equal(g_errs.tids[i], tid) )
        {
            retval = g_errs.errs[i];
            goto release_lock;
        }
    }

release_lock:
    pthread_mutex_unlock(&g_errs.lock);
    return retval;
}


void
raise_runtime_error(struct error_info* ei)
{
    pthread_t   tid = pthread_self();

    pthread_mutex_lock(&g_errs.lock);

    for ( uint32_t i = 0; i < g_errs.num; i++ )
    {
        if ( pthread_equal(g_errs.tids[i], tid) )
        {
            // replace existing
            memcpy(&g_errs.errs[i], ei, sizeof(error_info));
            goto release_lock;
        }

        /*
         * Dynamic cleanup to lower risk of resource exhaustion.
         * Do it here, where we actually allocate the memory, forcing
         * this to be processed at least whenever a new thread raises
         * an error.
         */
        if ( pthread_kill(g_errs.tids[i], 0) != 0 )
        {
            // doesn't exist, free memory. safe to adjust counter.
            free(g_errs.errs[i]);
            free(g_errs.tids[i]);
            g_errs.num--;
        }
    }

    /*
     * first error reported by this thread id. allocate memory to hold its
     * details, eventually free when thread no longer exists.
     */

    struct error_info*  newei = malloc(sizeof(struct error_info));
    if ( newei == NULL )
    {
        goto release_lock;
    }

    pthread_t*      newt = malloc(sizeof(pthread_t));
    if ( newt == NULL )
    {
        free(newei);
        goto release_lock;
    }

    // realloc-bits omitted

    g_errs.errs[g_errs.num] = newei;
    g_errs.tids[g_errs.num] = newt;
    g_errs.num++;

release_lock:
    pthread_mutex_unlock(&g_errs.lock);
}
Community
  • 1
  • 1
ZXcvbnM
  • 611
  • 7
  • 16
  • 2
    `if ( g_errs.tids[i] == tid )` This is broken. You can't compare `pthread_t`'s this way. (Consider if it's a `struct`.) Look up `pthread_equal` for the right way to compare. – David Schwartz Oct 24 '14 at 00:21
  • 2
    There is, however, a nice pthread_equal() function that will compare them for you. – Jeremy Friesner Oct 24 '14 at 00:23
  • First of all, `pthread_kill()` is perfectly valid (it does _not_ send a signal, for normal semantics of "signal") to use, so long as your thread is joinable (and I don't see why in your application, would you make a thread non-joinable). In your code, before you release the resources for your thread, employ a `pthread_join()`. – Mark Nunberg Oct 24 '14 at 01:45
  • Yeah, I may maintain a thread list elsewhere, that way they can be joinable in general and operate like that. I'll give it a try tomorrow and update, has just gone 4 am.. – ZXcvbnM Oct 24 '14 at 03:19

1 Answers1

0

... can I disable the ability for thread ids to be reused at runtime?

No, you can't.

alk
  • 69,737
  • 10
  • 105
  • 255