llx_scx_test.cc 6.17 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 67 68
  // Every thread adds every character from 'a' to 'z' into an ordered
  // linked list
  for (char value = 'a'; value <= 'z';) {
69
    // Find node to append new element on:
70 71 72 73 74
    internal::LlxScxRecord<Node> * node_rec = &head_llx;
    internal::LlxScxRecord<Node> * next_rec = node_rec->Data().next_;
    while (next_rec != 0 && next_rec->Data().value_ < value) {
      node_rec = next_rec;
      next_rec = next_rec->Data().next_;
75
    }
76
    Node node;
77 78
    bool finalized;
    // LLX on node the new element will be appended to:
79
    if (!llxscx_.TryLoadLinked(node_rec, node, finalized)) {
80 81
      continue;
    }
82 83
    PT_ASSERT_MSG(!finalized, "No node should be finalized");
    if (node.next_ == next_rec) {
84
      // Pointer still valid after LLX, try to append new node
85
      internal::FixedSizeList<LlxScxRecord<Node> *> linked_deps(1);
86
      internal::FixedSizeList<LlxScxRecord<Node> *> finalize_deps(0);
87
      linked_deps.PushBack(node_rec);
88
      // Create new node:
89
      Node new_node(static_cast<int>(thread_index), value, next_rec);
90 91
      internal::LlxScxRecord<Node> * new_node_ptr =
        new internal::LlxScxRecord<Node>(new_node);
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> * >(
97
          &(node_rec->Data().next_));
98
      // Call SCX:
99 100
      bool element_inserted =
        llxscx_.TryStoreConditional(
101 102 103 104
          field_cas_ptr,
          new_cas_value,
          linked_deps,
          finalize_deps);
105 106 107 108
      if (element_inserted) {
        // Value has been added to list, continue with next value
        ++value;
      }
109
    }
110 111 112 113
  }
}

void LlxScxTest::ParallelTestPost() {
114
  std::vector< std::pair<char, int> > values;
115 116 117
  internal::LlxScxRecord<Node> * node = &head_llx;
  internal::LlxScxRecord<Node> * next = head_llx.Data().next_;
  while (next != 0) {
118 119 120
    values.push_back(std::make_pair(
      next->Data().value_,
      next->Data().count_.Load()));
121 122 123
    node = next;
    next = next->Data().next_;
  }
124 125 126
  // Check if every character from 'a' to 'z' has been added to the
  // linked list in the correct order
  PT_ASSERT_EQ_MSG(static_cast<size_t>(26 * num_threads_), values.size(),
127
    "Unexpected size of result list");
128 129
}

130 131 132 133 134 135 136 137 138
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);
139

140 141 142
  LlxScxRecord< Payload > r1(100);
  LlxScxRecord< Payload > r2(200);
  LlxScxRecord< Payload > r3(300);
143

144 145 146 147 148 149 150 151
  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);
152

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

160 161
  PT_ASSERT(llxscx.TryValidateLink(links));

162 163 164
  FixedSizeList< LlxScxRecord<Payload> * >
    finalize_links(1);
  finalize_links.PushBack(&r3);
165 166 167

  // Try to store new value depending on links:
  size_t a = 42;
168 169 170 171
  PT_ASSERT(llxscx.TryStoreConditional(
    &field, a,
    links,
    finalize_links));
172 173
  // New value should have been changed successfully:
  PT_ASSERT_EQ(field.Load(), a);
174 175

  PT_ASSERT(!llxscx.TryValidateLink(links));
176 177
}

178 179 180
} // namespace test
} // namespace containers
} // namespace embb