Search code examples
cpthreadsvariadic-macros

Why does va_list var-arg copy not work as expected if the copy is saved and given to another thread?


As in this example, if I call tsub directly from main, output is as expected. If pthread mode is enabled for tsub, this doesn't give expected output.

va_list vobj;

void *tsub (void *arg)
{                     
    printf ("vobj: %d, %f\n", va_arg (vobj, int), va_arg (vobj, double));  
    return NULL;  
}               

void print_mydb (char *name, ...)
{        
    va_list ap;  
    va_start (ap, name);  
    va_copy (vobj, ap);  
    va_end (ap);                                                            
}                                 

int main (void)
{              
    pthread_t tid;              
    print_mydb ("b10", 6, 5.23);  

#if 1                           
    printf ("THREADY!!!!!\n");  
    pthread_create (&tid, NULL, tsub, (void *)vobj);  
    pthread_join (tid, NULL);  

#else                        
    tsub (NULL);  

#endif           
    return 0;   
}

Output for direct tsub call:

vobj: 6, 5.230000

Output for pthread invocation for tsub:

THREADY!!!!!
vobj: 0, 0.000000

Solution

  • AFAICS, you're invoking undefined behaviour both times; it just sometimes works (seems to work in this context) and sometimes doesn't. When print_mydb() returns, the copied vobj is pointing at invalid data — the function call that provided the data (argument list) it points at has returned, so the content of vobj is now meaningless. It doesn't matter whether you call tsub() directly or via pthread_create(), you're abusing memory once passed to print_mydb(). It breaks worse when pthread_create() tramples on the memory as it is called.

    Further, the function that calls va_copy() must call va_end() on the copied list before it returns. C11 §7.16.1 Variable argument list access macros ¶1

    Each invocation of the va_start and va_copy macros shall be matched by a corresponding invocation of the va_end macro in the same function.

    Consequently, print_mydb() exhibits undefined behaviour because it doesn't call va_end() on vobj before it returns.