[
  {
    "path": ".clang-format",
    "content": "BasedOnStyle: LLVM"
  },
  {
    "path": ".gitignore",
    "content": "build\n*~\n.idea/"
  },
  {
    "path": ".travis.yml",
    "content": "language: cpp\n\nsudo: false\n\nmatrix:\n  include:\n\n    # clang-4\n    - env: COMPILER=clang++-4.0\n      compiler: clang\n      os: linux\n      addons:\n        apt:\n          packages:\n            - clang-4.0\n            - libstdc++-6-dev\n          sources:\n            - ubuntu-toolchain-r-test\n            - llvm-toolchain-trusty-4.0\n\n    # clang-5\n    - env: COMPILER=clang++-5.0\n      compiler: clang\n      os: linux\n      addons:\n        apt:\n          packages:\n            - clang-5.0\n            - libstdc++-7-dev\n          sources:\n            - ubuntu-toolchain-r-test\n            - llvm-toolchain-trusty-5.0\n\n    # clang-6\n    - env: COMPILER=clang++-6.0\n      compiler: clang\n      os: linux\n      addons:\n        apt:\n          packages:\n            - clang-6.0\n            - libstdc++-7-dev\n          sources:\n            - ubuntu-toolchain-r-test\n            - llvm-toolchain-trusty-6.0\n\n    # gcc-5\n    - env: COMPILER=g++-5\n      compiler: gcc\n      os: linux\n      addons:\n        apt:\n          packages:\n            - g++-5\n          sources:\n            - ubuntu-toolchain-r-test\n\n    # gcc-6\n    - env: COMPILER=g++-6\n      compiler: gcc\n      os: linux\n      addons:\n        apt:\n          packages:\n            - g++-6\n          sources:\n            - ubuntu-toolchain-r-test\n\n    # gcc-7\n    - env: COMPILER=g++-7\n      compiler: gcc\n      os: linux\n      addons:\n        apt:\n          packages:\n            - g++-7\n          sources:\n            - ubuntu-toolchain-r-test\n\n    # gcc-8\n    - env: COMPILER=g++-8\n      compiler: gcc\n      os: linux\n      addons:\n        apt:\n          packages:\n          - g++-8\n          sources:\n          - ubuntu-toolchain-r-test\n\nbefore_script:\n  - export CXX=$COMPILER\n  - mkdir build\n  - cd build\n  - cmake --version\n  - cmake ..\n\nscript: make && ctest\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.2)\nproject(Seqlock CXX)\n\nset(CMAKE_EXPORT_COMPILE_COMMANDS \"true\")\nset(CMAKE_CXX_STANDARD 11)\n\nadd_library(Seqlock INTERFACE)\ntarget_include_directories(Seqlock INTERFACE\n        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>\n        $<INSTALL_INTERFACE:include>)\ntarget_compile_options(Seqlock INTERFACE -Wall -Wextra -Wpedantic -Werror)\n\nfind_package(Threads REQUIRED)\n\nadd_executable(SeqlockExample src/SeqlockExample.cpp)\ntarget_link_libraries(SeqlockExample Seqlock Threads::Threads)\n\nadd_executable(SeqlockTest src/SeqlockTest.cpp)\ntarget_link_libraries(SeqlockTest Seqlock Threads::Threads)\n\nenable_testing()\nadd_test(SeqlockTest SeqlockTest)\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2018 Erik Rigtorp <erik@rigtorp.se>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n"
  },
  {
    "path": "README.md",
    "content": "# Seqlock.h\n\n[![Build Status](https://travis-ci.org/rigtorp/Seqlock.svg?branch=master)](https://travis-ci.org/rigtorp/Seqlock)\n[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/rigtorp/Seqlock/master/LICENSE)\n\nImplementation of [seqlock](https://en.wikipedia.org/wiki/Seqlock) in\nC++11.\n\nA seqlock can be used as an alternative to\n[a readers-writer lock](https://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock). It\nwill never block the writer and doesn't require any memory bus locks.\n\n## Example\n\n```cpp\nstruct Data {\n  std::size_t a, b, c;\n};\nSeqlock<Data> sl;\nsl.store({0, 0, 0});\nauto t = std::thread([&] {\n  for (;;) {\n    auto d = sl.load();\n    if (d.a + 100 == d.b && d.c == d.a + d.b) {\n      return;\n    }\n  }\n});\nsl.store({100, 200, 300});\nt.join();\n```\n\n## Usage\n\nCreate a seqlock:\n\n```cpp\nstruct Data {\n  std::size_t a, b, c;\n};\nSeqlock<Data> sl;\n```\n\nStore a value (can only be called from a single thread):\n\n```cpp\nsl.store({1, 2, 3});\n```\n\nLoad a value (can be called from multiple threads):\n\n```cpp\nauto v = sl.load();\n```\n\n## Implementation\n\nImplementing the seqlock in portable C++11 is quite tricky. The basic\nseqlock implementation *unconditionally loads* the sequence number,\nthen *unconditionally loads* the protected data and finally\n*unconditionally loads* the sequence number again. Since loading the\nprotected data is done unconditionally on the sequence number the\ncompiler is free to move these loads before or after the loads from\nthe sequence number.\n\n```cpp\nT load() const noexcept {\n  T copy;\n  size_t seq0, seq1;\n  do {\n    seq0 = seq_.load(std::memory_order_acquire);\n    copy = value_;\n    seq1 = seq_.load(std::memory_order_acquire);\n  } while (seq0 != seq1 || seq0 & 1);\n  return copy;\n}\n```\n\nCompiling this code specialized for `int` with `g++-5.2 -std=c++11 -O3` yields\nthe following assembly:\n\n```\n0000000000401ad0 <_ZNK7rigtorp7SeqlockIiLm64EE4loadEv>:\n  // copy = value_;\n  401ad0:\t8b 47 08             \tmov    0x8(%rdi),%eax\n  401ad3:\t0f 1f 44 00 00       \tnopl   0x0(%rax,%rax,1)\n  // do {\n  //   seq0 = seq_.load(std::memory_order_acquire);\n  401ad8:\t48 8b 0f             \tmov    (%rdi),%rcx\n  //   seq1 = seq_.load(std::memory_order_acquire);\n  401adb:\t48 8b 17             \tmov    (%rdi),%rdx\n  // } while (seq0 != seq1 || seq0 & 1);\n  401ade:\t48 39 ca             \tcmp    %rcx,%rdx\n  401ae1:\t75 f5                \tjne    401ad8 <_ZNK7rigtorp7SeqlockIiLm64EE4loadEv+0x8>\n  401ae3:\t83 e2 01             \tand    $0x1,%edx\n  401ae6:\t75 f0                \tjne    401ad8 <_ZNK7rigtorp7SeqlockIiLm64EE4loadEv+0x8>\n  // return copy;\n  401ae8:\tf3 c3                \trepz retq \n  401aea:\t66 0f 1f 44 00 00    \tnopw   0x0(%rax,%rax,1)\n```\n\nWe see that the compiler did indeed reorder the load of the protected\ndata outside the critical section and the data is no longer protected\nfrom torn reads. Interestingly compiling using `clang++-3.7 -std=c++11\n-O3` produces the correct assembly:\n\n```\n0000000000401520 <_ZNK7rigtorp7SeqlockIiLm64EE4loadEv>:\n  // do {\n  //   seq0 = seq_.load(std::memory_order_acquire);  \n  401520:\t48 8b 0f             \tmov    (%rdi),%rcx\n  //   copy = value_;\n  401523:\t8b 47 08             \tmov    0x8(%rdi),%eax\n  //   seq1 = seq_.load(std::memory_order_acquire);\n  401526:\t48 8b 17             \tmov    (%rdi),%rdx\n  // } while (seq0 != seq1 || seq0 & 1);\n  401529:\tf6 c1 01             \ttest   $0x1,%cl\n  40152c:\t75 f2                \tjne    401520 <_ZNK7rigtorp7SeqlockIiLm64EE4loadEv>\n  40152e:\t48 39 d1             \tcmp    %rdx,%rcx\n  401531:\t75 ed                \tjne    401520 <_ZNK7rigtorp7SeqlockIiLm64EE4loadEv>\n  // return copy;\n  401533:\tc3                   \tretq   \n  401534:\t66 2e 0f 1f 84 00 00 \tnopw   %cs:0x0(%rax,%rax,1)\n  40153b:\t00 00 00 \n  40153e:\t66 90                \txchg   %ax,%ax\n```\n\nThere are two ways to fix this:\n\n* Make the location of the protected data dependent on the sequence\n  number by storing multiple instances of the data and selecting which\n  one to read from based on the sequence number. This solution should\n  be portable to all CPU architectures, but requires extra space.\n* For x86 it's enough to insert a compiler barrier using\n  `std::atomic_signal_fence(std::memory_order_acq_rel)`. This will\n  only work on the [x86 memory model][x86-mm]. On\n  [ARM memory model][arm-mm] you need to inserts a `dmb` memory\n  barrier instruction, which is not possible in C++11.\n  \nSince my target architecture is x86 I've implemented the second\noption:\n\n```cpp\nT load() const noexcept {\n  T copy;\n  size_t seq0, seq1;\n  do {\n    seq0 = seq_.load(std::memory_order_acquire);\n    std::atomic_signal_fence(std::memory_order_acq_rel);\n    copy = value_;\n    std::atomic_signal_fence(std::memory_order_acq_rel);\n    seq1 = seq_.load(std::memory_order_acquire);\n  } while (seq0 != seq1 || seq0 & 1);\n  return copy;\n}\n```\n\nCompiled with `g++-5.2 -std=c++11 -O3` it produces the\nfollowing correct assembly:\n\n```\n00000000004014e0 <_ZNK7rigtorp7SeqlockIiE4loadEv>:\n  4014e0:\t48 8d 4f 08          \tlea    0x8(%rdi),%rcx\n  4014e4:\t0f 1f 40 00          \tnopl   0x0(%rax)\n  // do {\n  //   seq0 = seq_.load(std::memory_order_acquire);\n  //   std::atomic_signal_fence(std::memory_order_acq_rel);\n  4014e8:\t48 8b 31             \tmov    (%rcx),%rsi\n  //   copy = value_;\n  4014eb:\t8b 07                \tmov    (%rdi),%eax\n  //   std::atomic_signal_fence(std::memory_order_acq_rel);\n  //   seq1 = seq_.load(std::memory_order_acquire);\n  4014ed:\t48 8b 11             \tmov    (%rcx),%rdx\n  // } while (seq0 != seq1 || seq0 & 1);\n  4014f0:\t48 39 f2             \tcmp    %rsi,%rdx\n  4014f3:\t75 f3                \tjne    4014e8 <_ZNK7rigtorp7SeqlockIiE4loadEv+0x8>\n  4014f5:\t83 e2 01             \tand    $0x1,%edx\n  4014f8:\t75 ee                \tjne    4014e8 <_ZNK7rigtorp7SeqlockIiE4loadEv+0x8>\n  // return copy;\n  4014fa:\tf3 c3                \trepz retq \n  4014fc:\t0f 1f 40 00          \tnopl   0x0(%rax)\n```\n\nThe store operation is implemented in a similar manner to the load\noperation. Additionally the data and sequence counter is aligned and\npadded to prevent false sharing with adjacent data.\n\nReferences:\n\n* [fast reader/writer lock for gettimeofday 2.5.30](http://lwn.net/Articles/7388/)\n* [Can Seqlocks Get Along With Programming Language Memory Models?](http://www.hpl.hp.com/techreports/2012/HPL-2012-68.pdf)\n  ([slides](http://safari.ece.cmu.edu/MSPC2012/slides_posters/boehm-slides.pdf))\n* [x86-TSO: A Rigorous and Usable Programmer’s Model for x86 Multiprocessors][x86-mm]\n* [A Tutorial Introduction to the ARM and POWER Relaxed Memory Models][arm-mm]\n\n[x86-mm]: http://www.cl.cam.ac.uk/~pes20/weakmemory/cacm.pdf\n[arm-mm]: http://www.cl.cam.ac.uk/~pes20/ppc-supplemental/test7.pdf\n\n## Testing\n\nTesting lock-free algorithms is hard. I'm using two approaches to test\nthe implementation:\n\n* A test that `load()` and `store()` publish results in the correct\n  order on a single thread.\n* A multithreaded fuzz test that `load()` never see a partial\n  `store()` (torn read).\n\n## Potential improvements\n\nAllow partial updates and reads using a visitor pattern.\n\nSupport for multiple writers can be achieved by using a CAS loop to\nacquire the odd sequence number in the store operation. This would\nhave the same effect as wrapping the seqlock in a spinlock.\n\nTrade-off space for reduced readers-writer contention by striping\nwrites across multiple seqlocks.\n\n## About\n\nThis project was created by [Erik Rigtorp](http://rigtorp.se)\n<[erik@rigtorp.se](mailto:erik@rigtorp.se)>.\n"
  },
  {
    "path": "include/rigtorp/Seqlock.h",
    "content": "/*\nCopyright (c) 2018 Erik Rigtorp <erik@rigtorp.se>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n */\n\n#pragma once\n\n#include <atomic>\n#include <type_traits>\n\n#ifndef NDEBUG\n#define RIGTORP_SEQLOCK_NOINLINE __attribute__((noinline))\n#else\n#define RIGTORP_SEQLOCK_NOINLINE\n#endif\n\nnamespace rigtorp {\n\ntemplate <typename T> class Seqlock {\npublic:\n  static_assert(std::is_nothrow_copy_assignable<T>::value,\n                \"T must satisfy is_nothrow_copy_assignable\");\n  static_assert(std::is_trivially_copy_assignable<T>::value,\n                \"T must satisfy is_trivially_copy_assignable\");\n\n  Seqlock() : seq_(0) {}\n\n  RIGTORP_SEQLOCK_NOINLINE T load() const noexcept {\n    T copy;\n    std::size_t seq0, seq1;\n    do {\n      seq0 = seq_.load(std::memory_order_acquire);\n      std::atomic_signal_fence(std::memory_order_acq_rel);\n      copy = value_;\n      std::atomic_signal_fence(std::memory_order_acq_rel);\n      seq1 = seq_.load(std::memory_order_acquire);\n    } while (seq0 != seq1 || seq0 & 1);\n    return copy;\n  }\n\n  RIGTORP_SEQLOCK_NOINLINE void store(const T &desired) noexcept {\n    std::size_t seq0 = seq_.load(std::memory_order_relaxed);\n    seq_.store(seq0 + 1, std::memory_order_release);\n    std::atomic_signal_fence(std::memory_order_acq_rel);\n    value_ = desired;\n    std::atomic_signal_fence(std::memory_order_acq_rel);\n    seq_.store(seq0 + 2, std::memory_order_release);\n  }\n\nprivate:\n  static const std::size_t kFalseSharingRange = 128;\n\n  // Align to prevent false sharing with adjecent data\n  alignas(kFalseSharingRange) T value_;\n  std::atomic<std::size_t> seq_;\n  // Padding to prevent false sharing with adjecent data\n  char padding_[kFalseSharingRange -\n                ((sizeof(value_) + sizeof(seq_)) % kFalseSharingRange)];\n  static_assert(\n      ((sizeof(value_) + sizeof(seq_) + sizeof(padding_)) %\n       kFalseSharingRange) == 0,\n      \"sizeof(Seqlock<T>) should be a multiple of kFalseSharingRange\");\n};\n}\n"
  },
  {
    "path": "src/SeqlockExample.cpp",
    "content": "#include <rigtorp/Seqlock.h>\n#include <thread>\n\nint main(int, char *[]) {\n  using namespace rigtorp;\n\n  struct Data {\n    std::size_t a, b, c;\n  };\n\n  Seqlock<Data> sl;\n  sl.store({0, 0, 0});\n  auto t = std::thread([&] {\n    for (;;) {\n      auto d = sl.load();\n      if (d.a + 100 == d.b && d.c == d.a + d.b) {\n        return;\n      }\n    }\n  });\n  sl.store({100, 200, 300});\n  t.join();\n\n  return 0;\n}\n"
  },
  {
    "path": "src/SeqlockTest.cpp",
    "content": "/*\nCopyright (c) 2018 Erik Rigtorp <erik@rigtorp.se>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n */\n\n#include <atomic>\n#include <iostream>\n#include <rigtorp/Seqlock.h>\n#include <thread>\n#include <vector>\n\nint main(int, char *[]) {\n  using namespace rigtorp;\n\n  // Basic test\n  {\n    Seqlock<int> sl;\n    sl.store(1);\n    if (sl.load() != 1) {\n      std::terminate();\n    }\n    sl.store(2);\n    if (sl.load() != 2) {\n      std::terminate();\n    }\n  }\n\n  // Fuzz test\n  {\n    struct Data {\n      std::size_t a, b, c;\n    };\n\n    Seqlock<Data> sl;\n    std::atomic<std::size_t> ready(0);\n    std::vector<std::thread> ts;\n\n    for (int i = 0; i < 100; ++i) {\n      ts.push_back(std::thread([&sl, &ready]() {\n        while (ready == 0) {\n        }\n        for (std::size_t i = 0; i < 10000000; ++i) {\n          auto copy = sl.load();\n          if (copy.a + 100 != copy.b || copy.c != copy.a + copy.b) {\n            std::terminate();\n          }\n        }\n        ready--;\n      }));\n    }\n\n    std::size_t counter = 0;\n    while (true) {\n      Data data = {counter++, data.a + 100, data.b + data.a};\n      sl.store(data);\n      if (counter == 1) {\n        ready += ts.size();\n      }\n      if (ready == 0) {\n        break;\n      }\n    }\n    std::cout << counter << std::endl;\n\n    for (auto &t : ts) {\n      t.join();\n    }\n  }\n\n  return 0;\n}\n"
  }
]