Skip to content
Snippets Groups Projects
UnblockOnMainActorTest.cpp 2.99 KiB
Newer Older
  • Learn to ignore specific revisions
  • // SPDX-License-Identifier: LGPL-3.0-or-later
    
    // Copyright © 2020 Florian Fischer, 2021 Florian Schmaus
    #include <atomic>		 // for atomic
    #include <cstdlib>	 // for exit, EXIT_FAILURE, EXIT_SUC...
    #include <ctime>		 // for nanosleep, timespec
    #include <iostream>	 // for operator<<, basic_ostream
    #include <thread>		 // for this_thread
    
    
    #include "Actor.hpp"										 // for Actor
    #include "BinaryPrivateSemaphore.hpp"		 // for BPS
    #include "CountingPrivateSemaphore.hpp"	 // for CPS
    
    #include "Debug.hpp"
    #include "Fiber.hpp"
    #include "Runtime.hpp"
    #include "emper-config.h"
    #include "emper.hpp"
    
    #include "lib/adt/LockedUnboundedQueue.hpp"
    
    /*
     * This test triggered a spurious wakeup bug in the UnboundedBlockingMpscQueue
     * of the AlarmActor which was not triggered by the AlarmActorTest.
     *
     * Although we are not completely sure why the signaling main thread is needed
     * we assume that the shorter AlarmActor::receive function or different scheduling
     * caused by the main thread trigger the spurious wakeup in UnboundedBlockingMpscQueue::get
     */
    
    lib::adt::LockedUnboundedQueue<BPS> wakeupQueue;
    std::atomic<bool> quit = false;
    
    class AlarmActor : public Actor<BPS*> {
     protected:
    	void receive(BPS* sem) override { wakeupQueue.enqueue(sem); }
    
     public:
    	AlarmActor(Runtime& runtime) : Actor(runtime) {}
    	void stop() { Actor::stop(); }
    };
    
    auto main(int argc, char* argv[]) -> int {
    	unsigned int sleeper_count = std::thread::hardware_concurrency();
    
    	unsigned int sleeps = EMPER_LOG_LEVEL > Info ? 100 : 1000;
    
    
    	if (argc > 3) {
    		std::cerr << "Usage: " << argv[0] << " [fiber count] [block count]" << std::endl;
    		exit(EXIT_FAILURE);
    	}
    
    	const int DECIMAL = 10;
    	if (argc > 1) {
    		sleeper_count = strtol(argv[1], nullptr, DECIMAL);
    	}
    
    	if (argc > 2) {
    		sleeps = strtol(argv[2], nullptr, DECIMAL);
    	}
    
    	Runtime runtime;
    
    	Fiber* fiber = Fiber::from([&] {
    		AlarmActor alarmActor(runtime);
    		alarmActor.start();
    
    		CPS cps;
    		for (unsigned int i = 0; i < sleeper_count; ++i) {
    			spawn(
    					[&alarmActor, &sleeps] {
    						for (unsigned int i = 1; i <= sleeps; ++i) {
    							BPS sem;
    							alarmActor.tell(&sem);
    							sem.wait();
    						}
    					},
    					cps);
    		}
    
    		// Wait for the sleeping fibers to finish
    		cps.wait();
    
    		// Wait for the actor to become idle.
    
    		bool actorIdle = alarmActor.waitUntilIdle(60);
    
    		if (!actorIdle) {
    			std::cerr << "FAILURE: Actor did not went idle";
    			quit = true;
    			exit(EXIT_FAILURE);
    		}
    
    		alarmActor.stop();
    
    		quit = true;
    		exit(EXIT_SUCCESS);
    	});
    
    	runtime.scheduleFromAnywhere(*fiber);
    
    	while (!quit) {
    		BPS* sem = wakeupQueue.dequeue();
    		if (sem) {
    			sem->signalFromAnywhere();
    		} else {
    			// clang-tidy-11 probably crashes when parsing C++ comparison in chrono.h
    			// See: https://bugs.llvm.org/show_bug.cgi?id=47768.
    			// std::this_thread::sleep_for(std::chrono::nanoseconds(1000));
    			// -> use nanosleep directly
    			struct timespec duration;
    			duration.tv_sec = 0;
    			duration.tv_nsec = 100;
    			nanosleep(&duration, nullptr);
    		}
    	}
    
    	runtime.waitUntilFinished();
    
    	return EXIT_FAILURE;
    }