C++ Logo


Advanced search

Re: [std-proposals] Fwd: set_new_handler extension

From: Sebastian Wittmeier <wittmeier_at_[hidden]>
Date: Thu, 1 Jun 2023 16:41:04 +0200
What code would we write (with the lock behind the scenes) that the compiler does   container.push_back(1); // the container should be locked here container.push_back(2); // the container should not be locked here container.push_back(3);   with no other thread inserting or deleting in between 1 or 2, but possibly before 3? Or does it analyze and know that automatically?   -----Ursprüngliche Nachricht----- Von:Phil Bouchard via Std-Proposals <std-proposals_at_[hidden]> Gesendet:Do 01.06.2023 16:27 Betreff:Re: [std-proposals] Fwd: set_new_handler extension An:Thiago Macieira <thiago_at_[hidden]>; CC:Phil Bouchard <boost_at_[hidden]>; std-proposals_at_[hidden]; On 6/1/23 01:34, Thiago Macieira wrote: > On Wednesday, 31 May 2023 21:42:45 PDT Phil Bouchard wrote: >>>      if (!container.empty()) >>>           container.push)back(1); >>> >>> is not safe. >> >> Why? The mutex is recursive so it just gives priority to the current >> thread for the scope of the condition. > > Because the container may have become non-empty between the check and the > push_back(1), in which case the code violated the requirement to append 1 only > if it was empty. > >>> And this of course becomes more complex when you have two or more >>> containers. Here's the first part of an algorithm: given containers C1 >>> and C2, if the first element in C1 isn't in C2, then remove it from there >>> (pop_front) and append (push_bacl) to C2. >>> >>> If you realise what the second part will be, feel free to comment on it >>> and >>> explain how your API will ensure the user writes the proper code for that. >> >> // imagine both containers are locked here for the scope of the condition: >> if (find(C2.begin(), C2.end(), * C1.begin()) != C2.end()) >> { >>       T value = C1.front(); >>       C1.pop_front(); >>       C2.push_back(value); >> } > > Locked how? That's the interesting part. > > Are you suggesting that the compiler analyse the if-find line, find all lockable > elements in use there, then lock them behind the scenes without user action > and keep them locked until the end of the scope? Yes that's what I am trying to say since the beginning. Here is just an example of the net code the compiler would see after making temporary variables last for the duration of the conditional scope: https://github.com/philippeb8/std__ts/blob/master/ts.cpp#L23 >> Again, where's the problem if both containers feature a recursive mutex? > > Recursion is not the problem. Atomicity of the action is. The point I tried to > make with the previous example is that one must reason about the duration of > the lock to decide when it must start and when it must end, so the conditions > don't change between statements. This is also the analogy of the transaction > that someone else posted in this thread. > > With the simple examples we're discussing here, it might be obvious what to do > and thus make solutions obvious. What others and I are telling you is that > when it gets to really complex thread-safe code, you *have* to reason about > when locks must start and when they must end, and what other locks you have. > Plus, reason about the order of locks, to avoid deadlocks. > > Thread-safety requires having the smallest possible critical sections, but no > smaller. If you pulverise your locks, you add overhead and actually lose > safety. > >> Regarding teaching, this is higher-level programming so a new namespace >> should encompass these new classes. > > That's not what I meant. You're oversimplifying the answer to a complex > question. Refer back to the top of this email: > >       if (!container.empty()) >            container.push)back(1); > > This may have no data race and thus cause no data corruption, but it's not > what was required because the states may have changed. If this is still > allowed, then just using the container in question does *not* confer thread- > safety. And therefore, if it is allowed to compile, how do you propose we > teach everyone *how* to write code to actually make it thread-safe? You add some type trait allowing the compiler to determine whether the class is a "thread-safe" class or not. > And how is that different from what we're already doing now? BTW forget my previous Github example, it is not generic enough. A thread-safe smart pointer (root_ptr or atomic<shared_ptr>) or the following wrapper with some type trait would be the way to go: https://fekir.info/post/sharing-data-between-threads/#_bind-the-data-and-mutex-together So for each temporary variable being a "thread_safe_type" object, the compiler would generate temporary variables lasting for the scope of the condition. But again we would need also new "thread-recursive" on top of "scope-recursive" mutices. Thanks, -- Logo <https://www.fornux.com/> *Phil Bouchard* facebook icon <https://www.linkedin.com/in/phil-bouchard-5723a910/> Founder & CEO T: (819) 328-4743 E: phil_at_[hidden]| www.fornux.com <http://www.fornux.com> 8 rue de la Baie| Gatineau (Qc), J8T 3H3 Canada Banner <https://goglobalawards.org/> Le message ci-dessus, ainsi que les documents l'accompagnant, sont destinés uniquement aux personnes identifiées et peuvent contenir des informations privilégiées, confidentielles ou ne pouvant être divulguées. Si vous avez reçu ce message par erreur, veuillez le détruire. This communication (and/or the attachments) is intended for named recipients only and may contain privileged or confidential information which is not to be disclosed. If you received this communication by mistake please destroy all copies. -- Std-Proposals mailing list Std-Proposals_at_[hidden] https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals

Received on 2023-06-01 14:41:06