Enlightened


  • Discourse touched me in a no-no place

    @Bulb said:

    Any process can fuck with any window.

    At that level, you don't want to let untrusted code access the display at all. There's an effectively-equivalent problem on Windows with message sending, and the solution is the same: don't run random shit downloaded off the internet. :)


  • area_pol

    @Bulb said:

    To be honest I don't understand why he even talks about memory safety. GUI widgets are generally not thread-safe and I don't think Enlightenment is an exception.

    EFL is not only widgets, and not even once thread safety is mentioned.

    @Bulb said:

    Const is not about memory safety anyway. It is about contract.

    A contract about read thread safety. It can be violated, of course, if someone is an idiot and de-consts data for modification. But assuming nobody does that evil, const means safe to read from multiple threads. But there's a catch:

    @Bulb said:

    Function receiving a const pointer (or reference) promises not to modify the pointee via that pointer and function returning const pointer requests the pointee not to be modified via that pointer.

    Pointers and references. Here you cannot apply those rules, unless you know all other pointers/references are also const, implying essentially a const object. You correctly identified that trap, but if the object is const, read thread-safety should be ensured.

    Also, don't forget const methods which should have the same guarantee. Again, unless we're talking about idiot programmers who don't read the Standard and think they know better.


  • Discourse touched me in a no-no place

    @NeighborhoodButcher said:

    if the object is const, read thread-safety should be ensured.

    Only works if the object itself is const. Const references don't give you that promise unless you've got a higher-level constraint of non-change for the period of parallel use. (Many algorithms can actually give that guarantee, FWIW.)



  • @NeighborhoodButcher said:

    Here you cannot apply those rules, unless you know all other pointers/references are also const, implying essentially a const object. You correctly identified that trap, but if the object is const, read thread-safety should be ensured.

    I don't care whether the object is const. The component that owns it can modify it whatever it likes. However if it only gives out const pointers to it, it can be certain it is still under control of it's content. Therefore const does not itself ensure any safety, but it limits the amount of code that needs to be considered for ensuring it and that's what the programmer wants.


  • area_pol

    @dkf said:

    Only works if the object itself is const.

    That's exactly what I said. Const pointers/references don't mean a thing, except assuming the callee doesn't modify it.

    @Bulb said:

    I don't care whether the object is const. The component that owns it can modify it whatever it likes.

    If the object is const, the owner cannot modify its visible state. If it does - someone did a pretty bad job at programming and should go back to making Excel reports or whatever.

    @Bulb said:

    However if it only gives out const pointers to it, it can be certain it is still under control of it's content. Therefore const does not itself ensure any safety, but it limits the amount of code that needs to be considered for ensuring it and that's what the programmer wants.

    You actually said the exact same thing as I - const pointers/references don't mean a thing unless the object itself is const. Which implies all pointers/references also need to be const.


  • Discourse touched me in a no-no place

    @NeighborhoodButcher said:

    If the object is const, the owner cannot modify its visible state.

    Nope. 😃 If the object is using the PIMPL pattern, a const object may have things that appear to look like state that can change. Yes, this is rules-lawyering, but it's an indication that const itself is not the real guarantee that you're looking for, and that you're using it as a proxy for the real guarantee: thread-safety. If that's the thing that you want to guarantee, you should come right out and state it explicitly.

    Proper thread safety is awkward, because it's not a true local property and so can't be always established by pure local analysis. Deadlock-freedom and livelock-freedom are more awkward still; proving those sorts of things requires whole program analysis at least, and possibly more if you're doing a multi-process application or working with client-server systems. Parallelism introduces a lot of complexity…


  • area_pol

    @dkf said:

    Nope. If the object is using the PIMPL pattern, a const object may have things that appear to look like state that can change. Yes, this is rules-lawyering, but it's an indication that const itself is not the real guarantee that you're looking for, and that you're using it as a proxy for the real guarantee: thread-safety. If that's the thing that you want to guarantee, you should come right out and state it explicitly.

    That is why we distinguish visible state from internal state. Visible state should not be modified by const objects, while internal state can change in any way they wish. That's why internal state needs to be synchronized to maintain the const contract. Take a look at Herb Sutter's presentation I posted earlier. He has explained that in a very fine way.

    @dkf said:

    Parallelism introduces a lot of complexity…

    On this we agree. IMHO that's why people should first read the C++ Standard and understand what all the notions mean (including const), before diving into threads. The topic can be quite complex sometimes.



  • @NeighborhoodButcher said:

    If the object is const, the owner cannot modify its visible state.

    Forget about what the owner can do. That can only be found by inspecting the owner and for any other code that just has a const reference to something it is impossible to know¹.

    Of course some consts—on variables and member variables—do not allow the owner to modify such object and therefore give the guarantee you are looking for. But that is minority of uses, since most objects start their life as mutable.

    @NeighborhoodButcher said:

    You actually said the exact same thing as I - const pointers/references don't mean a thing unless the object itself is const.

    Yes, we agree in that bit.

    @NeighborhoodButcher said:

    Which implies all pointers/references also need to be const.

    But not in the conclusion. You are looking for something that will guarantee, globally, that access to the object can be accessed in parallel. I am looking for guarantee to the owner that can safely lend the object without creating writes that could invalidate it's invariants or require additional synchronization. And I am saying the later is exactly what I get from const in all it's proper uses.


  • area_pol

    @Bulb said:

    Forget about what the owner can do. That can only be found by inspecting the owner and for any other code that just has a const reference to something it is impossible to know¹.

    Of course some consts—on variables and member variables—do not allow the owner to modify such object and therefore give the guarantee you are looking for. But that is minority of uses, since most objects start their life as mutable.

    I have a feeling you didn't understood me or Herb correctly, so let me take it from the top.
    Let's start with a lemma: const methods cannot modify visible state of an object. Internal state can be freely modified, since its side-effects are hidden by definition.
    Let's add another lemma: data race cannot occur if no stores to memory are present => pure loads are thread-safe by definition.
    Given the above, calling const methods which do not modify visible state (equivalent with no stores from the caller perspective) is thread-safe. Mixing it with non-const methods, which can modify visible state is not thread-safe on its own.
    Any method which modifies internal state, const or not, must ensure the above is true, therefore internal state modification should be synchronized if the state is touched in any way (load or store) inside const methods.
    I hope this clears things up. Note there was no mention of ownership, pointers, references etc. The above should hold in any circumstances. That's the interpretation of the Standard.

    @Bulb said:

    But not in the conclusion. You are looking for something that will guarantee, globally, that access to the object can be accessed in parallel. I am looking for guarantee to the owner that can safely lend the object without creating writes that could invalidate it's invariants or require additional synchronization. And I am saying the later is exactly what I get from const in all it's proper uses.

    If the present interpretation of const in C++ holds and nobody does anything stupid, like const_cast, there should not be a problem with synchronization and const.



  • I think there's a difference between a const-declared variable and a const-declared function parameter. A const-declared function parameter, the function is saying, "I will never cause this argument to be modified", which is what's at issue here, because somewhere along the line the const reference gets passed as a non-const argument to a different function, breaking the contract.



  • @riking said:

    I think there's a difference between a const-declared variable and a const-declared function parameter. A const-declared function parameter, the function is saying, "I will never cause this argument to be modified", which is what's at issue here, because somewhere along the line the const reference gets passed as a non-const argument to a different function, breaking the contract.

    Wouldn't that violate the constness of the outer function's const-ref param though? (Presuming the inner function is receiving its parameter by-mutable-ref that is -- if it's being passed that param by value, the copy ctor steps in and saves your butt ;) )



  • @riking said:

    I think there's a difference between a const-declared variable and a const-declared function parameter.
    No, there isn't. Const means "I will only call const methods on this object, and only pass it to functions that take it by const", be it an object or a reference to an object.


  • area_pol

    @riking said:

    I think there's a difference between a const-declared variable and a const-declared function parameter. A const-declared function parameter, the function is saying, "I will never cause this argument to be modified", which is what's at issue here, because somewhere along the line the const reference gets passed as a non-const argument to a different function, breaking the contract.

    We're not talking about function parameters, but const in general. If the object is const in every thread, that means every pointer/reference to it is also const in every thread (again - assuming the programmer isn't stupid and didn't const_cast it). That in turn implies that all access to this object is thread-safe, with respect to visible state.



  • @NeighborhoodButcher said:

    If the object is const in every thread, that means every pointer/reference to it is also const in every thread (again - assuming the programmer isn't stupid and didn't const_cast it). That in turn implies that all access to this object is thread-safe, with respect to visible state.

    Yes, but that's a pretty big if most of the time. Just because you see a const-ref or a const-ptr somewhere, there's no guarantee that that's the only reference to that object, and that no other reference is non-const.

    And if an object is accessed by multiple threads, it's almost guaranteed that some of the sites will access it by reference; the only thing you see at such a site is a const-ref, at which point you won't (without further analysis) know whether or not there's a non-const ref to it somewhere else (such as a different thread). This is why, IMO, I find it dangerous to claim that const would imply thread-safety.

    (If you can guarantee that an object is const from the get go -simple example would be global const instances-, then yes, you may assume thread safety.)


  • area_pol

    @cvi said:

    Yes, but that's a pretty big if most of the time. Just because you see a const-ref or a const-ptr somewhere, there's no guarantee that that's the only reference to that object, and that no other reference is non-const.

    Of course it's a big if. Nobody denies it, but we're talking about the meaning of const in terms of the Standard.

    @cvi said:

    And if an object is accessed by multiple threads, it's almost guaranteed that some of the sites will access it by reference; the only thing you see at such a site is a const-ref, at which point you won't (without further analysis) know whether or not there's a non-const ref to it somewhere else (such as a different thread). This is why, IMO, I find it dangerous to claim that const would imply thread-safety.

    Claiming there are other possible non-const refs doesn't imply const doesn't mean thread safety. It only implies you cannot assume thread safety in this kind of situation, so you must take care of the synchronization yourself. Your example explicitly breaks the assumption of accessing only by const methods and that's what we're talking about.



  • @NeighborhoodButcher said:

    Nobody denies it, but we're talking about the meaning of const in terms of the Standard.

    Ref to the standard language that supports this? (I googled a bit, and found some discussion, but no reference to the standard. Sutter's talk is referenced too, but with some disputes going on.)

    @NeighborhoodButcher said:

    Claiming there are other possible non-const refs doesn't imply const doesn't mean thread safety. It only implies you cannot assume thread safety in this kind of situation, so you must take care of the synchronization yourself.

    Which is my point - seeing something const does not mean that one can assume that it's thread safe for reading (with some very specific exceptions), and that doing so would be dangerous.



  • @cvi said:

    Which is my point - seeing something const does not mean that one can assume that it's thread safe for reading (with some very specific exceptions), and that doing so would be dangerous.

    I think there's some confusion here. Const doesn't make thread-unsafe objects magically thread-safe. If you have non-const references to an object in different threads, and the object has no thread safety guarantees, then nothing you do with the object is safe. You can't even trust it hasn't already been destroyed from under you. So you can't do anything with this object, and talking about const is premature. Make sure you even have an object before trying to read it.

    So we need to build some thread safety before we pass an object to multiple threads. The most obvious place to start is lifetime. So multiple threads have a handle to an object, possible ref-counted or maybe it's owned by whoever spawned the threads and won't die until the last thread returns.

    For argument's sake, we wrap the object in a shared_ptr. So we know at least that the object still exists. Still, just having an object around doesn't help us much. What can we do with an object? Well, the most basic operation is reading from it. Here is where const comes in. In a correctly designed object, which obeys the requirements set in the standard (and the STL does), const methods should be safe to read from. This is guaranteed safe automatically for every non-mutable member in the object (so long as those members follow the rules, and so on).

    The issue is, what about mutable members? The Sutter talk explains that the new language in the standard means that instead of thinking about "logical vs bitwise const", the correct way to think of it is synchronized access. So long as access to mutable members is done in a synchronized manner, an object fulfills the requirements of the standard.

    So, going back to our previously unsafe object, we now know that wrapping it in a shared_ptr will ensure it's lifetime, and passing it as const (and ensuring all the methods follow the requirements of the standard, like the STL does, which amounts to "sync access to mutable members") will make it safe for reading.

    This may not seem like much, but it's actually pretty valuable. It means that a lot of code that was uncertain before can now provide guarantees. If you don't have mutable members, your const objects are safe to read by default. If you have mutable members, you only need to check that access is synced.

    Of course, if you pass a thread-unsafe object to multiple threads, you're just as fucked as you were before. Const doesn't magically unfuck you. But it makes thread safety easier to think about. Write safety is still difficult, and may require re-engineering your object, but read safety is trivially attainable.


  • area_pol

    @cvi said:

    Ref to the standard language that supports this? (I googled a bit, and found some discussion, but no reference to the standard. Sutter's talk is referenced too, but with some disputes going on.)

    This is going to be a complex one, but here it goes (I'm going to use the final C++14 Standard). Start with 7.1.6.1.4, which states that const-qualified objects cannot be modified (except for mutable), unless someone wants undefined behavior. Go through 1.10 and observe what is a side effect and how it constitutes to data race. Then note that for no data race to occur, there must be a happens-before relationship between stores and loads (usually achieved by synchronization objects or simple atomic operations). That implies, for each non-modifying (non-mutable or const) load operation, for which the store happened-before, the results are well defined (1.10.15 onwards). If you put it all together you'll a picture of an object with some visible state, which must be properly synchronized to ensure happens-before relationship and which can be later read in parallel with a well defined value (1.10.18). This also requires that modifying operations on const objects (via mutable) need to be explicitly synchronized to preserve the happens-before relationship, unless someone want a data race (1.10.23).

    @cvi said:

    Which is my point - seeing something const does not mean that one can assume that it's thread safe for reading (with some very specific exceptions), and that doing so would be dangerous.

    I'm not arguing that. Don't confuse seeing something as const with it actually being const.



  • @NeighborhoodButcher said:

    This is going to be a complex one, but here it goes (I'm going to use the final C++14 Standard). Start with 7.1.6.1.4, which states that const-qualified objects cannot be modified (except for mutable), unless someone wants undefined behavior. Go through 1.10 and observe what is a side effect and how it constitutes to data race. Then note that for no data race to occur, there must be a happens-before relationship between stores and loads (usually achieved by synchronization objects or simple atomic operations). That implies, for each non-modifying (non-mutable or const) load operation, for which the store happened-before, the results are well defined (1.10.15 onwards). If you put it all together you'll a picture of an object with some visible state, which must be properly synchronized to ensure happens-before relationship and which can be later read in parallel with a well defined value (1.10.18). This also requires that modifying operations on const objects (via mutable) need to be explicitly synchronized to preserve the happens-before relationship, unless someone want a data race (1.10.23).

    Guess I will have some reading to do :-) (Thanks!)

    @NeighborhoodButcher said:

    I'm not arguing that. Don't confuse seeing something as const with it actually being const.

    No, I'm aware of the difference there.

    My point, from a practical perspective, is that seeing something const doesn't imply it's safe to read from (and guaranteeing that would be rather difficult). To see if it's actually const, you'd need to do some digging. But for "actually const" objects, fair enough, those you can safely read from.


  • area_pol

    @cvi said:

    Guess I will have some reading to do (Thanks!)

    Multithreading chapter a very interesting read, but can be tough. You have to generalize it a bit for whole objects, since the chapter is more about simpler primitives, but the logic is pretty much the same - if a write happened-before a load, you can read stuff with whatever threads you like => if a modification of some visible state of an object happened-before a const read, all is fine. Internal state must also follow these rules, so some synchronization must take place even in const methods.



  • At the risk of looking like I'm a bit slow, the above still requires that the shared object is const in all contexts, right? (I.e., "actually const" as mentioned by @NeighborhoodButcher, not just seemingly const).

    So,

    std::shared_ptr<Foo const> p = std::make_shared<Foo>();
    std::thread t( some_function, p );
    other_function( p );
    ...
    

    would always be safe since nobody can (legally) change the instance of Foo, whereas

    std::shared_ptr<Foo> p = std::make_shared<Foo>();
    std::thread t( some_function, p );
    other_function_possibly_modifying_the_foo( p );
    ...
    

    would not necessarily.

    Edit: assume that some_function() in both cases accepts a std::shared_ptr<Foo const> or equivalent as it's argument.



  • Yes, that's about right. Since p is const, only const methods can be called on it. If the const methods are properly written (meaning, any mutable members have synchronized access), the first example is guaranteed to not have race conditions.

    Keep in mind that the bit about mutable members is important. Const methods can modify mutable members, so it's not true that calling a const method won't change an object. It shouldn't change the visible state of the object, which is the "logical vs bitwise" const argument that the new language lays to rest. There's no longer any need to worry about what "logical const" means. Just ensure access to mutable members is synchronized properly.

    In the second example, you can be certain that t won't mess with you so long as you don't call other_function_possibly_modifying_the_foo until you join the thread. That is, assuming we know nothing about Foo other than it respects the standard.

    EDIT- Before the new standard, there was no way to make such a guarantee without inspecting the definition of the class. Const methods have always been able to modify mutable members, so you couldn't trust that a const object was not going to have race conditions.



  • @NeighborhoodButcher said:

    I have a feeling you didn't understood me or Herb correctly

    I don't understand you. I believe I do understand Herb perfectly though.

    @NeighborhoodButcher said:

    Given the above, calling const methods which do not modify visible state (equivalent with no stores from the caller perspective) is thread-safe.

    “Thread-safe” can mean a lot of things. We should be more specific here. A const function is thread-safe with respect to itself and other functions on the same instance.

    Now that we have this guarantee, that we do have and that I never argued against, we need to ensure that the overall code using the const functions is correct. To which we take advantage of the guarantees given by proper use of const.

    And to that you said:

    @NeighborhoodButcher said:

    Which implies all pointers/references also need to be const.

    but most of the time that won't be true, because most objects start their life as mutable. It would be nice if it was true; I like functional proponents and prefer to have my objects immutable whenever possible; but fact is that it's not always possible and there are many mutable objects around.

    However const is still useful even if the object is mutable in some parts of the code (which I called the owner). Because it provides guarantee to that part of code about what it won't do with the object, so the const methods don't need to be further checked.

    @NeighborhoodButcher said:

    Given the above, calling const methods which do not modify visible state (equivalent with no stores from the caller perspective) is thread-safe. Mixing it with non-const methods, which can modify visible state is not thread-safe on its own.Any method which modifies internal state, const or not, must ensure the above is true, therefore internal state modification should be synchronized if the state is touched in any way (load or store) inside const methods.I hope this clears things up. Note there was no mention of ownership, pointers, references etc. The above should hold in any circumstances. That's the interpretation of the Standard.

    That falls under the “proper” used of const. I never argued with that.

    @NeighborhoodButcher said:

    If the present interpretation of const in C++ holds and nobody does anything stupid, like const_cast, there should not be a problem with synchronization and const.

    That falls outside of “proper” use of const. C++ gives you enough rope to shoot yourself in the foot. Just don't do it.

    As mentioned long ago in this thread, Enlightenment uses const improperly, which is the reason it was brought up.

    @NeighborhoodButcher said:

    We're not talking about function parameters, but const in general. If the object is const in every thread…

    … which is exactly what const function parameters won't guarantee and then they sound to be of limited usefulness. Except they are extremely useful. They are also a lot more common than const variables (variable can be either member or non-member; both are variables in C++ nomenclature).

    @NeighborhoodButcher said:

    Of course it's a big if. Nobody denies it, but we're talking about the meaning of const in terms of the Standard.

    So then we should be talking about what the meaning of const in terms of the standard is when that condition is not satisfied. And that's what I am trying to.

    @cvi said:

    Sutter's talk is referenced too

    I believe it is not actually mentioned anywhere in the standard, only in the Sutter's talk.

    It also does not follow from the standard:

    @NeighborhoodButcher said:

    This also requires that modifying operations on const objects (via mutable) need to be explicitly synchronized to preserve the happens-before relationship, unless someone want a data race (1.10.23).

    so the standard does not say that modifying a mutable without synchronization is always wrong, only that doing it in parallel is a data race. Which means that there may be a const method that does it and it does not invoke Undefined Behaviour™ on it's own, it is only non-thread safe.

    Making such function is likely shooting yourself in the foot, for which C++ just gave you enough rope. It is silly, but it is not prohibited.


  • area_pol

    @Bulb said:

    I don't understand you. I believe I do understand Herb perfectly though.

    That's good because we're talking about the same thing.

    @Bulb said:

    “Thread-safe” can mean a lot of things. We should be more specific here. A const function is thread-safe with respect to itself and other functions on the same instance.

    Now that we have this guarantee, that we do have and that I never argued against, we need to ensure that the overall code using the const functions is correct. To which we take advantage of the guarantees given by proper use of const.

    I already pointed out at least two times, that we're assuming we're dealing with properly written code, not some garbage. When people intentionally mess things up, no code construct will save them.

    @Bulb said:

    That falls outside of “proper” use of const. C++ gives you enough rope to shoot yourself in the foot. Just don't do it.

    Looks like we both agree on the meaning of const then and why it should be used properly.

    @Bulb said:

    … which is exactly what const function parameters won't guarantee

    Never said they make that guarantee - quite the opposite. I never questioned the usefulness of const parameters - this is about const in general, not specific use cases. Const pointers/references don't give any guarantee by themselves.

    @Bulb said:

    I believe it is not actually mentioned anywhere in the standard, only in the Sutter's talk.

    It is implied from the Standard. You can do any loads as long as stores happened-before them.

    @Bulb said:

    so the standard does not say that modifying a mutable without synchronization is always wrong, only that doing it in parallel is a data race.

    The Standard doesn't say any of those things. Modifying mutable in parallel is not a data race. Doing it without proper synchronization can lead to a data race (1.10.23).



  • @Bulb said:

    Making such function is likely shooting yourself in the foot, for which C++ just gave you enough rope. It is silly, but it is not prohibited.

    There's guidance in the standard that the compiler can't, for various reasons, enforce. This means that getting a program to compile and run isn't the same as following the standard. And if you don't follow the standard, you don't have a properly written program.

    For example, dereferencing a null pointer is always wrong. However, the compiler lets you write int boom = *(int*)0;. The syntax checks out, despite the fact that casting 0 to a pointer type results in a null pointer.

    Similarly, from a syntax perspective, all const does is limit you to calling const methods on the object or it's members, with the exception of mutable members. You could mark every member in an object mutable, every method const, and then const would mean nothing for that object. The compiler will let you, but it goes against the standard to do so.

    When @NeighborhoodButcher says "unless you want a data race", he's using a figure of speech. You can't choose to want a data race and have correct code, any more than you can "choose to have undefined behavior". Data races are undefined behavior, and are therefore wrong.

    It's not prohibited in the sense that the compiler won't emit an error, because it can't do the requisite static analysis to know if some piece of code will ever invoke undefined behavior. But it's forbidden by the language of the standard, which establishes certain requisites for things.



  • @Bulb said:

    Which means that there may be a const method that does it and it does not invoke Undefined Behaviour™ on it's own, it is only non-thread safe.

    If you know that code won't ever be called by code that uses it in a threaded context, then sure, it doesn't. It's the same situation of a function that takes a parameter by pointer. If you know no-one will ever call it with a null-pointer, not checking for a null pointer won't ever result in undefined behavior. The moment someone sends you a null pointer, though, you have undefined behavior.



  • @Kian said:

    There's guidance in the standard that the compiler can't, for various reasons, enforce.

    I am not talking about those thing. If the spec says it is Undefined Behaviour™, it counts as prohibited.

    @Kian said:

    If you know that code won't ever be called by code that uses it in a threaded context, then sure, it doesn't.

    It does not matter whether you are sure. On it's own it does not invoke undefined behaviour. Therefore, on it's own, writing a const functions that is nevertheless not safe is not prohibited by standard.



  • @Kian said:

    You can't choose to want a data race and have correct code

    No, but I can choose to prevent the data race by other means since I somehow found that the function is not thread-safe despite being const. And the code will still be correct. Because standard does not require const functions to be thread-safe.



  • @NeighborhoodButcher said:

    I already pointed out at least two times, that we're assuming we're dealing with properly written code, not some garbage. When people intentionally mess things up, no code construct will save them.

    Fallacy warning: No True Scotsman

    • C code compliant with the standard cannot violate memory saftey
    • C code that is not memory safe is not proper C
    • Therefore, proper C is memory safe


  • @riking said:

    Fallacy warning: No True Scotsman

    • C code compliant with the standard cannot violate memory saftey
    • C code that is not memory safe is not proper C
    • Therefore, proper C is memory safe

    That's not a fallacy. That is correct. Proper C code is memory safe. C code that is not memory safe is wrong, and has to be fixed. The standard is what lets you distinguish between bad code and good code. It's the set of rules you use to write good code.



  • @Bulb said:

    No, but I can choose to prevent the data race by other means since I somehow found that the function is not thread-safe despite being const. And the code will still be correct. Because standard does not require const functions to be thread-safe.

    Now it does, that's the point of Sutter's talk. I'll try to get back to you with why when I get off work, I can't go looking into the standard now.


  • area_pol

    I have to agree with @Kian here. Despite I detest C, properly written C code is memory safe. The issue is with that "properly written" part, which is so damn hard in C. Unless there's something abhorrent in a language specification itself, every language is safe, if used correctly. If someone invests relatively large amounts of resources, a really memory safe C can be achieved. (The question would be - what's the point anyway?)

    @Bulb said:

    Because standard does not require const functions to be thread-safe.

    You noticed I gave exact paragraphs about it earlier, right? Let me repeat that once again: if a store happens-before a load, the value is well defined and is the same in all threads* => you can concurrently do a const (load) operation on some shared state, provided the store to it was properly synchronized. It's all in 1.10 in the Standard, so don't go saying it's not.

    • No, no stores in between the loads.


  • @NeighborhoodButcher said:

    [...] if a store happens-before a load, the value is well defined and is the same in all threads* => you can concurrently do a const (load) operation on some shared state [...]

    • No, no stores in between the loads.

    I finally had time to watch the talk, and I think sort of dislike it because this is not stated clearly enough and not enough times. Sooner or later some clueless person is going to see the whole "const => thread safe" thing (without that gotcha) and create some unholy racy mess.

    The fact that the standard language (kinda) supports that concurrent reads are OK without explicit synchronization is nice, though (despite the roundabout way involving the standard library spec).



  • @cvi said:

    Sooner or later some clueless person is going to see the whole "const => thread safe" thing (without that gotcha) and create some unholy racy mess.

    Clueless people touching threaded code already create unholy racy messes, though :)



  • @Kian said:

    Clueless people touching threaded code already create unholy racy messes, though :)

    True.

    But the fewer arguments (however wrong) they have to support their position, the better.



  • @NeighborhoodButcher said:

    @Bulb said:
    Because standard does not require const functions to be thread-safe.

    you can concurrently do a const (load) operation on some shared state

    You are talking about truly const primitive operation. I am talking about const-qualified member function.

    @NeighborhoodButcher said:

    provided the store to it was properly synchronized.

    And nothing prohibits const-qualified member function from doing unsynchronized writes to mutable members. According to the standard it causes a data-race if called in multiple threads, but the function itself is not illegal. Only calling it in multiple threads is.

    Therefore writing a const-qualified member function that modifies mutable member without synchronization is not prohibited by the standard. I don't say it is wise, but it is not prohibited by the C++ standard and can't be efficiently checked by C++ compiler.


  • area_pol

    @Bulb said:

    You are talking about truly const primitive operation. I am talking about const-qualified member function.

    The logic can be generalized, because at some level, you are dealing with primitive data. You can therefore propagate the notion.

    @Bulb said:

    And nothing prohibits const-qualified member function from doing unsynchronized writes to mutable members. According to the standard it causes a data-race if called in multiple threads, but the function itself is not illegal. Only calling it in multiple threads is.

    You are correct. That's why I said the exact same thing earlier (probably two times now).

    @Bulb said:

    Therefore writing a const-qualified member function that modifies mutable member without synchronization is not prohibited by the standard. I don't say it is wise, but it is not prohibited by the C++ standard and can't be efficiently checked by C++ compiler.

    As you said yourself, it causes undefined behavior by the Standard. Now we can debate if this means it's "against the Standard" or not. Instead I rather keep to the rule of writing code, which effects will be well defined, which implies synchronization on internal state (mutable state).



  • @Bulb said:

    Therefore writing a const-qualified member function that modifies mutable member without synchronization is not prohibited by the standard. I don't say it is wise, but it is not prohibited by the C++ standard and can't be efficiently checked by C++ compiler.

    The compiler is not the standard. Getting something to compile is not the same as writing standard compliant code. I gave before the trivial example: int boom = *((int*)0); This compiles, and is against the standard because it's attempting to dereference a null pointer.

    Undefined behavior is the answer to the problem of it being technically impossible for the compiler to check if any particular piece of code goes against the standard. The standard thus says "the right way to do something is this. If you stray from that, that's undefined".

    Undefined is against the standard. Races are undefined. Races are against the standard. Const should not cause races. Const methods that cause races are against the standard, even if they compile.



  • @NeighborhoodButcher said:

    The logic can be generalized, because at some level, you are dealing with primitive data.

    No, it can't. Because mutable breaks it.

    @NeighborhoodButcher said:

    As you said yourself, it causes undefined behavior by the Standard.

    My "it" is "writing a const-qualified function that modifies mutable state in non-thread-safe way. And I didn't say that causes undefined behaviour, because by itself it does not.

    @Kian said:

    The compiler is not the standard.

    I did not say so.

    @Kian said:

    Undefined is against the standard. Races are undefined. Races are against the standard.

    Yes.

    @Kian said:

    Const should not cause races.

    For functions taking arguments (including invocant) by const references, not said anywhere.

    @Kian said:

    Const methods that cause races are against the standard, even if they compile.

    No, they are not.



  • @Bulb said:

    No, they are not.

    From the Sutter talk (here are the slides if you don't want to watch the video):

    Q: What does const mean in C++98?

    A: We’ve taught a generation of C++ developers that “const means logically const, not physically/bitwise const.”

    But now:

    Standard language rules, §1.10/4 and /21:

    Two expression evaluations conflict if one of them modifies a memory location (1.7) and the other one accesses or modifies the same memory location.

    The execution of a program contains a data race if it contains two conflicting actions in different threads, at least one of which is not atomic, and neither happens before the other. Any such data race results in undefined behavior.

    and

    Standard library rules: §17.6.5.9/1-3:

    This section specifies requirements that implementations shall meet to prevent data races (1.10). […]

    A C++ standard library function shall not directly or indirectly modify objects (1.10) accessible by threads other than the current thread unless the objects are accessed directly or indirectly via the function’s non-const arguments, including this.

    Sure, those are standard library rules. But

    Q: So what?

    A: So any copyable type T that anyone could ever copy in a std::pair<T,…> or std::pair<…,T>(i.e., every copyable type) is required to support thread-safe copying – i.e., copying from the same object in multiple threads without synchronization.

    Which means

    The standard library guarantees const -> thread-safe for:

    • its own types; and
    • all operations it can call on your types.

    so just do it for all const operations, as the stdlib does.

    The standard requires that standard library functions can't modify or cause data races on const arguments. This means that any types you could use with the standard library, including user defined types, have to meet these requirements or they are not standard compliant.

    Thus: const -> thread safe (bitwise const or internally synchronized).


  • area_pol

    @Bulb said:

    No, it can't. Because mutable breaks it.

    mutable is a mechanism for explicitly stating: this is some internal state, which can change during const operations and which I will synchronize manually. It's not a mechanism for "breaking the rules". If you use it that way YOU are the one breaking the rules and introducing a race condition.

    @Bulb said:

    My "it" is "writing a const-qualified function that modifies mutable state in non-thread-safe way. And I didn't say that causes undefined behaviour, because by itself it does not.

    So, you are saying the same thing as me, yet you are arguing with me on that? I am honestly a bit lost. Or do I not understand you correctly?

    @Bulb said:

    For functions taking arguments (including invocant) by const references, not said anywhere.

    We already discussed pointers/references.

    @Bulb said:

    No, they are not.

    Despite I explicitly listed paragraphs from the Standard? Please, tell me which paragraphs take your point, then?



  • How is this conversation still going on? Jesus.

    I keep revisiting hoping someone will drop some new info about Enlightment or EFL, and it's just this const bullshit for like a WEEK.



  • @Kian said:

    Standard language rules, §1.10/4 and /21:

    That defines data race, fine. Not there yet.

    @Kian said:

    Standard library rules: §17.6.5.9/1-3:

    This promises const member functions in standard library are safe. It does not force it on 3rd party librararies.

    It still does not say

    class IdiotCounter {
        mutable count;
        // ...
        // NOT thread-safe. Just because. Deal with it.
        public: void const IdiotCounter &operator++() const { ++count; }
    };
    

    is against the standard. It is stupid. It does not match what standard library does. It won't be usable with some standard library tools. But it still isn't against the standard.

    @NeighborhoodButcher said:

    If you use it that way YOU are the one breaking the rules and introducing a race condition.

    But the rules only come from:

    • best coding practices and
    • standard imposes them on its own components

    @NeighborhoodButcher said:

    We already discussed pointers/references.

    I am still discussing pointers/references (including this), and only them.

    About const variables we are in full agreement.

    @NeighborhoodButcher said:

    Please, tell me which paragraphs take your point, then?

    I can't point paragraphs that don't say something. I am saying the paragraphs that you mention don't imply anything about const functions, i.e. functions that take their parameter via const reference and modify it's internal state.



  • Jesus. Stop. Just stop.

    Or at least, the VERY least, move it into a new topic I can mute.


  • ♿ (Parody)

    @blakeyrat said:

    I keep revisiting hoping someone will drop some new info about Enlightment or EFL, and it's just this constant const bullshit for like a WEEK.

    ­


  • area_pol

    Yeah, seems like we diverged too far. To end it all - everyone of us will do what one thinks it the right thing. If someone thinks he knows better than the Standard and the convener of the ISO C++ Committee, then ok - he will end up with an egg on his face someday. EOT on const for me. Let's get back to the wondrous world of enlightenment.



  • I'd be happy if the thread is Jeffed, but I can't do it myself.
    @Bulb said:

    This promises const member functions in standard library are safe. It does not force it on 3rd party librararies.

    It still does not say

    class IdiotCounter {
    mutable count;
    // ...
    // NOT thread-safe. Just because. Deal with it.
    public: void const IdiotCounter &operator++() const { ++count; }
    };

    is against the standard. It is stupid. It does not match what standard library does. It won't be usable with some standard library tools. But it still isn't against the standard.

    Do you know how to prevent it being used by library code? Because if not, the moment someone uses your custom type as a parameter for a templated library function you've violated the standard.

    You could say "only those methods that the standard library could possibly call are REQUIRED to be thread safe", so maybe just the more common ones (copy, operators, begin/end, etc). But then, you could conceivably pass any method to algorithms, and then any method you could pass to an algorithm has to meet the requirements too. So now it's "almost any method has to, but conceivably not everyone!" At which point you've reduced your argument to pedantic dickweedery.



  • @Kian said:

    I'd be happy if the thread is Jeffed, but I can't do it myself.

    Ok God knows I hate Discourse, but see that little right-facing arrow near the top of every post? The one labeled "reply as linked topic"?

    WHAT THE FUCK IS STOPPING YOU FROM CLICKING THAT!? Oh, right: the same mental retardation that causes you to post 437283 times about fucking const in C to an audience of hundreds of people and thinking ANY of them give a FLYING FUCK goddamned I hate you all HATE YOU ALL



  • @Kian said:

    Do you know how to prevent it being used by library code? Because if not, the moment someone uses your custom type as a parameter for a templated library function you've violated the standard.

    I get that argument (which is what Sutter is saying). Still, mutable members without synchronization seem useful enough to me (especially if you through other means guarantee that they will "never" be used in a multi-threaded context). Synchronization overhead is not free...



  • @blakeyrat said:

    Ok God knows I hate Discourse, but see that little right-facing arrow near the top of every post? The one labeled "reply as linked topic"?

    Ooohhhh! I never looked at it. Grey on white doesn't really jump out, and it's kind of out of the way. Looks more like a + sign for me than a right facing arrow.


Log in to reply