llx_scx_test.cc 5.82 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
/*
 * Copyright (c) 2014-2015, Siemens AG. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <llx_scx_test.h>
#include <embb/containers/internal/fixed_size_list.h>
29
#include <embb/containers/internal/llx_scx.h>
30
// #include <embb/containers/multiword_ll_sc.h>
31 32 33 34 35 36

namespace embb {
namespace containers {
namespace test {

using embb::containers::internal::FixedSizeList;
37 38
using embb::containers::internal::LlxScxRecord;
using embb::containers::internal::LlxScx;
39 40

LlxScxTest::LlxScxTest() :
41
  num_threads_(
42 43 44
    static_cast<int>(partest::TestSuite::GetDefaultNumThreads())),
  llxscx_(3),
  tail(0, '-'),
45 46 47
  head(0, '-'),
  tail_llx(tail),
  head_llx(head) {
48 49 50
  CreateUnit("SerialArrayTest")
    .Add(&LlxScxTest::SerialArrayTest, this);
  CreateUnit("ParallelTest")
51 52 53
    .Pre(&LlxScxTest::ParallelTestPre, this)
    .Add(&LlxScxTest::ParallelTest, this,
         static_cast<unsigned int>(num_threads_), 1)
54
    .Post(&LlxScxTest::ParallelTestPost, this);
55 56
}

57 58 59 60
void LlxScxTest::ParallelTestPre() {
  embb_internal_thread_index_reset();
}

61 62 63 64
void LlxScxTest::ParallelTest() {
  unsigned int thread_index;
  int return_val = embb_internal_thread_index(&thread_index);
  if (return_val != EMBB_SUCCESS)
65
    EMBB_THROW(embb::base::ErrorException, "Could not get thread id!"); 
66
  // Threads try to append n nodes to a linked list in parallel
67 68 69 70 71 72 73 74 75
  for (char value = 'a'; value <= 'z';) {
    // Find node to append new element on:
    internal::LlxScxRecord<Node> * node = &head_llx;
    internal::LlxScxRecord<Node> * next = node->Data().next_;
    while (next != 0 && next->Data().value_ < value) {
      node = next;
      next = next->Data().next_;
    }
    Node n;
76 77
    bool finalized;
    // LLX on node the new element will be appended to:
78 79 80
    if (!llxscx_.TryLoadLinked(node, n, finalized)) {
      continue;
    }
81
    if (n.next_ == next) {
82
      // Pointer still valid after LLX, try to append new node
83
      internal::FixedSizeList<LlxScxRecord<Node> *> linked_deps(1);
84
      internal::FixedSizeList<LlxScxRecord<Node> *> finalize_deps(0);
85 86 87 88 89
      linked_deps.PushBack(node);
      // Create new node:
      Node new_node(static_cast<int>(thread_index), value);
      internal::LlxScxRecord<Node> * new_node_ptr =
        new internal::LlxScxRecord<Node>(new_node);
90 91 92 93 94 95 96
      // Convert node pointer to size_t:
      size_t new_cas_value = reinterpret_cast<size_t>(new_node_ptr);
      // Convert target field pointer to size_t*:
      embb::base::Atomic<size_t> * field_cas_ptr =
        reinterpret_cast< embb::base::Atomic<size_t> * >(
          &(node->Data().next_));
      // Call SCX:
97 98
      bool element_inserted =
        llxscx_.TryStoreConditional(
99 100 101 102
          field_cas_ptr,
          new_cas_value,
          linked_deps,
          finalize_deps);
103 104 105 106
      if (element_inserted) {
        // Value has been added to list, continue with next value
        ++value;
      }
107
    }
108 109 110 111
  }
}

void LlxScxTest::ParallelTestPost() {
112
  std::vector< std::pair<char, int> > values;
113 114 115
  internal::LlxScxRecord<Node> * node = &head_llx;
  internal::LlxScxRecord<Node> * next = head_llx.Data().next_;
  while (next != 0) {
116 117 118
    values.push_back(std::make_pair(
      next->Data().value_,
      next->Data().count_.Load()));
119 120 121
    node = next;
    next = next->Data().next_;
  }
122 123
  PT_ASSERT_EQ_MSG(static_cast<size_t>(26 * num_threads_), values.size(),
    "Unexpected size of result list");
124 125
}

126 127 128 129 130 131 132 133 134
void LlxScxTest::SerialArrayTest() {
  typedef int Payload;
  typedef embb::base::Atomic<size_t> AtomicField;
  // LLX/SCX with maximum of 3 active load-links in every thread:
  LlxScx<Payload> llxscx(3);

  // Atomic<size_t> not assignable, TryStoreConditional requires
  // a specialization for atomics that uses a.Store(b.Load()).
  AtomicField field(23);
135

136 137 138
  LlxScxRecord< Payload > r1(100);
  LlxScxRecord< Payload > r2(200);
  LlxScxRecord< Payload > r3(300);
139

140 141 142 143 144 145 146 147
  Payload l1, l2, l3;
  bool finalized;  
  PT_ASSERT(llxscx.TryLoadLinked(&r1, l1, finalized));
  PT_ASSERT_EQ(100, l1);
  PT_ASSERT(llxscx.TryLoadLinked(&r2, l2, finalized));
  PT_ASSERT_EQ(200, l2);
  PT_ASSERT(llxscx.TryLoadLinked(&r3, l3, finalized));
  PT_ASSERT_EQ(300, l3);
148

149
  // links = { dr1, dr2, dr3 }
150
  FixedSizeList< LlxScxRecord<Payload> * >
151 152 153 154 155 156 157 158
    links(3);
  links.PushBack(&r1);
  links.PushBack(&r2);
  links.PushBack(&r3);

  FixedSizeList< LlxScxRecord<Payload> * >
    finalize_links(1);
  finalize_links.PushBack(&r3);
159 160 161

  // Try to store new value depending on links:
  size_t a = 42;
162 163 164 165
  PT_ASSERT(llxscx.TryStoreConditional(
    &field, a,
    links,
    finalize_links));
166 167 168 169
  // New value should have been changed successfully:
  PT_ASSERT_EQ(field.Load(), a);
}

170 171 172
} // namespace test
} // namespace containers
} // namespace embb