c++multithreadinglockingspinlocktop-command

spin locks not consuming 100% cpu


#include "boost/smart_ptr/detail/spinlock.hpp"
boost::detail::spinlock lock;
main(){
    std::lock_guard<boost::detail::spinlock> guard(lock);
    while(true)
        {
                i=i+100;
        }
}

Machine details:

CPU(s): 2

On-line CPU(s) list: 0,1

Thread(s) per core: 1

Core(s) per socket: 2

Socket(s): 1

Of the above code, When i ran :

First instance => it took 100% of the cpu( as per top command)

Second instance => it took 97-98% and the sum of these two instances show approx 195%-197%

Third instance => it took ~47-50% and the sum of these three showed close to 200% , by adjusting cpu consumption of first two instances.

My assumption was that, once the spin lock acquires the cpu, it doesn't get prempted by cpu(it doesn't get switched by cpu by scheduling some other thread for this time keeping the thread(spin locked) in scheduling queue), and hence i was expecting third instance to fail. But it ran showing that the first two processes thread's were prempted.

Where am i getting it wrong?


Solution

  • I think you misunderstand what a spin lock is. It's not much more complicated than this:

    class SpinLock {
    public:
        void lock() {
            while (is_locked) { /*do nothing*/ }
            // ...MAGIC HAPPENS HERE...
            is_locked = true;
        }
    
        void unlock() {
            is_locked = false;
            // ...SUBTLE magic happens here...
        }
    
    private:
        bool is_locked = false;
    };
    

    The MAGIC is code that uses special machine instructions* to ensure that, if more than one thread is "spinning" in the while loop at the same time, only one of them will get to see is_locked == false and exit the loop when some other thread calls the unlock() function.

    My assumption was that, once the spin lock acquires the cpu...

    There's nothing in a spin lock that can "acquire" a CPU. It's just code that gets run by a CPU, no different from any other code in your program. The operating system (OS) decides which thread to run on which CPU and when, and nothing that a spin lock does can influece that.

    ...it doesn't get preempted by CPU.

    A CPU doesn't preempt anything. A CPU just executes code. When the CPU happens to be running OS code, the OS can choose to preempt the current thread. Spin locks do not have any effect on which thread gets preempted, or when, or why.

    "Preempt," means that the OS pauses some running thread and allows some other thread to have a turn to run. It can happen on the order of 100 times every second, and usually, none of the threads involved have any awareness of it.

    The reason why spin locks have no influence over preemption is, they're just code. A pure spin lock does not call in to the OS or communicate with the OS in any way. The OS has no way to tell the difference between a thread that is calculating digits of pi, or a thread that is balancing bank accounts, or a thread that is waiting for a spin lock.


    The SUBTLE magic in the unlock() function consists of memory barrier instructions which are used to enforce the C++ memory model. That's a deep topic— too deep for this answer.


    * The C++ Atomic operations library gives you low-level access to those "special" machine instructions.