File indexing completed on 2024-04-28 16:59:49

0001 /*
0002    Copyright (C) 2014 Andreas Hartmetz <ahartmetz@gmail.com>
0003 
0004    This library is free software; you can redistribute it and/or
0005    modify it under the terms of the GNU Library General Public
0006    License as published by the Free Software Foundation; either
0007    version 2 of the License, or (at your option) any later version.
0008 
0009    This library is distributed in the hope that it will be useful,
0010    but WITHOUT ANY WARRANTY; without even the implied warranty of
0011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012    Library General Public License for more details.
0013 
0014    You should have received a copy of the GNU Library General Public License
0015    along with this library; see the file COPYING.LGPL.  If not, write to
0016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017    Boston, MA 02110-1301, USA.
0018 
0019    Alternatively, this file is available under the Mozilla Public License
0020    Version 1.1.  You may obtain a copy of the License at
0021    http://www.mozilla.org/MPL/
0022 */
0023 
0024 #ifndef SPINLOCK_H
0025 #define SPINLOCK_H
0026 
0027 #include <atomic>
0028 #include <cassert>
0029 
0030 #ifdef HAVE_VALGRIND
0031 #include <valgrind/helgrind.h>
0032 #else
0033 #include "valgrind-noop.h"
0034 #endif
0035 
0036 class Spinlock
0037 {
0038 public:
0039     Spinlock()
0040     {
0041         VALGRIND_HG_MUTEX_INIT_POST(this, 0);
0042     }
0043 
0044     // The assertion does two things (in debug mode):
0045     // - Check that a locked Spinlock is not destroyed
0046     // - Check that a destroyed Spinlock is not locked, by forcing a deadlock in that case
0047     //   (if the memory has not yet been overwritten)
0048     ~Spinlock()
0049     {
0050         VALGRIND_HG_MUTEX_DESTROY_PRE(this);
0051         assert(!m_locked.test_and_set(std::memory_order_acquire));
0052     }
0053 
0054     void lock()
0055     {
0056         VALGRIND_HG_MUTEX_LOCK_PRE(this, 0);
0057         while (m_locked.test_and_set(std::memory_order_acquire)) {
0058             // spin until locked
0059         }
0060         VALGRIND_HG_MUTEX_LOCK_POST(this);
0061     }
0062 
0063     void unlock()
0064     {
0065         VALGRIND_HG_MUTEX_UNLOCK_PRE(this);
0066         m_locked.clear(std::memory_order_release);
0067         VALGRIND_HG_MUTEX_UNLOCK_POST(this);
0068     }
0069 private:
0070     std::atomic_flag m_locked = ATOMIC_FLAG_INIT;
0071 };
0072 
0073 class SpinLocker
0074 {
0075 public:
0076     SpinLocker(Spinlock *lock)
0077        : m_lock(lock)
0078     {
0079         m_lock->lock();
0080     }
0081 
0082     ~SpinLocker()
0083     {
0084         m_lock->unlock();
0085     }
0086 private:
0087     Spinlock *m_lock;
0088 };
0089 
0090 #endif // SPINLOCK_H