Lightning Talk: Thread Safety With synchronized_value in C++ - Jørgen Fogh - CppCon 2023

Sdílet
Vložit
  • čas přidán 22. 04. 2024
  • cppcon.org/
    ---
    Lightning Talk: Thread Safety With synchronized_value in C++ - Jørgen Fogh - CppCon 2023
    github.com/CppCon/CppCon2023
    Adding thread safety to existing code is hard. The proposed type synchronized_value makes it less hard.
    I will show you why.
    ---
    Jørgen Fogh
    Jørgen Fogh has been writing software his entire life. The last 15 years he has primarily done it in C++.
    ---
    Videos Filmed & Edited by Bash Films: www.BashFilms.com
    CZcams Channel Managed by Digital Medium Ltd: events.digital-medium.co.uk
    ---
    Registration for CppCon: cppcon.org/registration/
    #cppcon #cppprogramming #cpp
  • Věda a technologie

Komentáře • 17

  • @snbv5real
    @snbv5real Před měsícem +2

    Made a similar class, thought I was doing something wrong when I made it since I've never seen anyone else do it. Glad there's work on what looks like a much better implementation that might make it into the standard!

    • @JorgenFogh
      @JorgenFogh Před měsícem

      A lot of people seem to have discovered this independently, which is a very good sign.
      If your class is available in a repo somewhere, I would love to link to it in the "related work" section of my library's readme. I am trying to collect the best ideas so I can combine them in an improved library.

  • @bruderdasisteinschwerermangel
    @bruderdasisteinschwerermangel Před měsícem +2

    this is effectively the concept of Rust's std::sync::Mutex and I really like the pattern. I actually implemented it myself not too long ago and it makes things much clearer and safer

  • @loicballeydier5546
    @loicballeydier5546 Před 29 dny +1

    Why update_guard ? We moved from lock_guard to xxx_lock for mutexes and now we come back to that for synchronized_value ? Why not using the same classes than for mutexes ?

  • @garyp.7501
    @garyp.7501 Před měsícem

    Nice! Looks very handy!

  • @headlibrarian1996
    @headlibrarian1996 Před 29 dny +1

    No template deduction for update_guard?

  • @Stat1cV01D
    @Stat1cV01D Před měsícem +1

    There's boost::synchronized_value as well

  • @danielmilyutin9914
    @danielmilyutin9914 Před měsícem +1

    1:57 seems odd to me.
    There is lock on map keys but value reference is still unsynchronized. Few threads can access same value by this key.

    • @JorgenFogh
      @JorgenFogh Před měsícem +1

      You're absolutely right. That was a bug. Well spotted!

  • @KenChen-xr9jz
    @KenChen-xr9jz Před měsícem

    good idea, I am eager to hear this implementation on cpp26.

  • @AnthonyDentinger
    @AnthonyDentinger Před měsícem

    Note that, while definitely *useful* as shown in this presentation, this solution is more situational, not magical:
    1) As someone else commented out, this would only protect one data; if the class had multiple ones, you would have to ensure all the locks are taken before starting to modify anything so that another thread cannot see some invalid intermediate state. Which you can do of course using the std::update_guard as shown, but at that point you may also consider just having a std::mutex and manually managing it.
    2) It, at best, only protects individual functions; once the function ends all locks are released, so in some situations where some thread needs to get data and handle it before modifying the class, this would still have races.
    Thank you for this presentation however; it’s nice to get quick updates on what might be coming, and their use-cases!

    • @JorgenFogh
      @JorgenFogh Před 21 dnem

      Thanks for your comment. It's nice to know that people are paying attention.
      I definitely agree that the solution isn't magical- it's just a lot better than a raw mutex.
      1) The solution is to wrap all the data in a struct. I often do this directly where the fields used to be declared, which means there is only a 3 line diff to review (2 lines for the start/end of the struct and 1 line for the synchronized_value).
      In most situations it wouldn't even be correct to use separate instances (and thus separate muteces) to guard related data.
      2) synchronized_value is no different from a raw mutex with lock_guard or unique_lock in this regard.
      Just put the update_guard in the same scope you would normally put a lock_guard.
      There is basically no difference between synchronized_value and raw muteces, except you can't forget to acquire the lock.
      Like all type checking, it doesn't seem to do anything once the code is correct. It's when your code _isn't_ correct that you see the benefit.
      In my experience, the worse the code base is, the more helpful synchronized_value becomes.
      That makes it hard to give a good example, since the example would have to be long and difficult to understand.

  • @dimula73
    @dimula73 Před měsícem

    Personally, I expected the whole `legacy_class 3` to be wrapped into synchronised_value. Then it would make perfect sense. Wrapping internal structures looks weird to me, since it is unsafe and update_guard just downgrade the whole solution to a simple mutex.
    Are there any downsides of wrapping a complex class with it?

    • @dimula73
      @dimula73 Před měsícem

      And I wonder how non-cost operator[] would be wrapped, as well as function calls?
      UPD: I checked the code, it looks fun. But I'm having really hard time understanding the limits of this approach :)

    • @JorgenFogh
      @JorgenFogh Před měsícem

      Compared to std::mutex, there are no downsides. Just be aware that synchronized_value solves some of the issues with raw mutexes, but not all of them.
      As @danielmilyutin9914 pointed out above, there is actually a bug in one of my slides, because I let a reference escape the lock scope.
      This can happen with or without synchronized_value, but at least you never forget to lock the mutex in the first place.
      One of the places where synchronized_value really makes a difference is in legacy code with very long methods, which needs to be fixed quickly. I used to put a lock at the top of each method to make it easy to see that I hadn't forgotten any locks. That came with a performance penalty, since the scopes were longer than necessary. With synchronized_value I can just "forget" deliberately and see what breaks. Then the compiler will tell me what the narrowest (probably) safe scope is. I still need to pay attention, but takes a lot less time.

  • @AlfredoCorrea
    @AlfredoCorrea Před měsícem

    Isn’t it the same as CopperSpice’s CSLibguarded?
    Sounds like a hammer for people that have no idea what they are doing when writing threaded code, (including myself most of the time.)

    • @JorgenFogh
      @JorgenFogh Před měsícem +1

      It is very similar and arguably better than this version.
      I just implemented the current proposal for the standard, but I am working on combining the best ideas from different implementations.
      I have linked to a few different versions from the github repo.