GCC Code Coverage Report


Directory: ./
File: tests/test_locks/main.cpp
Date: 2026-09-07 02:28:19
Exec Total Coverage
Lines: 0 49 0.0%
Functions: 0 5 0.0%
Branches: 0 118 0.0%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2000-2022 Inria
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * * Neither the name of the ALICE Project-Team nor the names of its
14 * contributors may be used to endorse or promote products derived from this
15 * software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 *
29 * Contact: Bruno Levy
30 *
31 * https://www.inria.fr/fr/bruno-levy
32 *
33 * Inria,
34 * Domaine de Voluceau,
35 * 78150 Le Chesnay - Rocquencourt
36 * FRANCE
37 *
38 */
39
40
41 #include <geogram/basic/common.h>
42 #include <geogram/basic/logger.h>
43 #include <geogram/basic/process.h>
44 #include <geogram/basic/command_line.h>
45 #include <geogram/basic/command_line_args.h>
46 #include <geogram/basic/stopwatch.h>
47
48 namespace {
49
50 using namespace GEO;
51
52 /**
53 * \brief Locking test
54 * \details
55 * The test consists in concurrent threads randomly accessing a critical
56 * section represented by an array of integer values. The critical section
57 * can be protected by either a global lock, or per-element using a
58 * SpinLockArray.
59 */
60 class LockTest {
61 public:
62 /**
63 * \brief Creates a new locking test
64 * \param[in] size number of elements in the critical section.
65 * \param[in] single_lock if true, the critical section is protected
66 * by a single global lock. Otherwise, the critical section is
67 * protected per-element using a SpinLockArray.
68 * \param[in] nb_times number of access operations performed by each
69 * thread.
70 */
71 LockTest(
72 index_t size, bool single_lock, index_t nb_times
73 ) :
74 single_lock_(single_lock),
75 global_lock_(GEOGRAM_SPINLOCK_INIT),
76 io_lock_(GEOGRAM_SPINLOCK_INIT),
77 nb_times_(nb_times)
78 {
79 Process::release_spinlock(global_lock_);
80 if(!single_lock_) {
81 locks_.resize(size);
82 }
83 data_.assign(size, -1);
84 }
85
86 /**
87 * \brief Accesses the critical section without locking
88 * \details This function is executed by each thread: it does nb_times
89 * random access in the critical section \b without locking it.
90 * \param[in] pid The id of the thread
91 */
92 void test_locks(index_t pid) {
93 Process::acquire_spinlock(io_lock_);
94 std::cerr << "Starting thread " << Thread::current()->id()
95 << std::endl;
96 Process::release_spinlock(io_lock_);
97 index_t j = 0;
98 for(index_t i = 0; i < nb_times_; ++i) {
99 j = (j + 7) % index_t(data_.size());
100 lock(j);
101 geo_assert(data_[j] == -1);
102 data_[j] = signed_index_t(pid);
103 fast_pause();
104 geo_assert(data_[j] == signed_index_t(pid));
105 data_[j] = -1;
106 unlock(j);
107 }
108 Process::acquire_spinlock(io_lock_);
109 std::cerr << "End of thread " << Thread::current()->id()
110 << std::endl;
111 Process::release_spinlock(io_lock_);
112 }
113
114 protected:
115 /**
116 * \brief Locks a critical section element
117 * \details Locks the element at index \p i in the critical section.
118 * In single_lock mode, this locks the whole critical section is
119 * locked, otherwise the element is locked individually using the
120 * spinlock at index \p i in a SpinLockArray .
121 * \param[in] i index of the element in the critical section
122 */
123 void lock(index_t i) {
124 if(single_lock_) {
125 Process::acquire_spinlock(global_lock_);
126 } else {
127 locks_.acquire_spinlock(i);
128 }
129 }
130
131 /**
132 * \brief Unlocks an critical section element
133 * \details Unlocks the element at index \p i in the critical section.
134 * In single_lock mode, this unlocks the whole critical section is
135 * locked, otherwise the element is unlocked individually using the
136 * spinlock at index \p i in a SpinLockArray .
137 * \param[in] i index of the element in the critical section
138 */
139 void unlock(index_t i) {
140 if(single_lock_) {
141 Process::release_spinlock(global_lock_);
142 } else {
143 locks_.release_spinlock(i);
144 }
145 }
146
147 /**
148 * \brief Does a (very fast pause)
149 * \details This pause is called by test_locks() to increase the time
150 * spent in the critical section.
151 */
152 static void fast_pause() {
153 for(index_t i = 0; i < 50000; ++i) {
154 }
155 }
156
157 private:
158 bool single_lock_;
159 Process::spinlock global_lock_;
160 Process::spinlock io_lock_;
161 Process::SpinLockArray locks_;
162 std::vector<signed_index_t> data_;
163 index_t nb_times_;
164 };
165 }
166
167 int main(int argc, char** argv) {
168 using namespace GEO;
169
170 GEO::initialize(GEO::GEOGRAM_INSTALL_ALL);
171
172 try {
173 Stopwatch W("Total time");
174 CmdLine::import_arg_group("standard");
175 CmdLine::declare_arg("array_size", 2, "number of cells in array");
176 CmdLine::declare_arg("global_lock", false, "use a global lock");
177 CmdLine::declare_arg("nb_times", 1000000, "number of ops");
178 CmdLine::declare_arg("locks", true, "use locks in test");
179
180 if(!CmdLine::parse(argc, argv)) {
181 return 1;
182 }
183
184 LockTest lock_test(
185 CmdLine::get_arg_uint("array_size"),
186 CmdLine::get_arg_bool("global_lock"),
187 CmdLine::get_arg_uint("nb_times")
188 );
189
190 if(CmdLine::get_arg_bool("locks")) {
191 parallel_for(
192 0, Process::max_threads(),
193 std::bind(&LockTest::test_locks, &lock_test, std::placeholders::_1)
194 );
195 } else {
196 parallel_for(
197 0, Process::max_threads(),
198 std::bind(&LockTest::test_locks, &lock_test, std::placeholders::_1)
199 );
200 }
201 }
202 catch(const std::exception& e) {
203 std::cerr << "Received an exception: " << e.what() << std::endl;
204 return 1;
205 }
206
207 return 0;
208 }
209