concurrentqueue icon indicating copy to clipboard operation
concurrentqueue copied to clipboard

explicite producer inner call set_many_empty lead to assert(false)

Open yuThomas opened this issue 2 years ago • 4 comments

	inline bool set_many_empty(MOODYCAMEL_MAYBE_UNUSED index_t i, size_t count)
	{
		MOODYCAMEL_CONSTEXPR_IF (context == explicit_context && BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD) {
			// Set flags
			std::atomic_thread_fence(std::memory_order_release);
			i = BLOCK_SIZE - 1 - static_cast<size_t>(i & static_cast<index_t>(BLOCK_SIZE - 1)) - count + 1;
			for (size_t j = 0; j != count; ++j) {
				assert(!emptyFlags[i + j].load(std::memory_order_relaxed));  @marked 1
				emptyFlags[i + j].store(true, std::memory_order_relaxed);
			}
			return false;
		}
		else {
			// Increment counter
			auto prevVal = elementsCompletelyDequeued.fetch_add(count, std::memory_order_release);
			assert(prevVal + count <= BLOCK_SIZE);
			return prevVal + count == BLOCK_SIZE;
		}
	}

code at @marked 1, if the block has been marked true, it will trigger assert(false) and coredump the program. i wonder:

  1. how can it be?
  2. can comment such code, actually, the block just marked rue once more

yuThomas avatar Jan 12 '23 05:01 yuThomas

Obviously the assertion should not fire. Can you share an example that reproduces this?

cameron314 avatar Jan 16 '23 02:01 cameron314

Obviously the assertion should not fire. Can you share an example that reproduces this?

sorry too late, example as below:

int main(int argc, char** argv) { moodycamel::ConcurrentQueuestd::string que; std::vectormoodycamel::ProducerToken vecP;

std::string data("string");
for( int i=0; i<3; i++ )
{
    vecP.emplace_back(moodycamel::ProducerToken(que));
    std::thread th([&]{
        std::vector<std::string> dataBuck(100);
        size_t num;
        for(;;)
        {
            num = que.try_dequeue_bulk_from_producer(vecP[i], &dataBuck[0], 100);
            if ( num > 0 )
            {
                for ( size_t j=0; j<num; j++)
                {
                    // do something else    
                }
            }
        }
    });
    th.detach();
}

for( int i=0; i<8; i++)
{
    std::thread th([=,&que]{
        for(;;)
        {
            std::hash<std::string> h;
            que.enqueue(vecP[h(data + std::to_string(i))/3], data);
        }
    });
    th.detach();
}

getchar();

return 0;

}

yuThomas avatar Jan 30 '23 01:01 yuThomas

In this example, multiple producer threads are sharing the same producer tokens concurrently, which breaks the contract of the API.

cameron314 avatar Jan 30 '23 01:01 cameron314

In this example, multiple producer threads are sharing the same producer tokens concurrently, which breaks the contract of the API.

get it, thanks

yuThomas avatar Jan 30 '23 02:01 yuThomas