4

Why is a condition_variable not MoveConstructible (as per http://en.cppreference.com/w/cpp/thread/condition_variable)? That prohibits inclusion in lots of containers (eg. std::unordered_map) that move things around.

This forces people to use a unique_ptr which incurs one extra heap allocation, which things like make_shared were built to solve. Also if one doesn't have a pool allocator this can become quite inefficient.

Curious
  • 20,870
  • 8
  • 61
  • 146
  • Possibly it would have been too burdensome to make it thread safe. You would not want a notify to happen during a move of the variable. – NathanOliver Jul 06 '16 at 19:55
  • @NathanOliver Aren't all functions in the interface of a `condition_variable` thread safe anyway? I'm just thinking about why that should have been a problem – Curious Jul 06 '16 at 19:57
  • I think it just does not make any sense. Consider a case where a condition variable is in wait state and another thread copied it. What should be the state of the copied condition variable ? If you say it should be like in its default constructed state then again copying really made no sense. Also, it depends on whether the native condition variable structure (used inside `std::condition_variable`) say `pthread_cond_t` is allowed to be copied without invoking UB or not. – Arunmu Jul 06 '16 at 20:07
  • A `std::condition_variable` is not CopyConstructible, MoveConstructible, CopyAssignable, MoveAssignable. Are you particularly interested in knowing why it is not MoveAssignable, or do you want to know "why" for all of those traits? – R Sahu Jul 06 '16 at 20:08
  • 4
    It is a synchronization construct that multiple threads are (potentially) using simultaneously. How could you move it safely? E.g., suppose it directly contains a spinlock. Some thread is spinning on a given _address in your process address space_ and you're going to move it out from under it? – davidbak Jul 06 '16 at 20:41
  • 3
    `condition_variable` was born here: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2406.html#condition It is not an easy read, but is fairly complete. It refers to `condition_variable` as `cond_var`. This is nothing but a rename that happened during standardization. "Born here" is an exaggeration. `condition_variable` is the product of existing practice over decades prior to C++11. There was a deliberate decision to not be inventive because this is a very tricky and low-level API. `condition_variable_any` is an example of a higher-level API that can be built on top of the lower API. – Howard Hinnant Jul 07 '16 at 04:22
  • 1
    Most of the things Jonathan Wakely wrote in [this answer](http://stackoverflow.com/a/14304618/2756719) about `std::mutex` apply mutatis mutandis to `condition_variable` too. – T.C. Jul 07 '16 at 04:29

1 Answers1

7

condition_variable is a synchronization construct that multiple threads are (potentially) using simultaneously. (In fact, that's its purpose.) How could you move it safely? E.g., suppose it directly contains a spinlock. Some thread is spinning on a given address in your process address space and you're going to move the object out from under it?

Any kind of user-mode synchronization construct can't be moved. The thing that does the actual synchronization needs a fixed address. You could force the object to do all of its real work on a heap-allocated object that wouldn't be moved - and there you go right to the indirection to the heap that you wanted to avoid. (Kernel-mode synchronization constructs can be moved: you've got a handle to some OS thing. But they're much more expensive to use.)

They can't be copied either - because what would that mean?

It just has to be this way. Your design must account for it, that's all.

(And I don't really understand the second paragraph of your question. make_shared was built to make ref counts less expensive and don't have anything to do with moving stuff around. A pool allocator may or may not improve any particular situation, much less this one, and you won't know unless you measure it.)

davidbak
  • 5,775
  • 3
  • 34
  • 50