hazard_pointer_test.cc 5.38 KB
Newer Older
1
/*
2
 * Copyright (c) 2014-2015, Siemens AG. All rights reserved.
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
 *
 * 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 "./hazard_pointer_test.h"

29 30
#include <embb/base/internal/config.h>

31 32 33 34
namespace embb {
namespace containers {
namespace test {
HazardPointerTest::HazardPointerTest() :
35
#ifdef EMBB_PLATFORM_COMPILER_MSVC
36 37 38
#pragma warning(push)
#pragma warning(disable:4355)
#endif
39
  delete_pointer_callback(*this, &HazardPointerTest::DeletePointerCallback),
40
#ifdef EMBB_PLATFORM_COMPILER_MSVC
41 42
#pragma warning(pop)
#endif
43 44 45 46
  object_pool(NULL),
  stack(NULL),
  hp(NULL),
n_threads(static_cast<int>
47
  (partest::TestSuite::GetDefaultNumThreads())) {
48 49
  n_elements_per_thread = 100;
  n_elements = n_threads*n_elements_per_thread;
50
  embb::base::Function < void, embb::base::Atomic<int>* >
51
    delete_pointer_callback(
52 53 54 55 56 57 58 59 60 61
    *this,
    &HazardPointerTest::DeletePointerCallback);

  // Kind of timing depending test. But tests exactly what hazard pointers are
  // designed for. One thread creates an element, does something, retires it.
  // Another thread also accesses this element (passed using a stack), by
  // placing a guard it protects this element. If the guard was successfully
  // placed, the pointer is not allowed to be deleted until the second thread
  // removes this guard.
  CreateUnit("HazardPointerTestThatGuardWorks").
62
    Pre(&HazardPointerTest::HazardPointerTest1_Pre, this).
63
    Add(
64 65 66
    &HazardPointerTest::HazardPointerTest1_ThreadMethod,
    this, static_cast<size_t>(n_threads)).
    Post(&HazardPointerTest::HazardPointerTest1_Post, this);
67 68
}

69
void HazardPointerTest::HazardPointerTest1_Pre() {
70
  embb_internal_thread_index_reset();
71 72 73 74 75 76 77
  object_pool = new embb::containers::ObjectPool< embb::base::Atomic<int> >
    (static_cast<size_t>(n_elements));
  stack = new embb::containers::LockFreeStack< embb::base::Atomic<int>* >
    (static_cast<size_t>(n_elements));
  hp = new embb::containers::internal::HazardPointer< embb::base::Atomic<int>*>
    (delete_pointer_callback,
    NULL,
78 79 80
    1);
}

81 82 83 84
void HazardPointerTest::HazardPointerTest1_Post() {
  delete object_pool;
  delete stack;
  delete hp;
85 86
}

87
void HazardPointerTest::HazardPointerTest1_ThreadMethod() {
88 89 90
  unsigned int thread_index;
  embb_internal_thread_index(&thread_index);

91 92
  for (int i = 0; i != n_elements_per_thread; ++i) {
    embb::base::Atomic<int>* allocated_object = object_pool->Allocate(0);
93

94
    hp->GuardPointer(0, allocated_object);
95

96
    bool success = stack->TryPush(allocated_object);
97 98 99

    PT_ASSERT(success == true);

100
    embb::base::Atomic<int>* allocated_object_from_different_thread;
101 102 103 104 105 106 107

    int diff_count = 0;

    bool same = false;
    bool success_pop;

    while (
108
      (success_pop = stack->TryPop(allocated_object_from_different_thread))
109 110 111
      == true
      && allocated_object_from_different_thread == allocated_object
      ) {
112 113
      //try to make it probable to get an element from a different thread
      //however, can be the same. Try 10000 times to get a different element.
114 115 116 117
      if (diff_count++ > 10000) {
        same = true;
        break;
      }
118
      bool success = stack->TryPush(allocated_object_from_different_thread);
119 120 121 122
      PT_ASSERT(success == true);
    }
    PT_ASSERT(success_pop == true);
    allocated_object->Store(1);
123
    hp->EnqueuePointerForDeletion(allocated_object);
124 125

    if (!same) {
126
      hp->GuardPointer(0, allocated_object_from_different_thread);
127 128 129 130 131 132

      // if this holds, we were successful in guarding... otherwise we
      // were to late, because the pointer has already been added
      // to the retired list.
      if (*allocated_object_from_different_thread == 0) {
        // the pointer must not be deleted here!
133
        vector_mutex.Lock();
134
        for (std::vector< embb::base::Atomic<int>* >::iterator
135 136
          it = deleted_vector.begin();
          it != deleted_vector.end();
137 138 139
        ++it) {
          PT_ASSERT(*it != allocated_object_from_different_thread);
        }
140
        vector_mutex.Unlock();
141
      }
142
      hp->GuardPointer(0, NULL);
143 144 145 146 147 148
    }
  }
}

void HazardPointerTest::DeletePointerCallback
(embb::base::Atomic<int>* to_delete) {
149 150 151
  vector_mutex.Lock();
  deleted_vector.push_back(to_delete);
  vector_mutex.Unlock();
152
}
153 154 155
} // namespace test
} // namespace containers
} // namespace embb