| 804 | // A reasonable guess, probably close enough for most hardware |
| 805 | static constexpr size_t cache_line_size = 64; |
| 806 | struct alignas(cache_line_size) worker |
| 807 | { |
| 808 | std::thread thread; |
| 809 | std::condition_variable work_ready; |
| 810 | std::mutex mut; |
| 811 | std::atomic_flag busy_flag = ATOMIC_FLAG_INIT; |
| 812 | std::function<void()> work; |
| 813 | |
| 814 | void worker_main( |
| 815 | std::atomic<bool> &shutdown_flag, |
| 816 | std::atomic<size_t> &unscheduled_tasks, |
| 817 | concurrent_queue<std::function<void()>> &overflow_work) |
| 818 | { |
| 819 | using lock_t_inner = std::unique_lock<std::mutex>; |
| 820 | bool expect_work = true; |
| 821 | while (!shutdown_flag || expect_work) |
| 822 | { |
| 823 | std::function<void()> local_work; |
| 824 | if (expect_work || unscheduled_tasks == 0) |
| 825 | { |
| 826 | lock_t_inner lock(mut); |
| 827 | // Wait until there is work to be executed |
| 828 | work_ready.wait(lock, [&]{ return (work || shutdown_flag); }); |
| 829 | local_work.swap(work); |
| 830 | expect_work = false; |
| 831 | } |
| 832 | |
| 833 | bool marked_busy = false; |
| 834 | if (local_work) |
| 835 | { |
| 836 | marked_busy = true; |
| 837 | local_work(); |
| 838 | } |
| 839 | |
| 840 | if (!overflow_work.empty()) |
| 841 | { |
| 842 | if (!marked_busy && busy_flag.test_and_set()) |
| 843 | { |
| 844 | expect_work = true; |
| 845 | continue; |
| 846 | } |
| 847 | marked_busy = true; |
| 848 | |
| 849 | while (overflow_work.try_pop(local_work)) |
| 850 | { |
| 851 | --unscheduled_tasks; |
| 852 | local_work(); |
| 853 | } |
| 854 | } |
| 855 | |
| 856 | if (marked_busy) busy_flag.clear(); |
| 857 | } |
| 858 | } |
| 859 | }; |
| 860 | |
| 861 | concurrent_queue<std::function<void()>> overflow_work_; |
| 862 | std::mutex mut_; |