It is the first time that I am working with threads in C and I am getting problems passing function arguments. For some reasons I need to create this thread.
I have a function that sends a messages to a server. The header of my function is:
void* sender (char* payload, void (*Callback)(char*))
Then on my main (simplified), I create the following thread:
main ()
{
pthread_t sender_id;
int err_s;
err_s = pthread_create (&sender_id, NULL, &sender, NULL);
}
The problem is that my function sender has many arguments so I am getting this error:
note: expected 'void * (*)(void *)' but argument is of type 'void * (*)(char *, void (*)(char *))'
I would be gratefull if sombody could help me.
You can just create a structure lets say:
struct thread_prm
{
char *payload;
void (*Callback)(char*) cb;
}
And you just allocate a structure that looks like this and pass it to pthread_create as the last argument. Like this:
struct thread_prm *arg;
arg = malloc(sizeof(struct thread_prm));
(assign arg members to something)
pthread_create(&sender_id, NULL, &sender, arg);
And you modify sender:
void* sender (void *arg)
{
struct thread_prm *_arg = arg;
char* payload;
void (*Callback)(char*) cb;
payload = _arg->payload;
cb = _arg->cb;
...
}
Related
I have written a multi-threaded program that works, but throws warnings at compile-time.
The header file contains the definition of a struct which contains data, as well as the thread function prototype:
//=============main.h=============
// Main data structure definition
typedef struct _SPSData SPSData;
struct _SPSData {
GtkWidget *main_window;
GtkWidget *menubar1;
GtkWidget *view;
GtkWidget *parent; // Pointer to parent that spawned an error. e.g., data->menubar1, data->main_window, or data->traceroute_window
char *error_text; // String to hold error messages.
char *warning_text; // String to hold warning messages.
etc.
};
// Thread function prototype
int ipv4_send (SPSData *);
The thread function to be started by g_thread_new() looks like:
//==========ipv4_send.c=============
int ipv4_send (SPSData *data)
{
some error detection
if error {
return (EXIT_FAILURE);
}
Send ipv4 stuff.
return (EXIT_SUCCESS);
}
And there's a callback from the UI which invokes ipv4_send() via g_thread_new():
//==========callbacks.c=======
// Send packet(s)
int on_button1_clicked (GtkButton *button1, SPSData *data)
{
(void) button1; // This statement suppresses compiler warning about unused parameter.
GThread *thread;
// Spawn thread to send IPv4 packet(s).
thread = g_thread_new ("ipv4_send", (GThreadFunc) ipv4_send, data);
if (! thread) {
fprintf (stderr, "Error: Unable to create new thread for ipv4_send() in on_button1_clicked().\n");
exit (EXIT_FAILURE);
}
return (EXIT_SUCCESS);
}
On compilation, I receive the warning:
callbacks.c: In function ‘on_button1_clicked’:
callbacks.c:3846:41: warning: cast between incompatible function types from ‘int (*)(SPSData *)’ {aka ‘int (*)(struct _SPSData *)’} to ‘void * (*)(void *)’ [-Wcast-function-type]
3846 | thread = g_thread_new ("ipv4_send", (GThreadFunc) ipv4_send, data);
Should the thread function be defined as follows?
==========ipv4_send.c=============
int *ipv4_send (SPSData *data)
{
etc.
with corresponding change to the prototype:
// Thread function prototype
int *ipv4_send (SPSData *);
I don't know how to implement that, as my return statements would be incorrect.
In summary, I don't know how to make my function conform to the expected GThreadFunc typing.
Here is the answer as I interpreted what Cheatah said in the comments.
This works perfectly and the compiler is no longer throwing warnings.
The header file contains the definition of a struct which contains data, as well as the thread function prototype:
//=============main.h=============
// Main data structure definition
typedef struct _SPSData SPSData;
struct _SPSData {
GtkWidget *main_window;
GtkWidget *menubar1;
GtkWidget *view;
GtkWidget *parent; // Pointer to parent that spawned an error. e.g., data->menubar1, data->main_window, or data->traceroute_window
char *error_text; // String to hold error messages.
char *warning_text; // String to hold warning messages.
etc.
};
// Thread function prototype (consistent with GThreadFunc).
void * ipv4_send (void *);
The thread function to be started by g_thread_new() looks like:
//==========ipv4_send.c=============
void *ipv4_send (void *pointer)
{
struct _SPSData *data;
data = (SPSData *) pointer;
some error detection
if error {
return (pointer);
}
Send ipv4 stuff.
return (pointer);
}
And there's a callback from the UI which invokes ipv4_send() via g_thread_new():
//==========callbacks.c=======
// Send packet(s)
int on_button1_clicked (GtkButton *button1, SPSData *data)
{
(void) button1; // This statement suppresses compiler warning about unused parameter.
GThread *thread;
// Spawn thread to send IPv4 packet(s).
thread = g_thread_new ("ipv4_send", (GThreadFunc) ipv4_send, data);
if (! thread) {
fprintf (stderr, "Error: Unable to create new thread for ipv4_send() in on_button1_clicked().\n");
exit (EXIT_FAILURE);
}
return (EXIT_SUCCESS);
}
Thank you, Cheatah!
I'm doing this callback on linux timer, but I don't know why the address changes when it was converted back on the callback function. Code below
typedef void* timer_cb_args;
typedef void (*timer_cb)(timer_cb_args);
struct cb_wrapper
{
timer_cb callback;
timer_cb_args args;
};
void callback_wrapper(union sigval sv)
{
struct cb_wrapper *cb = (struct cb_wrapper*)(sv.sival_ptr);
printf("Casted sival_ptr pointer on Callback wrapper: %p\n\n", cb);
printf("Callback wrapper function pointer: %p\n", cb->callback);
printf("Callback wrapper args pointer: %p\n\n", &cb->args);
cb->callback(cb->args);
}
int timer_start(timer_handle_t *timer_handle,
timer_cb callback,
timer_cb_args args,
guint32 duration)
{
int ret = 0;
timer_t *timer = calloc(1, sizeof(timer_t));
*timer_handle = (timer_handle_t) calloc(1, sizeof(timer_handle_t));
(*timer_handle)->m_timer = timer;
struct sigevent evp;
memset(&evp, 0, sizeof(struct sigevent));
struct cb_wrapper cbargs;
memset(&cbargs, 0, sizeof(struct cb_wrapper));
cbargs.callback = callback;
cbargs.args = args;
evp.sigev_notify = SIGEV_THREAD;
evp.sigev_notify_function = &callback_wrapper;
evp.sigev_value.sival_ptr = &cbargs;
printf("sival_ptr pointer on call: %p\n", evp.sigev_value.sival_ptr);
printf("Function pointer: %p\n", cbargs.callback);
printf("Args pointer on call: %p\n\n", cbargs.args);
int timer_result;
timer_result = timer_create(CLOCK_REALTIME, &evp, timer);
if (timer_result < 0)
return -1;
struct itimerspec timespec;
memset(×pec, 0, sizeof(struct itimerspec));
timespec.it_value.tv_sec = duration;
timer_result = timer_settime(*timer, 0, ×pec, NULL);
if (timer_result < 0)
return -1;
return ret;
}
output is:
sival_ptr pointer on call: 0x7ffce75c3950
Function pointer: 0x55f26d13abb4
Args pointer on call: 0x7ffce75c3a00
Callback wrapper.
Casted sival_ptr pointer on Callback wrapper: 0x7ffce75c3950 //OK same
Callback wrapper function pointer: 0x55f26d13abb4 //OK same
Callback wrapper args pointer: 0x7ffce75c3958 //NOK not same as above
The problem is here:
typedef void* timer_cb_args;
typedef void (*timer_cb)(timer_cb_args);
You are hiding pointers behind typedef and the only thing achieved by that is making everyone including yourself confused.
Therefore you write bugs such as this:
evp.sigev_notify_function = &callback_wrapper;
evp.sigev_value.sival_ptr = &cbargs;
callback_wrapper is a function pointer and they have special rules about dereferencing/decay (Why do function pointer definitions work with any number of ampersands '&' or asterisks '*'?), so that line works by accident.
cbargs is however just an ordinary void* so
evp.sigev_value.sival_ptr = &cbargs; assigned a void** to a void*. And since evp.sigev_value.sival_ptr is a void*, that's allowed without the compiler giving diagnostic messages.
This is a really subtle bug! I managed to find it in some five minutes here, but it could as well have taken forever. And the root cause is bad typedef practices.
Fix it like this:
typedef void timer_cb (timer_cb_args);
struct cb_wrapper
{
timer_cb* callback;
void* args;
};
...
evp.sigev_notify_function = callback_wrapper;
evp.sigev_value.sival_ptr = cbargs;
And then clean up the rest of the code accordingly, clearing out all pointers hidden behind typedefs.
Also unrelated to this bug, as someone pointed out it isn't a good idea to pass local variables by reference to callbacks. Because once the function setting up the callback is done, that memory is toast. A normal fix when for example passing variables to thread callbacks, is to pass a pointer to dynamic memory. Or alternatively just ensure that the thread creating thread doesn't die/go out of scope before the end of execution, after all other threads are cleaned up.
I'm trying to pass this function: void* checkMatrix(); as an argument to this function: void createThreads(void*(*f));.
I've read a post here so my decleration above is a result of this.
I'm calling the function like this: createThreads(checkMatrix); but it gives me a warning that type is incompatible [void** and void*()]. I can get around with a fast cast but it won't fix the problem.
Finally I write the function like this (simple initialization):
void createThreads(void* (*f)) {
pthread_t* a;
int i;
a = (pthread_t*) malloc(*arr.l * sizeof(pthread_t));
if (a == NULL) {
fprintf(stderr, "ERROR!\n");
exit(1);
}
for (i = 0; i < *arr.l; i++) {
if (pthread_create((a + i), NULL, (void*) &f, NULL)) {
fprintf(stderr, "ERROR IN THREAD CREATION!\n");
exit(2);
}
}
for (i = 0; i < *arr.l; i++)
pthread_join(*(a + i), NULL);
}
In conclusion, the problem is that it stops, with memory problem, but the cause is the creation of the threads and espacially in the 3rd argument that I specify the function that the thread will work on. I think I'm doing something wrong with the calling. I can't find the answer and can't get around it.
Thanks for your time!
void* (*f) is just void **f with a set of redundant parentheses. You probably wanted to use this for the parameter type:
void* (*f)()
However, that is not what pthread_create expects. The thread's main function is supposed to return void* and take a void* parameter. So what you really want is probably this:
void createThreads(void* (*f)(void*)) {
/* ... as before ... */
if (pthread_create((a + i), NULL, f, NULL)) {
/* ... as before ... */
}
To begin with void* checkMatrix(); is obsolete style and shouldn't be used. Second, pthread callback functions take void* as parameter. So use void* checkMatrix(void*); instead.
To pass it to a function, simply do
void createThreads (void* (*f)(void*))
Recommended practice when using function pointers is otherwise to use typedefs, to increase readability. For example you could cook up something like
typedef void* pthread_callback (void*);
void createThreads (pthread_callback* f)
Try changing the function signature to void createThreads(void* (*f)(void *))
and change the pthread_create function call topthread_create((a + i), NULL, f, NULL)
I have a function void startScanner(...) taking two function pointer as arguments: userType *vConfig(void) and void * vCallback(void). In this function i would like to create a thread and call vCallback() function in the function thread created. So i decided to pass vCallback as args to pthreadcreate.
The code of startScanner function :
void startScanner(tUsrStatus (*vConfig)(), void* (vCallback)()){
if(pthread_create(&scannerThread, NULL, scannerThreadFunc, vCallback))
{
printf("Thread creation fails!\n");
}
}
The scannerTread function:
static void *scannerThreadFunc(void *arg()){
void *funcptr(void) = arg;
while(1)
{
funcptr();
}
pthread_exit(NULL);
}
I get the following error:
error: function ‘funcptr’ is initialized like a variable
error: nested function ‘funcptr’ declared but never defined
How can i fix this?
Syntax errors aside (*) , it's impossible in standard C to pass a function pointer in a void *. There's a fundamental difference between pointers to functions and pointers to data, they can't be converted into each other. This is because there might be platforms where function and data pointers would differ even in size, or refer to different address spaces, or whatever.
But of course, there's a simple way to achieve what you want: Put your function pointer inside a struct and pass a pointer to that.
typedef (*callback)(void);
typedef struct threadargs
{
callback cb;
} threadargs;
void mycallback(void)
{
// ...
}
void *threadfunc(void *arg)
{
threadargs *ta = arg;
// call your callback:
ta->cb();
return ta; // or: return 0, or some pthread_exit(), ...
}
int main(void)
{
pthread_t thread;
threadargs ta = { mycallback };
pthread_create(&thread, 0, threadfunc, &ta);
// make sure "ta" lives for as long as the thread executes,
// here just wait until it exits:
pthread_join(&thread, 0);
}
add error checking etc.
(*) as for the concrete error you're getting, a function pointer needs parantheses around the identifier, so instead of
void *funcptr(void) = arg;
you'd have to write
void (*funcptr)(void) = arg;
To facilitate the usage of function pointers, it's common to typedef them, as seen in my example above. Anyways, as explained above, this wouldn't solve your problem here.
I would need some help. I been doing it for hours and is not able to get it to work. Generally, I have a function which access a kernel Driver and I would like to pass that function as a parameter to another function that include some pthread code. I researched and found out that I may need a function pointer.
Here is the function which I want to pass as a parameter.
static void kernelTest(char Send[BUFFER_LENGTH])
{
int fd = open("/dev/kernelTest", O_RDWR);
}
Here is the function which I want to pass in:
static void createKThread(void (*f)(char *))
{
pthread_t t1;
int ret;
ret = pthread_create(&t1, NULL, (*f)(char), NULL);
pthread_join(t1, NULL);
}
I attempted the function pointer but it is giving me error.
error: expected expression before ‘char’
I greatly appreciate any help rendered. Thank You!
(*f)(char) is invalid syntax. It looks like you're attempting to call the function f and passing char as a parameter, which you can't do.
Since you're not actually calling f, just pass it to pthread_create directly:
ret = pthread_create(&t1, NULL, f, NULL);
There's still a problem with this, however. The third parameter to pthread_create is expected to be of type void *(*)(void *), i.e. a pointer to a function that has a void * parameter and returns a void *. Your function has type void (*)(char *), so the parameters are incompatible.
You need to either change the signature of kernelTest to match what pthread_create expects:
static void *kernelTest(void *param)
{
char *send = param;
int fd = open("/dev/kernelTest", O_RDWR);
return NULL;
}
Or you need to create a wrapper function which matches pthread_create:
static void *kernelTest_wrapper(void *param)
{
char *send = param;
kernelTest(send);
return NULL;
}