Henry Rich
Henry Rich

Reputation: 41

C++: Library function to produce a spin lock with PAUSE, and no wait?

I need a spin lock to protect a resource. The lock is acquired a few times a second, and when acquired it is held for about 100 instructions. In other words, it's almost always available. The exception is that every few days, the lock will be held for 10 milliseconds. I feel the need to put a PAUSE instruction or equivalent into the loop to make sure that the 10 milliseconds doesn't balloon if a hyperthread is waiting during that period. I want to use library functions if possible, but I want to make sure that the function never goes into a wait state, but instead stays in the loop waiting for the lock.

I have read the C++ Standard, and timed_mutex seems like it might do what I want, but I see no guarantee that try_lock_for() will (1) never go to sleep (2) insert PAUSE. It may be an implementation issue.

Do you know the implementations well enough to suggest a mutex or locking class that will do what I want?

Upvotes: 0

Views: 1016

Answers (1)

Zalman Stern
Zalman Stern

Reputation: 3191

My first piece of advice is to make sure you really need what you think you need before going down this path. The main reason to want a spinlock is to reduce the overhead of locking at all in situations where one knows there is very little contention and the guarantees of a full mutex, such as some degree of fairness, are not needed. Your description meets the lack of contention, but if the lock is only being acquired a few times a second, it doesn't seem like the overhead of a mutex is going to matter.

But if you're sure this is necessary...

Generally if you need something as specific as the PAUSE instruction, and strong guarantees that the thread doesn't directly call something that causes a heavier-weight sleep, then using C++ Standard Library functions is a non-starter. You simply will not get guarantees like that across platforms. However the requirements are already a bit ill-specified. E.g. the thread can likely be preempted inside the spin-loop anyway. If this is an issue then you're into the realm of realtime primitives. Realtime OSes often do give the level of control and portable primitives to do this sort of thing.

So that said, you have two paths you can take. First, you can implement your own spinlock. It is not particularly hard to do using std::atomic_flag. To get the PAUSE functionality, semi-portably, you'll need to use conditional compilation, per something like this: Cross-platform implementation of the x86 pause instruction . (You can likely use the x86 intrinsics support and _mm_pause() instead of an asm statement.)

The other way to go is to use another already implemented library. Boost's details support used to provide a portable yield function which does the PAUSE on x86 and something else on the other platforms, but I'm not sure if it is still available since the rationalized a lot of the thread support with C++11. Intel's TBB has spin_mutex which might meet the needs. (You'll probably have to look inside the implementation to decide.) There are likely some smaller lighter weight alternatives out there as well. Choosing a preexisting library usually depends on your application constraints.

If you're analyzing performance, etc. it is often productive to implement your own for one platform even if you don't end up using it in the final code as it allows tight control over the internals when doing performance experiments. A spin lock is small enough that it doesn't take a ton of time to write a simple one, even using PAUSE and such.

EDIT: Depending on the platform, pthread_spin_lock might also meet the requirements.

Upvotes: 1

Related Questions