Add simple spinlock implementation that is safe to use from anywhere.
Signed-off-by: Colin McCabe <colin.mccabe@dreamhost.com>
unittest_signals_CXXFLAGS = ${AM_CXXFLAGS} ${UNITTEST_CXXFLAGS}
check_PROGRAMS += unittest_signals
+unittest_simple_spin_SOURCES = test/simple_spin.cc
+unittest_simple_spin_LDADD = libceph.la ${UNITTEST_LDADD}
+unittest_simple_spin_CXXFLAGS = ${AM_CXXFLAGS} ${UNITTEST_CXXFLAGS}
+check_PROGRAMS += unittest_simple_spin
+
unittest_librados_SOURCES = test/librados.cc
unittest_librados_LDFLAGS = -pthread ${AM_LDFLAGS}
unittest_librados_LDADD = librados.la \
common/buffer.cc \
common/code_environment.cc \
common/signal.cc \
+ common/simple_spin.cc \
common/Thread.cc \
include/ceph_fs.cc \
include/ceph_hash.cc \
common/common_init.h\
common/code_environment.h \
common/signal.h\
+ common/simple_spin.h\
common/dyn_snprintf.h\
common/run_cmd.h\
common/safe_io.h\
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2011 New Dream Network
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "common/simple_spin.h"
+
+#include <stdio.h>
+#include <stdint.h>
+#include <pthread.h>
+
+uint32_t bar = 13;
+uint32_t *foo = &bar;
+
+void simple_spin_lock(simple_spinlock_t *lock)
+{
+ while(1) {
+ __sync_synchronize();
+ uint32_t oldval = *lock;
+ if (oldval == 0) {
+ if (__sync_bool_compare_and_swap(lock, 0, 1))
+ return;
+ }
+ // delay
+ for (int i = 0; i < 100000; i++) {
+ *foo = (*foo * 33) + 17;
+ }
+ }
+}
+
+void simple_spin_unlock(simple_spinlock_t *lock)
+{
+ __sync_bool_compare_and_swap(lock, 1, 0);
+}
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2011 New Dream Network
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#ifndef CEPH_SIMPLE_SPIN_H
+#define CEPH_SIMPLE_SPIN_H
+
+/* This is a simple spinlock implementation based on atomic compare-and-swap.
+ * Like all spinlocks, it is intended to protect short, simple critical
+ * sections. It is signal-safe. Unlike pthread_spin_lock and friends, it has a
+ * static initializer so you can write:
+ *
+ * simple_spinlock_t my_spinlock = SIMPLE_SPINLOCK_INITIALIZER
+ *
+ * This allows you to use the lock anywhere you want-- even in global
+ * constructors. Since simple_spinlock_t is a primitive type, it will start out
+ * correctly initialized.
+ */
+
+#include <stdint.h>
+
+typedef uint32_t simple_spinlock_t;
+
+#define SIMPLE_SPINLOCK_INITIALIZER 0
+
+void simple_spin_lock(simple_spinlock_t *lock);
+void simple_spin_unlock(simple_spinlock_t *lock);
+
+#endif
--- /dev/null
+#include "gtest/gtest.h"
+
+#include "common/simple_spin.h"
+
+TEST(SimpleSpin, Test0)
+{
+ simple_spinlock_t lock0 = SIMPLE_SPINLOCK_INITIALIZER;
+ simple_spin_lock(&lock0);
+ simple_spin_unlock(&lock0);
+}
+
+static simple_spinlock_t lock = SIMPLE_SPINLOCK_INITIALIZER;
+static uint32_t counter = 0;
+
+static void* mythread(void *v)
+{
+ for (int j = 0; j < 1000000; ++j) {
+ simple_spin_lock(&lock);
+ counter++;
+ simple_spin_unlock(&lock);
+ }
+}
+
+TEST(SimpleSpin, Test1)
+{
+ int ret;
+ pthread_t thread1;
+ pthread_t thread2;
+ ret = pthread_create(&thread1, NULL, mythread, NULL);
+ ASSERT_EQ(ret, 0);
+ ret = pthread_create(&thread2, NULL, mythread, NULL);
+ ASSERT_EQ(ret, 0);
+ ret = pthread_join(thread1, NULL);
+ ASSERT_EQ(ret, 0);
+ ret = pthread_join(thread2, NULL);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(counter, 2000000);
+}