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

Поділитися
Вставка
  • Опубліковано 22 кві 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
    UA-cam Channel Managed by Digital Medium Ltd: events.digital-medium.co.uk
    ---
    Registration for CppCon: cppcon.org/registration/
    #cppcon #cppprogramming #cpp
  • Наука та технологія

КОМЕНТАРІ • 17

  • @bruderdasisteinschwerermangel
    @bruderdasisteinschwerermangel Місяць тому +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

  • @snbv5real
    @snbv5real Місяць тому +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 Місяць тому

      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.

  • @loicballeydier5546
    @loicballeydier5546 Місяць тому +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 Місяць тому

    Nice! Looks very handy!

  • @KenChen-xr9jz
    @KenChen-xr9jz Місяць тому

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

  • @Stat1cV01D
    @Stat1cV01D Місяць тому +1

    There's boost::synchronized_value as well

  • @danielmilyutin9914
    @danielmilyutin9914 Місяць тому +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 Місяць тому +1

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

  • @headlibrarian1996
    @headlibrarian1996 Місяць тому +1

    No template deduction for update_guard?

  • @AnthonyDentinger
    @AnthonyDentinger Місяць тому

    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 26 днів тому

      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 Місяць тому

    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 Місяць тому

      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 Місяць тому

      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 Місяць тому

    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 Місяць тому +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.