2

In particular, I want to set a value of a function pointer. For simplicity, I want to do it many times, from multiple threads, but always in a simple manner like this:

typedef void (*F)();
F f = 0;

void foo()
{
}

// called many times from multiple threads
void set()
{
    f = &foo;
}

int main()
{
    set();  // also other threads can invoke it at any time
    f();
    return 0;
}

Thus, initially the function pointer is NULL and then becomes &foo when the code is executed for the first time. I wonder if due to any non-atomic write operation the function pointer may become disrupted.

It is guaranteed that it will be read for the first time after it is set.

EDIT: I clarify:

  1. The main reason I use a function pointer is to remove some dependencies between modules. This is a small element of a big real project. I really can't call 'foo' directly.
  2. I know how to program and I do not need basic information about things like mutex. My question is whether this is safe WITHOUT mutex.
  3. It is guaranteed in the code that no other thread sets the pointer to anything other than &foo.
Michal Czardybon
  • 2,795
  • 4
  • 28
  • 40

3 Answers3

4

Have you considered use a std::mutex and std::lock_guard to enforce thread safety?

For example

{
    std::lock_guard<std::mutex> lg(my_mutex);
    f = &foo;
    // then use f to perform your operation
}

After the closing brace, the lock_guard falls out of scope and unlocks your mutex. This means that whoever sets f can then use that function pointer safely knowing that another thread hasn't changed the function pointer between setting it and trying to use it.

Cory Kramer
  • 114,268
  • 16
  • 167
  • 218
  • Yes, I am aware of this possibility, but I wanted to know, if this write was safe without it. I am trying not to use any mutex to reduce dependencies in my project. – Michal Czardybon Jan 28 '15 at 13:49
  • @MichalCzardybon To specifically answer that question, [no, a pointer assignment is not necessarily thread safe](http://stackoverflow.com/questions/879077/is-changing-a-pointer-considered-an-atomic-action-in-c). – Cory Kramer Jan 28 '15 at 13:50
  • But I am talking about a case where the value being assigned is always the same. So even if two threads write it simultaneously, they write the same value. I can't imaging how this could lead to any problem. Btw. f = &foo is translated to a single 'mov' instruction. – Michal Czardybon Jan 28 '15 at 14:04
  • @MichalCzardybon Why do you use a function pointer here if it is always the same function ? Specifically, if you can call `set` from anywhere and then call `f`, then surely you can call `foo` from anywhere can't you ? – Félix Cantournet Jan 28 '15 at 14:09
  • @Felix: this is because this sample comes from a complex and tricky project. One element of that is that declaration of 'foo' is not visible in a module that calls it through the pointer. – Michal Czardybon Jan 28 '15 at 14:19
  • @MichalCzardybon you question sounds more and more like "why is this code not thread safe?" Since the way you asked the question is very general, you can only get general answers. But we all know that the devil is in the details, the very fine details. To really asses if anything will go wrong will happen with your code a deep analysis is required - but generally it's not thread safe (which translates to, you may get away with it half the time, but not always) – Pandrei Jan 28 '15 at 14:24
  • 2
    @MichalCzardybon Ok. Use `std::mutex`. It's the c++ stdlib for crying out loud. It's not a dependency... – Félix Cantournet Jan 28 '15 at 14:25
  • I am asking if this is safe without mutex. Using a mutex requires having some global object whereas this is a header-only library. – Michal Czardybon Jan 28 '15 at 14:31
2

The standard says your code invokes undefined behavior. But in your particular case, i.e., with your particular compiler and architecture, your code may actually be problem-free. So, from a theoretical point of view, your code is not OK. From a practical point of view, the answer depends on your specific case and is by no means general. Personally, I suggest replacing F f = 0; with std::atomic<F> f(0); so that the code is guaranteed to be OK in all cases.

Just to illustrate a case where your code breaks: The implementation may choose to clear the target before each write operation. Such an implementation is legal and conforms to the standard, though not necessarily award-winning :)

Lingxi
  • 14,579
  • 2
  • 37
  • 93
  • This is exactly what I thought initially. I was just not sure if I was not missing something. Should I accept your questions just because you think the way I think? ;-) – Michal Czardybon Jan 28 '15 at 17:55
  • @MichalCzardybon I'm glad we think it the same way. I would appreciate it if you could accept my answer :) – Lingxi Jan 29 '15 at 01:04
  • I am accepting, but I would be grateful if you provide also some citation justifying your statements. Before that the acceptance is just because you gave me the answer I wanted to hear. – Michal Czardybon Jan 29 '15 at 08:27
0

I think this is more of a thread synchronization problem than a c++ related one. The answer to your question is, yes you can write a value to a varaible/pointer safely in c++ (Not sure what you mean by constant; it looks to me like you simply want to assign a function pointer), you simply have to use mutual exclusion so that the threads don't overwrite the value when you don't want them to

Depending on what you ultimately want to achieve, you may need more than one mutex (i.e if you want to ensure a certain order in which the threads assign the function pointer), but this is definitely the way to safely do it.

Pandrei
  • 4,843
  • 3
  • 27
  • 44
  • My question is: is it safe without a mutex. – Michal Czardybon Jan 28 '15 at 13:56
  • no, it's not; you never know when a thread will be swiped out so the code will likely crash. Imagine what happens if the first thread is swiped out while executing the method it assigned the function pointer and a second thread comes in and overwrites the function pointer. – Pandrei Jan 28 '15 at 14:00
  • But all the assignments, which are possible, write exactly the same value. This is the point of this question. Can we get any other value than &foo after two threads write &foo at the same time? – Michal Czardybon Jan 28 '15 at 14:10
  • I don't think you can get a different value if two threads write the same thing, but this is highly dependent on the scheduler and the compiler. But if you say it like that it sounds more like a theoretical question, with no practical applicability. – Pandrei Jan 28 '15 at 14:16