int name(int &mySum, Futex& f) { while (true) { f.lock(); if (ans >= MAX_ANS){ f.unlock(); break; } ++ans; ++mySum; f.unlock(); } return 0; }
void worker(int threadId, int targ_max, int &target, vector<int> &incrementsCounter, Futex &futex) { while(true) { futex.lock(threadId); if (target < targ_max) { target++; incrementsCounter[threadId]++; futex.unlock(threadId); } else { futex.unlock(threadId); break; } } }
/** * @brief main will load test Futex miplementation * * it starts two threads: one producer and one consumer * Consumer will dequeue "work" from lock free queue and process it. If queue is * empty - consumer will call Futex::wait() to sleep on futex. * * Producer will batch create some "work" in lock free queue and Futex::notify(), * then wait for queue will become empty (trying to catch point when consumer * change from working mode to sleep mode), then enqueue some more work and * call Futex::notify() to wake up consumer. After that it checks that queue will * be processed by consumer (was it sleeping or going to sleep at notify()). * Producer and Consumer is running on different OS threads. * * The aim of this test is to check, that notify will always work (whether consumer waiting() * or not) * * @return 0 on success */ int main() { std::cout << "main started\n"; TestLockFreeQueue queue; Futex futex; std::cout << "main will create producers\n"; std::thread producer1{producer_function, std::ref(futex), std::ref(queue)}; std::cout << "main will create consumers\n"; std::thread consumer1{consumer_function, std::ref(futex), std::ref(queue)}; std::cout << "main will sleep\n"; sleep( 100 ); std::cout << "main will notify\n"; stop_requested = true; futex.notify_all(); std::cout << "main will join consumer\n"; consumer1.join(); std::cout << "main will join producers\n"; producer1.join(); std::cout << "main will finish\n"; return 0; }
void run(int thr_num=10, int targ_max=2*(1e7), bool print=true) { int target = 0; vector<int> incrementsCounter(thr_num, 0); vector<thread> threads; Futex futex; futex.initOwner(); for (int i = 0; i < thr_num; ++i) { threads.push_back(thread(worker, i, targ_max, ref(target), ref(incrementsCounter), ref(futex))); } joinAll(threads); int incrementsSum = 0; for (int i = 0; i < thr_num; ++i) { if (print) { cout << "thread " << i << ": " << incrementsCounter[i] << " increments" << endl; } incrementsSum += incrementsCounter[i]; } assert(target == incrementsSum && target == targ_max); if (print) { cout << "target: " << target << endl; cout << "incsum: " << incrementsSum << endl; cout << "wanted: " << targ_max << endl << "-------------------------------" << endl; } }
void consumer_function( Futex& futex, TestLockFreeQueue& queue ) { cpu_set_t cpuset; CPU_ZERO(&cpuset); CPU_SET( 1, &cpuset); if( pthread_setaffinity_np( pthread_self(), sizeof(cpu_set_t), &cpuset) < 0 ) { printf("error pthread_setaffinity_np() to 1's CPU, %s", strerror(errno)); exit( EXIT_FAILURE ); } std::cout << "consumer started\n"; uint32_t q_value; while( !stop_requested ) { if( (q_value = queue.dequeue()) == 0 ) { futex.wait(); continue; } } }
void run_basic_thread( Futex<Atom>& f) { EXPECT_TRUE(f.futexWait(0)); }
void producer_function( Futex& futex, TestLockFreeQueue& queue ) { cpu_set_t cpuset; CPU_ZERO(&cpuset); CPU_SET(0, &cpuset); if( pthread_setaffinity_np( pthread_self(), sizeof(cpu_set_t), &cpuset) < 0 ) { printf("error pthread_setaffinity_np() to 0's CPU, %s", strerror(errno)); exit( EXIT_FAILURE ); } std::cout << "producer started\n"; uint32_t q_value; while( !stop_requested ) { if( (q_value = queue.enqueue(batch_size)) == 0 ) { std::cout << "producer got 0 (queue is full)\n"; usleep(100000); continue; } // std::cout << q_value << " after batch enqueue\n"; futex.notify(); usleep(1); uint32_t i = 0; while( !stop_requested && queue.enqueue() != 1 ) { usleep(1); ++i; } futex.notify(); if( i == 1 ) // increase batch_size if exhausted too quick { batch_size *= 2; std::cout << "New batch_size " << batch_size << std::endl; } if( i > 20 ) // decrease batch_size if it's too big { batch_size /= 2; std::cout << "New batch_size " << batch_size << std::endl; } while( (q_value = queue.enqueue(0) ) != 0 ) { // some data remain after last notify // consumer will wake up and exhaust it // if not - it's stalled for( int i = 0; i < 1000; ++i) { usleep(1); if( (q_value = queue.enqueue(0)) == 0 ) break; } if( q_value == 0 ) break; if( stop_requested ) break; std::cout << "after 1000 mks delay current q_value " << q_value << std::endl; usleep(100000); } // std::cout << i << " enqueue iterations for " << batch_size << " batch_size\n"; } }
void run_basic_thread( Futex<Atom>& f) { EXPECT_EQ(FutexResult::AWOKEN, f.futexWait(0)); }