Using std::mutex in a single-thread board

I know that while working on a single-thread board two instructions can't happend at the same time, so I could simply use a flag, but as I'm working with interrupts and maybe someone loads this code into a board with multiple cores I want to keep it this way.

Is there an alternative of using #include <mutex>'s std::mutex and std::lock_guard<std::mutex> on a single-thread board (Arduino Nano 33 IoT)?

This is the error (and it happends because of the #ifdef _GLIBCXX_HAS_GTHREADS, found on "std_mutex.h"):

In file included from src\BuzzerPlayer.cpp:1:0:
include/BuzzerPlayer.h:17:10: error: 'mutex' in namespace 'std' does not name a type
     std::mutex _playing_mutex;
          ^~~~~
src\BuzzerPlayer.cpp: In member function 'void BuzzerPlayer::setState(uint16_t)':
src\BuzzerPlayer.cpp:21:26: error: 'mutex' is not a member of 'std'
     std::lock_guard<std::mutex> lck(this->_playing_mutex);
                          ^~~~~
src\BuzzerPlayer.cpp:21:26: error: 'mutex' is not a member of 'std'

You probably have to use conditional compilation to detect when compiling for a board for which this is applicable. Then only compile in the mutex stuff when needed / available.

Different architectures and frameworks can have different (incomplete or missing) implementations of the standard libraries.

Unfortunately a user of your code only can remove offending #include's and risk that more code becomes unusable, despite other parts of the library were available.

I suggest that you specify definitely tested machines, development systems, library versions etc. at the time of release in the "supported" section of your documentation.

A board is never inherently single-threaded. Users can use an RTOS or some kind of preemptive scheduler to add minimal threading support to even an Arduino UNO, necessitating synchronization (through mutexes or other means) between different threads, even if there is only one physical core.

You should never try to block on mutex acquisition in an interrupt handler, it will simply deadlock. Strictly speaking, an interrupt handler is only allowed to access shared variables of type volatile sig_atomic_t or lock-free atomics. std::signal - cppreference.com

Most code is not thread-safe. Any function should be considered thread-unsafe unless the documentation explicitly states otherwise. If a user wants to call your code from multiple threads, they should add the necessary synchronization.
In my opinion, it is not the responsibility of a buzzer library to impose synchronization, because no user should expect it to be thread-safe or interrupt-safe in the first place.

If an inexperienced user writes incorrect multithreading code anyway, data races in the buzzer code are going to be the least of their worries. Defensively adding mutexes to the buzzer library code won't save them, and is not worth the overhead and portability issues.

I guess you're differentiating that from the Spin Lock used in ESP32 portENTER_CRITICAL()?

With that, an ISR in one core will spin if it tries to acquire a portMUX_TYPE variable currently held by the other core.

Indeed. The difference between a critical section and a standard mutex is that a critical section will disable interrupts, whereas a mutex does not. When an interrupt fires, you know that it is impossible for the main program to be in a critical section, so it is safe to enter one in the ISR.
But it is possible that the main program holds a mutex when an interrupt fires, and if you try to acquire it in the ISR, it will block indefinitely, because there is no opportunity for the main code to make progress and release the mutex.