Hardware-based solutions are not accessible to application programmers and are highly machine-dependent (different hardware uses different instructions.) As such, most operating systems offer a set of API functions that internally implement such solutions.
The simplest of these tools is the mutex lock or mutexes, short for mutual exclusion. This solution consists of two functions, which we generically call acquire() (to acquire a lock prior to entering a critical section) and release() (to release the lock when exiting the critical section.)
Just as with hardware locks, the acquire step will block the process if the lock is in use by another process, and both the acquire and release operations are atomic.
Example: Linux provides the functions pthread_mutex_lock() and pthread_mutex_unlock() to implement a mutex lock when two or more threads want to access a critical section.