Using atomics - No mutex, conditional_variable or semaphore
\n///\n/// This is a lock free (but not wait free) approach.\n/// I mean no locks in software(userspace or kernel) but there are\n/// hardware locks involved.\n
```\n///\n/// This is a lock free (but not wait free) approach.\n/// I mean no locks in software(userspace or kernel) but there are\n/// hardware locks involved.\n///\n/// This guarantees no deadlocks i.e. atmost two can eat simultaneously\n/// and at least one is guaranteed to make  progress but starvation\n/// of a philosopher is a possibility since the solution heavily\n/// depends on ordering stemming from thread scheduling and \'which\'\n/// core wins the compare_and_exchange etc.\n///\n/// So this is not a good solution to the dining philosophers problem as such.\n/// But if you turn the problem around to maximizing the amount of food eaten\n/// with the same constraints, this might be an ideal solution.\n/// i.e. We are optimizing for the final throughput (\'total food consumed\')\n/// and less about starvation of an individual philosopher.\n///\n/// i.e. if count(active philosophers eating) +\n/// count(philosophers waiting to eat) <= number of cpu cores\n/// available to the process, below solution will likely achieve high\n/// throughput i.e. maximizing the final total number of\n/// eat-s/second.\n/// [In this question considering there are 60 possible active\n/// eat-s/eat requests, it\'s likely not to have 60 cores available.]\n///\n/// I think solutions that use conditional variable may prevent\n/// starvation since conditional variable can ensure the order\n/// of thread wake up is same as the order in which they were blocked.\n/// We may also completely turn this over to a thread safe scheduler\n// (a central authority) to ensure fairness etc.\n/// But using a bunch of locks alone may not prevent starvation.\n///\n/// It\'s also not clear how the testing for starvation is done here.\n/// \nconstexpr static auto cacheline_size = 64UL;\ntemplate<std::size_t PhilosopherCount> requires (PhilosopherCount < 64)\nclass alignas(cacheline_size) Forks\n{\n public:\n    void acquire(std::size_t philosopher) noexcept\n    {\n        assert(philosopher < PhilosopherCount);\n        auto [right_fork, left_fork] = get_forks(philosopher);\n        auto fork_mask = get_fork_mask(right_fork, left_fork);\n        auto indicator = _in_use_indicator.load(std::memory_order_acquire);\n        do\n        {\n            while(indicator & fork_mask) // wait for opportunity for both relevant forks to be available\n            {\n                indicator  = _in_use_indicator.load(std::memory_order_acquire);\n\t\t\t\t// asm("pause");\n            }\n\t\t// Now try to set both the bits representing the two forks in a single transaction without modifying other bits.\n        }while(!_in_use_indicator.compare_exchange_weak(indicator,  // Fails if someone else changed the indicator right before this call.\n                                                        indicator | fork_mask, //    This is where starvation is a possibility but the failure here also\n                                                        std::memory_order_release,  //  implies that someone else has made progress.\n                                                        std::memory_order_acquire));  // Not exposed to ABA problem\n    }\n    \n    void release(std::size_t philosopher) noexcept\n    {\n        auto [right_fork, left_fork] = get_forks(philosopher);\n        auto fork_mask               = get_fork_mask(right_fork, left_fork);\n        \n        assert(in_use(right_fork) && in_use(left_fork));\n        _in_use_indicator.fetch_and(~fork_mask, std::memory_order_release);\n    }\n    \n private:\n    \n    static std::pair<std::size_t, std::size_t> get_forks(std::size_t philosopher) noexcept\n    {\n        auto right_fork = philosopher == 0 ? PhilosopherCount - 1 : philosopher - 1;\n        auto left_fork = philosopher;\n        return {right_fork, left_fork};\n    }\n    \n    static std::size_t get_fork_mask(std::size_t right_fork, std::size_t left_fork) noexcept\n    {\n        return (1 << right_fork) | (1 << left_fork);\n    }\n    \n    bool in_use(std::size_t fork) const noexcept\n    {\n        return _in_use_indicator.load(std::memory_order_acquire) & (1<<fork);\n    }\n    \n    // TODO: Should add padding to own this whole cacheline.\n    std::atomic<std::size_t> _in_use_indicator{0}; // 00000 -> all forks are available, 10001 -> Philosopher 0 eats, 00011 -> 1 eats, 10111 -> 0 & 2 eat and so on.\n};\n\nclass DiningPhilosophers\n{\n public:\n    DiningPhilosophers()\n    {    \n    }\n\n    void wantsToEat(int philosopher,\n                    function<void()> pickLeftFork,\n                    function<void()> pickRightFork,\n                    function<void()> eat,\n                    function<void()> putLeftFork,\n                    function<void()> putRightFork)\n    {\n\t    Guard guard(_forks, philosopher);\n        pickLeftFork();\n        pickRightFork();\n        eat();\n        putLeftFork();\n        putRightFork();\n    }\n    \n private:\n    using ForksResource = Forks<5>;\n    \n    class Guard\n    {\n     public:\n        \n        Guard(ForksResource& forks, std::size_t philosopher) noexcept\n              : _forks(forks),\n                _philosopher(philosopher)\n        {\n            _forks.acquire(_philosopher);\n        }\n        \n        ~Guard() noexcept\n        {\n            _forks.release(_philosopher);\n        }\n        \n     private:\n        \n        ForksResource&  _forks;\n        std::size_t     _philosopher;\n    \n    };\n    \n    ForksResource _forks;\n};\n```
['C++']