6

Is it safe to use the :volatile-mutable qualifier with deftype in a single-threaded program? This is a follow up to this question, this one, and this one. (It's a Clojure question, but I added the "Java" tag because Java programmers are likely to have insights about it, too.)

I've found that I can get a significant performance boost in a program I'm working on by using :volatile-mutable fields in a deftype rather than atoms, but I'm worried because the docstring for deftype says:

Note well that mutable fields are extremely difficult to use correctly, and are present only to facilitate the building of higher level constructs, such as Clojure's reference types, in Clojure itself. They are for experts only - if the semantics and implications of :volatile-mutable or :unsynchronized-mutable are not immediately apparent to you, you should not be using them.

In fact, the semantics and implications of :volatile-mutable are not immediately apparent to me.

However, chapter 6 of Clojure Programming, by Emerick, Carper, and Grand says:

"Volatile" here has the same meaning as the volatile field modifier in Java: reads and writes are atomic and must be executed in program order; i.e., they cannot be reordered by the JIT compiler or by the CPU. Volatiles are thus unsurprising and thread-safe — but uncoordinated and still entirely open to race conditions.

This seems to imply that as long as accesses to a single volatile-mutable deftype field all take place within a single thread, there is nothing to special to worry about. (Nothing special, in that I still have to be careful about how I handle state if I might be using lazy sequences.) So if nothing introduces parallelism into my Clojure program, there should be no special danger to using deftype with :volatile-mutable.

Is that correct? What dangers am I not understanding?

Community
  • 1
  • 1
Mars
  • 8,689
  • 2
  • 42
  • 70
  • 2
    If you have a single java thread, and `volatile` has the same meaning as java's volatile - you do not need `volatile` at all. – ZhongYu May 27 '15 at 05:45
  • atomicity is another issue. you could have concurrency issues even with one thread. But I'm not familiar with Clojure so I can't comment. – ZhongYu May 27 '15 at 05:46
  • bayou.io: The volatile keyword also enforces that the program order of calls on volatile variables can not be reordered. That might have some use in some case, however I can't think of one. – Christophe De Troyer May 27 '15 at 09:25
  • Thanks @bayou.io, @ChristopheDeTroyer. Re single-threaded `volatile` irrelevant: That helps clarify the situation. (fyi though, in Clojure it's generally rarely possible to do simple assignment to a variable except when the variable was defined in another language such as Java. `:volatile-mutable` with `deftype` might be the closest Clojure gets to simple assignment to a member variable as in Java.) Re concurrency/atomiticy issues with one thread: Do you mean when the thread is switching between different tasks? Re no reordering of calls: I don't think it matters for me, but I don't mind. – Mars May 27 '15 at 16:30
  • @ChristopheDeTroyer - within a single thread, program orders must be preserved, for all reads/writes. (or actually, reordering could be performed by the compiler/runtime, but it must not be observed by the application; the code is executed in program order, as far as the application can see) – ZhongYu May 27 '15 at 18:52
  • @Mars - yes, i mean, like time sharing on a single CPU. "volatile" won't be needed, but still, atomicity could be an issue. – ZhongYu May 27 '15 at 18:52
  • 3
    For single threaded use, there is no point to volatile, and unsynchronized-mutable will be faster as you will not have unnecessary memory barriers. – pete23 May 29 '15 at 08:17
  • 3
    unsynchronized-mutable is, of course, just a bare field in Java – pete23 May 29 '15 at 08:18
  • @pete23, thank you! That is *very* helpful information--both comments. I didn't realize that that's what unsynchronized-mutable meant. A bare field is something I understand. – Mars May 29 '15 at 17:10
  • 2
    Thank you @SotiriosDelimanolis for the bounty! – Mars Jul 19 '15 at 05:24

1 Answers1

3

That's correct, it's safe. You just have to be sure that your context is really single-threaded. Sometimes it's not that easy to guarantee that.

There's no risk in terms of thread-safety or atomicity when using a volatile mutable (or just mutable) field in a single-threaded context, because there's only one thread so there's no chance of two threads writing a new value to the field at the same time, or one thread writing a new value based on outdated values.

As others have pointed out in the comments you might want to simply use an :unsynchronized-mutable field to avoid the cost introduced by volatile. That cost comes from the fact that every write must be committed to main memory instead of thread local memory. See this answer for more info about this.

At the same time, you gain nothing by using volatile in a single-threaded context because there's no chance of having one thread writing a new value that will not be "seen" by other thread reading the same field. That's what a volatile is intended for, but it's irrelevant in a single-thread context.

Also note that clojure 1.7 introduced volatile! intended to provide a "volatile box for managing state" as a faster alternative to atom, with a similar interface but without it's compare and swap semantics. The only difference when using it is that you call vswap! and vreset! instead of swap! and reset!. I would use that instead of deftype with ^:volatile-mutable if I need a volatile.

Community
  • 1
  • 1
nberger
  • 3,659
  • 17
  • 19
  • Thanks--among other things, I didn't know about `volatile!` etc., and it's useful to know about the caching issue. – Mars Jul 19 '15 at 04:55