Comment by gpderetta

3 months ago

CPS can trivially deadlock for all meaningful definitions of deadlock.

Would you consider this a mutex?

   async_mutex mux;

   co_await mux.lock();
   /* critical section */
   co_await mux.unlock();
   

What about: my_mutex mux;

   {
      std::lock_guard _{mux};
      /* critical section */
   }

where the code runs in a user space fiber.

Would you consider boost synchronized a mutex?

Don't confuse the semantics with the implementation details (yes async/await leaks implementation details).

You only achieved a deadlock by re-introducing mutexes.

  • Given:

        Something someting;
        async_mutex mtx;
        void my_critical_section(Data&);
    

    1:

        await mtx.lock();
        my_critical_section(something);
        await mtx.unlock();
    

    2:

        auto my_locked_critical_section() {
          await mtx.lock();
          my_critical_section(something);
          await mtx.unlock();
        }
        ...    
        await my_locked_critical_section(something);
    

    3:

        auto locked(auto mtx, auto critical_section) {
          await mtx.lock();
          critical_section();
          await mtx.unlock();
        }
    
        ...    
        await locked(mtx, [&]{ my_critical_section(something); });
    

    4:

        template<class T>
        struct synchronized {
           async_mutex mtx;
           T data;
           auto async_visit(auto fn) { locked(mtx, [fn,&data]{ fn(data); }); }
        };
    
        synchronized<Something> something;
        await something.async_visit([](Something& data) { my_critical_section(something); });
    

    If 1 is a mutex, at which point it stops being a mutex? Note that 4 is my initial example.

    • it's a mutex iff it's acquiring a resource exclusively.

      which you don't need to do for synchronization of coroutines since you can control in which order things are scheduled and whether that's done concurrently or not.

      2 replies →