Search code examples
cclangpthreads

What is the correct ANSI-compatible way to create a pthread from a function with no parameters in C using clang-10+?


I have a function with no parameters in C. Clang-16 suggests me to avoid function declaration without a prototype, but pthread_create does not work anymore with prototype that has no parameters. This is the scenario:

The main.c file tries to create a pthread from a thread_function function:

// main.c
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include "./thread.h"

int main(void) {
  pthread_t thread_id;
  pthread_create(&thread_id, NULL, thread_function, NULL);
  pthread_join(thread_id, NULL);
  return EXIT_SUCCESS;
}

The thread_function function is declared in thread.h header file:

// thread.h
#ifndef _THREAD_H_
#define _THREAD_H_

void *thread_function();

#endif  // _THREAD_H_

And is defined in thread.c file:

// thread.c
#include <stdio.h>

void *thread_function() {
  printf("Hi\n");
  return NULL;
}

I compiled the program with clang-16:

$ clang-16 main.c thread.c -o thread -Wpedantic
In file included from main.c:4:
././thread.h:4:22: warning: a function declaration without a prototype is deprecated in all versions of C [-Wstrict-prototypes]
void *thread_function();
                     ^
                      void
1 warning generated.
thread.c:3:22: warning: a function declaration without a prototype is deprecated in all versions of C [-Wstrict-prototypes]
void *thread_function() {
                     ^
                      void
1 warning generated.

To fix warnings, I applied both suggested fixes:

// thread.h
#ifndef _THREAD_H_
#define _THREAD_H_

void *thread_function(void);

#endif  // _THREAD_H_

And also in thread.c:

// thread.c
#include <stdio.h>

void *thread_function(void) {
  printf("Hi\n");
  return NULL;
}

Now I can not compile the program anymore:

$ clang-16 main.c thread.c -o thread -Wpedantic
main.c:8:36: error: incompatible function pointer types passing 'void *(void)' to parameter of type 'void * _Nullable (* _Nonnull)(void * _Nullable)' [-Wincompatible-function-pointer-types]
  pthread_create(&thread_id, NULL, thread_function, NULL);
                                   ^~~~~~~~~~~~~~~
/Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk/usr/include/pthread.h:334:31: note: passing argument to parameter here
                void * _Nullable (* _Nonnull)(void * _Nullable),
                                            ^
1 error generated.

I changed the signature of the function to:

// thread.h
#ifndef _THREAD_H_
#define _THREAD_H_

void *thread_function(void *);

#endif  // _THREAD_H_

And also:

// thread.c
#include <stdio.h>

void *thread_function(void *) {
  printf("Hi\n");
  return NULL;
}

It compiles then but it is not ANSI-compatible anymore:

$ clang-16 main.c thread.c -o thread -Wpedantic
thread.c:3:29: warning: omitting the parameter name in a function definition is a C2x extension [-Wc2x-extensions]
void *thread_function(void *) {
                            ^
1 warning generated.

If I change the signature to void *thread_function(void *args) it is not "a function with no parameters" anymore. How can I create pthread from a function with no parameters and still be ANSI-compatible?


Solution

  • Pthreads have always expected a function pointer of type void* func (void*) regardless of what compiler or C standard you are using. If you ever did anything else, you have been doing it wrong.

    void *thread_function(); is an invalid thread callback function in all versions of C and POSIX. Similarly, void *thread_function(void) does not improve anything since it is still the wrong function type.

    The last compiler message you get is because a function definition (as opposed to declaration/prototype) without explicit parameter names is nonsense. To be ISO C compatible (forget about "ANSI"), you need to name it:

    void *thread_function (void* param)
    

    Now if you aren't passing any parameters to the function you can just cast it to void to disable warnings: (void)param;. Alternatively in upcoming C23, the parameter could be declared as [[maybe_unused]] void* param.

    If I change the signature to void *thread_function(void *args) it is not "a function with no parameters" anymore.

    It never was. Pthreads demand a function with a void* parameter, period.

    What function types to use here is not some option that the programmer can decide. The correct types to use are the ones listed by the library API, man pthread_create:

    #include <pthread.h>
    
    int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                       void *(*start_routine) (void *), void *arg);