Let's rely on undefined behaviour for memory safety



  • I'm sure some of you saw this on The Old New Thing as well, but I couldn't find anything on it here, so.

    👮 Raymond Chen
    😕 Peter Atashian

    👮 (in article): It is your responsibility as a programmer not to call Acquire­SRW­Lock­Shared or Acquire­SRW­Lock­Exclusive from a thread that has already acquired the lock. Failing to comply with this rule will result in undefined behavior.

    😕: Wait, am I seriously not allowed to call these recursively? Rust legitimately relies on being able to safely call any of the acquire methods at any time even if it is already acquired in the same thread. If it seriously results in memory unsafe undefined behavior then that is a huge deal.

    👮: The fact that they cannot be acquired recursively is what makes them slim! Attempting to acquire recursively results in undefined behavior.

    😕: Rust currently relies on them being memory safe. So if by undefined behavior you mean that memory unsafety can occur, this means Rust cannot rely on SRWLocks anymore and will have to change its implementation to something else, such as a custom implementation on top of NT Keyed Events. Are keyed events de facto stable at this point? Can Rust rely on their API continuing to exist in the future and not changing?

    👮: The behavior is undefined. It might deadlock. It might succeed. It might (and probably will) cause the lock to fail to fulfil its contract in the future. [...] Keyed events are not documented and therefore come with no stability guarantee.

    😕: [...] so Rust cannot use SRWLocks. And because keyed events have no stability guarantee this means that Rust cannot implement its own locks on top of them either. So basically Rust is screwed in this regard. Thanks Microsoft.

    👮: Let me get this straight. You intentionally invoked undefined behavior, and now you’re upset that the undefined behavior isn’t undefined in the way that you like, and somehow that’s Microsoft’s fault.

    😕: Nah, it is Rust’s fault for not reading the documentation fully enough and understanding all the consequences. However, [...] it is Microsoft’s fault that we cannot use either keyed events or SRW Locks for it.

    👮: Okay, so it’s Microsoft’s fault that it wrote a synchronization primitive that doesn’t exactly meets your needs. Gotcha.

    👨: Are you sure SRWs and keyed events are your only options here? Windows has more synchronization objects than that…

    👴: Exactly. Don’t blame Microsoft because someone apparently hasn’t heard of parts of the Windows API that have been around since the first version of Windows NT.

    I particularly like the way the first reaction to "OMG we're invoking undefined behaviour here" is "let's use an undocumented feature instead!" Yeah, there's no way that could go wrong.


  • Notification Spam Recipient

    Whoa, it's like deja vu but with higher definition!


  • BINNED

    @Scarlet_Manuka said in Let's rely on undefined behaviour for memory safety:

    let's use an undocumented feature instead!"

    Undocumented is different from poorly documented. MSDN is usually great (the best to be honest), but not in this case.

    An SRW lock is the size of a pointer. The advantage is that it is fast to update the lock state. The disadvantage is that very little state information can be stored, so SRW locks cannot be acquired recursively. In addition, a thread that owns an SRW lock in shared mode cannot upgrade its ownership of the lock to exclusive mode.

    Cannot? or must not? cannot when reading about Trying to lock could very easily be interpreted as will fail



  • @dse That's actually what the article was about in the first place: clarifying that cannot here should be interpreted as must not.

    The undocumented feature part refers to the keyed events.


  • 🚽 Regular

    @dse said in Let's rely on undefined behaviour for memory safety:

    Undocumented is different from poorly documented.

    Another quote from that link @bb36e posted in the Funny Stuff:

    Documentation is like sex: when it is good, it is very, very good; and when it is bad, it is better than nothing. ― Dick Brandon



  • @Scarlet_Manuka said in Let's rely on undefined behaviour for memory safety:

    Wait, am I seriously not allowed to call these recursively?

    On a side-note, Linux kernel internally uses semaphores as mutexes. It should be rather obvious that these are not recursive either (though in this case the behaviour is defined: the thread will deadlock). And Linus militantly refuses to include any kind of mutexes. Because synchronization rules under which it might not be clear whether you hold the lock or not are likely to be wrong anyway.

    And he's right. In fact, the Rust safety rules require that it's Mutex is not recursive. If it was, it would allow a child function to get a mutable reference to the protected object even if its caller already has one, but there may only be one outstanding mutable reference to any object at any time.

    Having undefined behaviour is a bit of a problem though. Rust needs such attempt to reliably fail.



  • @Bulb - I'm wondering - can this be solved by just storing a thread id (containing the current locking thread's id) next to the SRW and checking it before locking & setting it after locking & clearing it before unlocking?
    Given some very basic concurrency guarantees (thread id is atomic, writes in a thread are visible in the same thread), this should work, no?



  • @CreatedToDislikeThis said in Let's rely on undefined behaviour for memory safety:

    I'm wondering - can this be solved by just storing a thread id (containing the current locking thread's id) next to the SRW and checking it before locking & setting it after locking & clearing it before unlocking?
    Given some very basic concurrency guarantees (thread id is atomic, writes in a thread are visible in the same thread), this should work, no?

    The SRWs have a shared mode, so multiple threads can hold the lock (i.e., for read access). Makes storing the thread IDs a fair bit more difficult.



  • @cvi said in Let's rely on undefined behaviour for memory safety:

    @CreatedToDislikeThis said in Let's rely on undefined behaviour for memory safety:

    I'm wondering - can this be solved by just storing a thread id (containing the current locking thread's id) next to the SRW and checking it before locking & setting it after locking & clearing it before unlocking?
    Given some very basic concurrency guarantees (thread id is atomic, writes in a thread are visible in the same thread), this should work, no?

    The SRWs have a shared mode, so multiple threads can hold the lock (i.e., for reading). Makes storing the thread IDs a fair bit more difficult.

    Oh - doh, missed that they were SRWs!
    That's indeed trickier and in fact pthread's rwlocks don't guarantee too much about them either. (pthread only defines what happens when a thread acquires the shared lock while holding the shared lock - all other such cases are undefined, and the case of acquiring the exclusive lock while holding the shared lock can't be solved with a single thread id either).



  • That said I would wish that more lock implementations had debug versions of their lock that'd check for any wrong usage of the API and abort on it, even if this is more expensive.
    It's nice to say "it's undefined behaviour", but when you have no earthly idea whether and where you're triggering it, that's not very helpful.



  • @CreatedToDislikeThis said in Let's rely on undefined behaviour for memory safety:

    That said I would wish that more lock implementations had debug versions of their lock that'd check for any wrong usage of the API and abort on it, even if this is more expensive.
    It's nice to say "it's undefined behaviour", but when you have no earthly idea whether and where you're triggering it, that's not very helpful.

    Are you complaining that if you don't know what you are doing, things that you don't know what they are will happen? Are you surprised by this?



  • @CreatedToDislikeThis said in Let's rely on undefined behaviour for memory safety:

    It's nice to say "it's undefined behaviour", but when you have no earthly idea whether and where you're triggering it, that's not very helpful.

    To be honest I am somewhat surprised these things are UndefinedBehaviour™. Semaphore is as simple as it gets, can be used for implementing mutexes, both normal and R/W (initial value is maximum, lock_shared is down(1) and lock_exclusive is down(max)) and attempt to lock such thing recursively is well defined—it deadlocks. Can anybody see how allowing UB™ can make the lock any simpler?



  • @Steve_The_Cynic said in Let's rely on undefined behaviour for memory safety:

    @CreatedToDislikeThis said in Let's rely on undefined behaviour for memory safety:

    That said I would wish that more lock implementations had debug versions of their lock that'd check for any wrong usage of the API and abort on it, even if this is more expensive.
    It's nice to say "it's undefined behaviour", but when you have no earthly idea whether and where you're triggering it, that's not very helpful.

    Are you complaining that if you don't know what you are doing, things that you don't know what they are will happen? Are you surprised by this?

    No, I'm complaining that if I do know what I'm doing but am not omniscient, things that I don't know what will happen and I may well not know about them, depending on what they are.
    Are you claiming omniscience? Zero-bugs code?



  • @CreatedToDislikeThis Agreed, having a debug-version that you can conditionally compile in would be nice.



  • @CreatedToDislikeThis, @cvi, well, the SRW locks are lighter version of other synchronization primitives that there also are. So you can develop with the heavier-weight ones that do have debug mode and then #define over to the SRW ones for optimized build...



  • @Bulb The point about having it in by default in e.g., debug builds (much like the iterator checking that VS does/used to do), is that the misuse by rust would have been detected more or less immediately. But I agree with your point that the SRW locks probably qualify as a sufficiently low-level primitive that weighing them down even in debug mode may be a bad choice.

    Out of curiosity, I checked std::mutex::lock, which states

    If lock is called by a thread that already owns the mutex, the behavior is undefined: the program may deadlock, or, if the implementation can detect the deadlock, a resource_deadlock_would_occur error condition may be thrown.

    Does that mean that the implementation must either deadlocks or throws, or is the implementation allowed to do some random other things too?



  • @CreatedToDislikeThis said in Let's rely on undefined behaviour for memory safety:

    @Steve_The_Cynic said in Let's rely on undefined behaviour for memory safety:

    @CreatedToDislikeThis said in Let's rely on undefined behaviour for memory safety:

    That said I would wish that more lock implementations had debug versions of their lock that'd check for any wrong usage of the API and abort on it, even if this is more expensive.
    It's nice to say "it's undefined behaviour", but when you have no earthly idea whether and where you're triggering it, that's not very helpful.

    Are you complaining that if you don't know what you are doing, things that you don't know what they are will happen? Are you surprised by this?

    No, I'm complaining that if I do know what I'm doing but am not omniscient, things that I don't know what will happen and I may well not know about them, depending on what they are.
    Are you claiming omniscience? Zero-bugs code?

    Good grief, not at all. Sounds like you're saying you don't know what the code's doing, though. Or that it was badly designed, and it is having problems because of this.



  • @cvi said in Let's rely on undefined behaviour for memory safety:

    @Bulb The point about having it in by default in e.g., debug builds (much like the iterator checking that VS does/used to do), is that the misuse by rust would have been detected more or less immediately. But I agree with your point that the SRW locks probably qualify as a sufficiently low-level primitive that weighing them down even in debug mode may be a bad choice.

    Out of curiosity, I checked std::mutex::lock, which states

    If lock is called by a thread that already owns the mutex, the behavior is undefined: the program may deadlock, or, if the implementation can detect the deadlock, a resource_deadlock_would_occur error condition may be thrown.

    Does that mean that the implementation must either deadlocks or throws, or is the implementation allowed to do some random other things too?

    The behaviour is undefined. It is allowed to, for example, format the system drive, and its behaviour, while undesirable, would be within the specification. Look up "nasal demons" on Google, but the summary is that as soon as you stray into territory marked out by the sign "Here Be Undefined Behaviour" (aka UB), all bets are off. Even the mechanism of betting is off.


  • Impossible Mission - B

    @Steve_The_Cynic said in Let's rely on undefined behaviour for memory safety:

    Tthe summary is that as soon as you stray into territory marked out by the sign "Here Be Undefined Behaviour" (aka UB), all bets are off. Even the mechanism of betting is off.

    As Raymond Chen has put it in the past, (loosely paraphrasing here,) any result is valid as undefined behavior, including your code working as intended. That still doesn't mean it's right.



  • @cvi said in Let's rely on undefined behaviour for memory safety:

    Does that mean that the implementation must either deadlocks or throws, or is the implementation allowed to do some random other things too?

    That means whoever wrote the docs isn't familiar with common terminology.

    Unspecified behavior is when you have a limited set of options and while the implementation can choose either, or all of them, without documenting the choice, it won't do anything else.

    Undefined means nasal demons, and makes the latter part pointless since there's no guarantee the implementation won't do anything that's not on the list.


  • Winner of the 2016 Presidential Election

    @Maciejasjmj said in Let's rely on undefined behaviour for memory safety:

    without documenting the choice

    And if they have to document the choice, it's called implementation-defined behavior.



  • @Steve_The_Cynic I'm aware what "undefined behaviour" implies, but normally you don't list things that may happen when UB occurs.

    I should have phrased my post differently - asking instead if anybody knows what the spec says. If the spec says it's UB, then it's UB. If the specs says it's either deadlock or throw, then it's not UB (but implementation specific behaviour as mentioned elsewhere).

    Edit: While I CBA to find the spec, several other sources including MSDN just say that it's UB. So, I guess that's that.



  • @dse said in Let's rely on undefined behaviour for memory safety:

    @Scarlet_Manuka said in Let's rely on undefined behaviour for memory safety:

    let's use an undocumented feature instead!"

    Undocumented is different from poorly documented. MSDN is usually great (the best to be honest),

    That says less about MSDN and more about the state of documentation in general, IMAO.

    @Scarlet_Manuka said in Let's rely on undefined behaviour for memory safety:

    I'm sure some of you saw this on The Old New Thing as well, but I couldn't find anything on it here, so.

    👮 Raymond Chen
    😕 Peter Atashian

    😕: Nah, it is Rust’s fault for not reading the documentation fully enough and understanding all the consequences. However, [...] it is Microsoft’s fault that we cannot use either keyed events or SRW Locks for it.

    👮: Okay, so it’s Microsoft’s fault that it wrote a synchronization primitive that doesn’t exactly meets your needs. Gotcha.

    I'm pretty sure it's more a case of 'Microsoft should have been clearer on the fact that it is undefined behavior, because we would have done something else if we'd know that.' Though as @dse says, MSDN is usually better about that than it is here, and the Rust team share a part of the fault for not looking more closely when they chose their implementation.

    👨: Are you sure SRWs and keyed events are your only options here? Windows has more synchronization objects than that…

    👴: Exactly. Don’t blame Microsoft because someone apparently hasn’t heard of parts of the Windows API that have been around since the first version of Windows NT.

    Also, was the last emoji supposed to be 👮, or was that your own comment?

    @Scarlet_Manuka said in Let's rely on undefined behaviour for memory safety:

    I particularly like the way the first reaction to "OMG we're invoking undefined behaviour here" is "let's use an undocumented feature instead!" Yeah, there's no way that could go wrong.

    Yeah, that's pretty :wtf:-y right there.



  • The problem with reader/writer locks in Windows is that there are "slim" reader/writer locks with limited functionality, but no "fat" ones that support more stuff (at least, not advertised as "reader writer locks" the same way as the slim ones). And to compound the problem, Win32 provides no way to acquire atomically several units of the same semaphore.

    So you find yourself stuck having to implement your reader/writer lock with mutexes (which in Win32 can be acquired recursively) and events. And it's even worse if you want a multi-processus one, in which case you're better off using files.



  • @Medinoc said in Let's rely on undefined behaviour for memory safety:

    The problem with reader/writer locks in Windows is that there are "slim" reader/writer locks with limited functionality, but no "fat" ones that support more stuff (at least, not advertised as "reader writer locks" the same way as the slim ones). And to compound the problem, Win32 provides no way to acquire atomically several units of the same semaphore.

    So you find yourself stuck having to implement your reader/writer lock with mutexes (which in Win32 can be acquired recursively) and events. And it's even worse if you want a multi-processus one, in which case you're better off using files.

    Um. Win32 mutexes work across process boundaries. Just give them a name, and CreateMutex them with that name from all processes that want to participate.(1) Same for events, except you call CreateEvent, duh.

    (1) The first process to call CreateMutex creates it. The others will attach to it instead, and everyone involved can tell whether or not they are the process that created it. Once all handles on it are closed (and not before), it disappears.



  • @Scarlet_Manuka so what's stopping them from writing wrappers for the functions, setting a static flag in the wrapper and then short circuit if it's called recursively so the real function isn't called again in the same thread?


  • Discourse touched me in a no-no place

    @ScholRLEA said in Let's rely on undefined behaviour for memory safety:

    Also, was the last emoji supposed to be 👮, or was that your own comment?

    No, that was a different person in the original comment thread on Old New Thing.


  • BINNED

    @anotherusername said in Let's rely on undefined behaviour for memory safety:

    @Scarlet_Manuka so what's stopping them from writing wrappers for the functions, setting a static flag in the wrapper and then short circuit if it's called recursively so the real function isn't called again in the same thread?

    You should read what @Medinoc just said, his explanation is on point. named mutexes (as @Steve_The_Cynic mentioned) could come to rescue (together with some shared memory perhaps) but no simple static flag.


  • Winner of the 2016 Presidential Election Banned

    @Zecc said in Let's rely on undefined behaviour for memory safety:

    @dse said in Let's rely on undefined behaviour for memory safety:

    Undocumented is different from poorly documented.

    Another quote from that link @bb36e posted in the Funny Stuff:

    Documentation is like sex: when it is good, it is very, very good; and when it is bad, it is better than nothing. ― Dick Brandon

    TWDTF has featured a few comments which I would argue are, in fact, worse than uncommented code.



  • @cvi said in Let's rely on undefined behaviour for memory safety:

    Does that mean that the implementation must either deadlocks or throws, or is the implementation allowed to do some random other things too?

    I read this as "It's UB, but here are some things I've seen it do. Also a short list of things that would be reasonable in this case."

    A list of things that sometimes happen when UB is invoked that comes in a brown sack is worth precisely one brown sack, but apparently this guy wanted to be helpful.



  • @Fox said in Let's rely on undefined behaviour for memory safety:

    TWDTF has featured a few comments which I would argue are, in fact, worse than uncommented code.

    I agree; however, I'm sure we'd disagree on just which comments those are.



  • @Zecc said in Let's rely on undefined behaviour for memory safety:

    @dse said in Let's rely on undefined behaviour for memory safety:

    Undocumented is different from poorly documented.

    Another quote from that link @bb36e posted in the Funny Stuff:

    Documentation is like sex: when it is good, it is very, very good; and when it is bad, it is better than nothing. ― Dick Brandon

    Clearly these people just do not have the neccessary experience with bad sex or they wouldn't say that. (Was it Woody Allen who preferred a sandwich to bad sex?)



  • @ScholRLEA said in Let's rely on undefined behaviour for memory safety:

    Also, was the last emoji supposed to be , or was that your own comment?

    The last couple of lines were from other commenters, not the two main participants, so I used different emoji for them.



  • @cvi said in Let's rely on undefined behaviour for memory safety:

    Does that mean that the implementation must either deadlocks or throws, or is the implementation allowed to do some random other things too?

    The specification itself (N4296, C++14 final draft) is not clear on this point. The lock() operation specifies a requirement that the mutex is not locked and then there is a note with the two options, deadlocking or throwing, when it is violated. But a note is not normative and the operation definition does not say either way:

    30.4.1.2 Mutex types:
    6. The expression m.lock() shall be well-formed and have the following semantics:
    7. Requires: If m is of type std::mutex, std::timed_mutex, or std::shared_timed_mutex, the calling thread does not own the mutex.

    30.4.1.2.1 Class mutex:
    4. [Note: A program may deadlock if the thread that owns a mutex object calls lock() on that object. If the implementation can detect the deadlock, a resource_deadlock_would_occur error condition may be observed. — end note]

    @AyGeePlus said in Let's rely on undefined behaviour for memory safety:

    but apparently this guy wanted to be helpful.

    Well, unfortunately the specification also wanted to be helpful.


  • Discourse touched me in a no-no place

    @Bulb I'd be tempted to say that IF an implementation can detect a call to lock() on an object locked by the current thread, it SHOULD either deadlock or throw the defined exception. However there's no guarantee that detection is possible, or that if a failure happens that you have anything you can do except inspect the corpse of the process. Deadlocking and stopping the process (e.g., with abort()) with the thread in that deadlocked state is actually the best option, as that at least means that you can use a debugger to figure out who's been a naughty boy and needs the SPANK SPANK SPANK option.



  • @dkf If an implementation CAN (and does) detect attempts at recursive locks, it can say how it responds to it too. What I am wondering is whether it makes sense for implementation that DOESN'T to do anything else then deadlock. Because deadlock is what a simple implementation will do - the thread will simply start waiting for itself to call unlock(). And if that's what happens, the documentation/specification should be saying "will deadlock", or at least "will not return" rather than "behaviour is undefined".



  • @Bulb Agreed, I was thinking the same thing.

    E.g., if you go for atomics (plus something like a futex to avoid spinlocking for too long), the most efficient implementation that I can see would just be guaranteed to deadlock (because the lock is already taken).

    OTOH, if you want to enable implementations to layer on top of some other API (pthreads), you can't really force stronger guarantees than that API.

    The man page for pthread_mutex_lock() is also extra helpful:

    │Mutex Type │ Robustness │     Relock     
    │DEFAULT    │ non-robust │ undefined behavior†
    

    ...

    †     If  the  mutex  type  is  PTHREAD_MUTEX_DEFAULT,  the behavior of
          pthread_mutex_lock() may correspond to one  of  the  three  other
          standard  mutex types as described in the table above. If it does
          not correspond to one of those three, the behavior  is  undefined
          for the cases marked †.
    

    So .... I guess the behaviour is undefined then? :facepalm:



  • Idiot. The solution is very easy.

    The calls work right now, right? So keep them. Library contracts are not legal contracts (yet), they can't sue you for relying on undefined behaviour.

    What is Microsoft going to do, change their implementation? No, they won't, because that would break software compatibility and they don't do that. You just code away, and they'll add compatibility shims to your code as necessary.


  • FoxDev

    @anonymous234 said in Let's rely on undefined behaviour for memory safety:

    You just code away, and they'll add compatibility shims to your code as necessary.

    -shudder-

    that's... that's..... but..... no! just no!

    .... great, now i feel all icky bad..... BRB, i need to take a shower to wash this badness off with consecrated soap made from the fat of my former enemies



  • @cvi Yes, except:

    • pthread_mutex can be constructed with specific type and then the behaviour is defined and
    • the SRW lock is not layered on top of anything; it is a fast and simple primitive.

  • BINNED

    @accalia said in Let's rely on undefined behaviour for memory safety:

    i need to take a shower to wash this badness off with consecrated soap made from the fat of my former enemies

    @Rhywden, damn it man, your characters are escaping your books!

    🐠



  • @Onyx said in Let's rely on undefined behaviour for memory safety:

    @accalia said in Let's rely on undefined behaviour for memory safety:

    i need to take a shower to wash this badness off with consecrated soap made from the fat of my former enemies

    @Rhywden, damn it man, your characters are escaping your books!

    🐠

    As a teacher, you sometimes evolve a dark and twisted sense of humour. I'm still a bit miffed that my colleagues voted "nay" on my scheme on how to grade a successful alcohol distillation done by my pupils.



  • @HardwareGeek I've never seen an useful comment, and never write comments


  • Discourse touched me in a no-no place

    @groo said in Let's rely on undefined behaviour for memory safety:

    I've never seen an useful comment

    The following comment from some code I maintain is alleged to be useful. It's main purpose is to stop idiots (👋) from trying to change the algorithm and getting something that is no better in terms of resistance to attack yet is slower for all that. The code in question looks too simple for most programmers to think that it's any good at this sort of thing by comparison with most competing hashes, so warning folks to leave it alone is actually valuable. ;)

        /*
         * I tried a zillion different hash functions and asked many other people
         * for advice. Many people had their own favorite functions, all
         * different, but no-one had much idea why they were good ones. I chose
         * the one below (multiply by 9 and add new character) because of the
         * following reasons:
         *
         * 1. Multiplying by 10 is perfect for keys that are decimal strings, and
         *    multiplying by 9 is just about as good.
         * 2. Times-9 is (shift-left-3) plus (old). This means that each
         *    character's bits hang around in the low-order bits of the hash value
         *    for ever, plus they spread fairly rapidly up to the high-order bits
         *    to fill out the hash value. This seems works well both for decimal
         *    and non-decimal strings, but isn't strong against maliciously-chosen
         *    keys.
         *
         * Note that this function is very weak against malicious strings; it's
         * very easy to generate multiple keys that have the same hashcode. On the
         * other hand, that hardly ever actually occurs and this function *is*
         * very cheap, even by comparison with industry-standard hashes like FNV.
         * If real strength of hash is required though, use a custom hash based on
         * Bob Jenkins's lookup3(), but be aware that it's significantly slower.
         * Since […] names are usually reasonably-named, speed is far more important
         * than strength.
         */
    

    The full comment also has references to other places in the code which need to be kept in sync, issues that discuss in depth why things aren't changed, and (indirectly) to at least one academic paper.



  • @Bulb said in Let's rely on undefined behaviour for memory safety:

    The specification itself (N4296, C++14 final draft) is not clear on this point.

    I think it's pretty clear. The spec says the call requires that the calling thread does not own the mutex. If you make the call without meeting the requirement, you've broken the contract. Anything that happens afterwards is undefined. That's how contracts work: once you break your side, you can't hold the other part to anything.

    The note may be of use if you're seeing your program deadlock and wondering why, since for most implementations one of those things is the most likely thing to happen.



  • @Kian said in Let's rely on undefined behaviour for memory safety:

    I think it's pretty clear.

    But it is not saying whether the behaviour when you break the contract is undefined, unspecified or implementation defined (ok, actually, it might, somewhere in the section header for all the functions; I didn't check). And all three cases actually occur for other functions and constructs.


  • Java Dev

    @dkf Yes, some code just seems too simple to be true. We extensively use a similar hash based on the number 31, as we process a lot of text data.


  • Discourse touched me in a no-no place

    @PleegWat said in Let's rely on undefined behaviour for memory safety:

    We extensively use a similar hash based on the number 31, as we process a lot of text data.

    It turns out that 9 works pretty well for that too. I've done the benchmarks (years ago). :)



  • @Bulb The spec clarifies:

    17.6.4.11 Requires paragraph [res.on.required]
    1 Violation of the preconditions specified in a function’s Requires: paragraph results in undefined behavior
    unless the function’s Throws: paragraph specifies throwing an exception when the precondition is violated.

    Which should remove all ambiguity.

    In the particular case, the "Throws" paragraph says to refer to 30.2.2, which says:

    30.2.2 Exceptions [thread.req.exception]
    1 Some functions described in this Clause are specified to throw exceptions of type system_error (19.5.6).
    Such exceptions shall be thrown if any of the function’s error conditions is detected or a call to an operating
    system or other underlying API results in an error that prevents the library function from meeting its
    specifications. Failure to allocate storage shall be reported as described in 17.6.5.12.

    None of the errors defined for lock() excuse the precondition being violated, thus the behavior is undefined.


  • :belt_onion:

    @anonymous234 said in Let's rely on undefined behaviour for memory safety:

    Idiot. The solution is very easy.

    The calls work right now, right? So keep them. Library contracts are not legal contracts (yet), they can't sue you for relying on undefined behaviour.

    What is Microsoft going to do, change their implementation? No, they won't, because that would break software compatibility and they don't do that. You just code away, and they'll add compatibility shims to your code as necessary.

    This is why Raymond Chen can't have nice things.


Log in to reply