简体   繁体   中英

Make a variable that is initialized by a function available to a function in multithreaded environment

So here is the problem I'm trying to solve, I'm programming in C.

We have a function that can initialize a struct for you.

typedef struct {
  int val1;
  int val2;
} custom_t;

custom_t init_custom() {
  custom_t temp;

  temp.val1 = 5;
  temp.val2 = 5;


  return temp;
}

And you would just use it like so:

custom_t some_name = init_custom();

I have 4 functions that takes the custom_t as input and can do some work with it.

In another file I have a lot of library functions that will run in a multithreaded enviroment. These library functions will all need to do work on the same custom_t variable, no matter the thread.

The library functions won't get the custom_t variable passed to it, because the goal is that another user should be able to use the library functions without thinking of the custom_t variable.

I'm thinking that I have to make the custom_t variable global in the namespace where I define the library functions but I an error saying that global variables must be const.

I am unsure how to achieve this and I would appreciate all the help I can get. If my explanation wasn't good enough feel free to ask any questions and I will try to elaborate.

EDIT: Fixed the variable init typo

With custom_t = init_custom(); you're trying to set a typename (ie custom_t ).

Just call it something else:

custom_t my_global_custom = init_custom();

But, to access this from multiple threads and library functions, assuming you'll need to write to it, you'll need to wrap access to this in a mutex:

pthread_mutex_t custom_mutex = PTHREAD_MUTEX_INITIALIZER;
custom_t my_global_custom;

my_global_custom = init_custom();

// how each thread must access it
pthread_mutex_lock(&custom_mutex);
func_that_uses_my_global_custom();
pthread_mutex_unlock(&custom_mutex);

UPDATE:

My example wasn't intended to be literally an initializer but an assignment :

pthread_mutex_t custom_mutex = PTHREAD_MUTEX_INITIALIZER;
custom_t my_global_custom;
custom_t my_global_2;

custom_t
init_custom(void)
{
    custom_t temp;

    temp.val1 = 5;
    temp.val2 = 5;

    return temp;
}

void
init_custom2(custom_t *temp)
{

    temp->val1 = 5;
    temp->val2 = 5;
}

int
main(void)
{

    // either one of these should work ..
    my_global_custom = init_custom();
    init_custom2(&my_global_2);

    // start some threads ...

    return 0;
}

void *
thread_func(void *)
{

    // how each thread must access it:
    pthread_mutex_lock(&custom_mutex);
    func_that_uses_my_global_custom();
    pthread_mutex_unlock(&custom_mutex);

    return (void *) 0;
}

UPDATE #2:

But do you know any way to initialize my_global_custom outside the main function? Or is that just not possible?

Another way [under gcc at least], is to create a contructor function. Given the above functions and definitions, move the init calls into:

void __attribute__((constructor))
my_global_constructor(void)
{

    my_global_custom = init_custom();
    init_custom2(&my_global_2);
}

Nothing needs to [and nothing should ] call this function. It will be called automatically before main is called because it's now a special function.

These are often used by libraries that want to do some init, but don't want to burden main with having to know to call (eg) init_liba(); init_libb(); ... init_liba(); init_libb(); ... init_liba(); init_libb(); ... In this case, it is called at the "right" time for the library [based upon linkage, etc.].

There is also a __attribute__((destructor)) than can be used to "destroy" things [after main returns, IIRC].

For more on this, see: How exactly does __attribute__((constructor)) work?

Personally, I now use the above attribute, but, for nostalgia, I like the older .init/.fini sections.

As clarified in you comment on @CraigEstey's answer, the problem is not that the variable needs to be const , but that its initializer needs to be a compile-time constant. One approach to this sort of problem is to define a static initialization value via a macro instead of as a function return value. For example, in some appropriate header file declare

typedef struct {
    int val1;
    int val2;
} custom_t;

#define CUSTOM_T_INITIALIZER { .val1 = 5, .val2 = 5 }

You can then initialize variables of type custom_t like so:

custom_t some_name = CUSTOM_T_INITIALIZER;

, including at file scope.

Update:

If you need to assign a value to your file-scope or static-duration variable that is not compile-time constant (including if it has an aggregate type and the value desired for one of its members is not a compile-time constant), then you cannot use an initializer for that purpose. Instead, you must arrange to assign the wanted value to that variable after the program starts, perhaps by calling an initialization function of some sort.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM