// This POC aims to demonstrate and measure possible issues around concurrent access to a vector through the push_back method // // Steps to have fun // 1. Enable or disable the reserve in main() // 2. See results of checkingVectorCoherence // 3. Test to enable the mutex around push_back #include #include #include #include #include #include #include #include #include using namespace std; const long TOTAL = 100000; vector values; // PcoMutex m1; void task(int tid) { for (long i = 0; i < TOTAL; i++) { // m1.lock(); values.push_back(i); // m1.unlock(); string msg = "tid: " + to_string(tid) + " i: " + to_string(i) + "\n"; cout << msg; } } const int NB_THREADS = 4; const int FLAG = 0x03; // flag to fill into the allocated memory of the vector to detect non initialized zones const int FLAG4 = 0x03030303;// 4 times the FLAG byte void checkingVectorCoherence() { cout << "Checking vector content" << endl; vector occurencesCountersByValue; occurencesCountersByValue.assign(TOTAL, 0); int flag_counter = 0; int missing_values_counter = 0; for (auto value: values) { if (value == FLAG4) { flag_counter++; continue; } occurencesCountersByValue.at(value)++; } for (int i = 0; i < TOTAL; i++) { auto count = occurencesCountersByValue.at(i); if (count != NB_THREADS) { cerr << "Value " << i << " is present " << count << " times instead of " << NB_THREADS << endl; missing_values_counter += NB_THREADS - count; } } cerr << "Found " << missing_values_counter << " missing values " << endl; cerr << "values.size() is " << values.size() << " -> " << (NB_THREADS * TOTAL - values.size()) << " lost increments towards " << NB_THREADS * TOTAL << endl; cerr << "values.capacity() -> " << values.capacity() << endl; cerr << "Found " << flag_counter << " times the initialisation flag " << FLAG4 << endl; } int main(void) { cout << endl << "values.size() -> " << values.size() << endl; // values.reserve(TOTAL * NB_THREADS); // Initialize the allocated memory of the internal buffer on the heap // with the FLAG byte to be able to detect integer values of FLAG4 afterwards // that were not initialized. See checkingVectorCoherence for use of FLAG4. assert(TOTAL < FLAG4);// make sure we don't have to push_back values that equals the FLAG4 values.push_back(1); int *first = &values[0]; memset(first, FLAG, TOTAL * NB_THREADS * sizeof(int)); values.pop_back();// Remove the first value to come back as before cout << endl << "values.size() -> " << values.size() << endl; PcoThread *threads[NB_THREADS]; for (int i = 0; i < NB_THREADS; i++) { threads[i] = new PcoThread(task, i); } for (int i = 0; i < NB_THREADS; i++) { threads[i]->join(); delete threads[i]; } checkingVectorCoherence(); return EXIT_SUCCESS; }