0

I have a Visual C++ program that opens a file in one thread with FILE* fp = fopen(...). I want that thread to block on an event object while another thread reads the file, then signals the blocked thread when it is done, which will then close the file. Because fp is shared between threads, I have declared it as volatile FILE* fp. However, fread() won't accept a volatile as its FILE* argument. I tried using a local pointer, with FILE* fpLocal = fp; in the thread that will call fread(), but that got me this:

 Error: a value of type "volatile FILE*" cannot be assigned to an entity of type "FILE*"

Naturally, this has me worried that maybe I'm making a mistake by trying to open a file in one thread and read it in another to begin with, though I don't see why (yet).

Can someone help me with this? Why can't I assign a volatile FILE* to a FILE*?

Stevens Miller
  • 1,387
  • 8
  • 25
  • 12
    Drop the `volatile`. It has nothing to do with threads. – R. Martinho Fernandes May 22 '12 at 15:35
  • 2
    You can't assign a `volatile FILE*` to a `FILE*` for the same reason as you can't assign a `const FILE*` to a `FILE*`, but as @R.MartinhoFernandes noted, using `volatile` here is very misguided to begin with. – ildjarn May 22 '12 at 15:36
  • 2
    The only thing `volatile` guarantees is that every access to a `volatile` variable will result in a load/store (and AFAIK, this was meant to be useful for systems which mapped memory to I/O devices and such. The C standard for example, did not even once mention the word 'thread') – ArjunShankar May 22 '12 at 15:40
  • "The only thing volatile guarantees ...": A good number of compilers don't even do that. See *Volatiles Are Miscompiled, and What to Do about It* at http://www.cs.utah.edu/~regehr/papers/emsoft08-preprint.pdf . – David Hammen May 22 '12 at 15:47
  • 1
    Note that `volatile` in this context applies to the pointer, not the file object itself. Not terribly helpful even if it did what you wanted, which it doesn't. – Mark Ransom May 22 '12 at 15:57
  • @Mark: Is that because, even though `volatile` will prevent caching of the pointer, it won't prevent caching of what the pointer is pointing at? – Stevens Miller May 22 '12 at 16:14
  • @Stevens - No, it is because `volatile` isn't about threads, it is used to interface things like memory mapped hardware. If one of your threads is blocked while waiting, you are already done synchronizing the threads. – Bo Persson May 22 '12 at 16:30
  • @Bo: Thanks. What's the right way to guarantee that each thread, after proper synchronization, sees the changes made to variables written by the other thread? All the pointers people are giving me are great and I'm learning a lot (thanks, everyone!), but it does seem like 'volatile' will guarantee that, once a variable is written by any thread, all other threads will later read what that first thread wrote. Is there a better way to be sure variables are actually stored back to memory before unblocking my reading thread? – Stevens Miller May 22 '12 at 16:36
  • @Stevens - You have already been given the link [Why is volatile not considered useful](http://stackoverflow.com/questions/2484980/why-is-volatile-not-considered-useful-in-multithreaded-c-or-c-programming). If you use locks or other synchronization provided by the language or the OS, you don't *need* `volatile`. – Bo Persson May 22 '12 at 17:02
  • @Bo. I read that link. It addresses atomicity and reordering, which I believe I have already coped with via synchronization. That link also says that `volatile` guarantees that "the read/write actually happens (that the compiler won't just store the value in a register instead and defer updating main memory until much later)." That's more akin to what I'm dealing with, but I think Mark's point is correct: merely guaranteeing that the pointer is updated in main memory doesn't guarantee anything about what it points at. – Stevens Miller May 22 '12 at 17:44
  • 1
    @Stevens - It also doesn't guarantee that *other* CPUs see the changes. They might have separate caches. And we don't know where the threads are running. – Bo Persson May 22 '12 at 17:52
  • @Bo: Yipes! That could be a real problem. Okay, I'm going to give it up on `volatile`. Sounds like memory barriers are where my focus should be. Thanks. – Stevens Miller May 22 '12 at 17:55

1 Answers1

3

Why can't I assign a volatile FILE* to a FILE*?

Because C++ has strict type checking and you cannot assign types that do not match to each other.
One needs to use casting operators if that be the case, however it is important to note that using them incorrectly might lead to Undefined Behaviors as well.
This behavior is same as for the const qualifier.

As a side note as already mentioned in comments, volatile is not the way to go here.

Alok Save
  • 202,538
  • 53
  • 430
  • 533
  • Dropping volatile would make everything easy, but now I must ask why that's safe. I thought volatile was how you protected against one thread changing a cached value that another thread might read. That's what I gleaned from the MSDN where it says, "The volatile keyword is a type qualifier used to declare that an object can be modified in the program by something such as the operating system, the hardware, or a concurrently executing thread." – Stevens Miller May 22 '12 at 15:42
  • @StevensMiller that doesn't give any ordering, atomicity, or visibility guarantees. It only means the compiler can't do certain optimisations by assuming the value won't change. – R. Martinho Fernandes May 22 '12 at 15:44
  • 1
    @StevensMiller: This should get you started: [Why is volatile not considered useful in multithreaded C or C++ programming?](http://stackoverflow.com/questions/2484980/why-is-volatile-not-considered-useful-in-multithreaded-c-or-c-programming) – Alok Save May 22 '12 at 15:44
  • @Stevens : In VC++, `volatile` implies atomicity as well as a memory barrier (and has since at least VC++ 2003), but that's only an extension (even the docs note "Microsoft Specific") and does not hold true for _standard_ C++, which is what everyone else is talking about. – ildjarn May 22 '12 at 15:44
  • If you're really modifying the `FILE` object from multiple threads, mere `volatile` won't protect you anyway. And even if it did, assigning it to a `FILE*` would make you lose those protections. Use real multithreading features instead; maybe a critical section would suit your needs. – Rob Kennedy May 22 '12 at 15:47
  • @Als: Thanks for the reference. It says, "For thread-safe accesses to shared data, we need a guarantee that the read/write actually happens (that the compiler won't just store the value in a register instead and defer updating main memory until much later)... volatile does guarantee the first point." That seems like what I want, assuming my ordering and so on is correctly handled by my synch objects. – Stevens Miller May 22 '12 at 16:06
  • Marked Als's answer as valid, since he directly addressed the actual question I asked. Maybe I _should_ have asked this: what's a good way to be sure that when thread A sets a variable to value X, thread B will subsequently read X from that same variable? (Alternatively: assuming two threads are properly synchronized so as to avoid atomicity and ordering problems, what's a good way for them to share reliable read/write access to the same variable?) – Stevens Miller May 22 '12 at 16:40
  • 1
    @StevensMiller: Sorry for delayed response.I was a bit caught up.As quoted from the answer `volatile` does guarantee the variable is not cached but is actually modified but it doesn't provide the reordering guarantee.What you need is actually an Memory barrier and most synchronization constructs implicitly provide an memory barrier.A simple mutex or an semaphore should serve you well. – Alok Save May 22 '12 at 17:51
  • @Als. Thanks! I am getting schooled up on memory barriers as I write this (ain't windowed GUIs grand?). Sounds like it may very well be just what I need. I do appreciate the guidance. – Stevens Miller May 22 '12 at 17:52