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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
|
/*
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; version 2 of the
* License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#include "base/threading.h"
#include "wb_helpers.h"
// Would be good if we could determine the order of how test modules are run.
// For instance the threading tests should run before higher level tests (e.g. the auto completion cache tests).
TEST_MODULE(threading_test, "Base library threading support tests");
// The minimum time span on which we base all other timing. Can be changed to counter
// unpredictable thread switching timing.
#define BASE_TIME 1000
static base::refcount_t counter;
gpointer thread_function1(gpointer data)
{
base::Semaphore *semaphore = static_cast<base::Semaphore *>(data);
semaphore->wait();
// We now can do some work. The main thread is waiting meanwhile.
// Total time 200ms.
while (++counter < 10)
g_usleep(20 * BASE_TIME);
// Done with work. Awake the main thread.
semaphore->post();
// We need here at least some wait time. If release and alloc follow too close to each other
// the other thread might not awake and the logic here doesn't work.
// Must be less than the wait time before the main thread calls try_wait().
g_usleep(BASE_TIME);
// Immediately allocate the semaphore again and wait a moment.
semaphore->wait();
g_usleep(1000 * BASE_TIME);
// Do some final work.
while (++counter < 15)
g_usleep(20 * BASE_TIME);
semaphore->post();
return NULL;
}
/**
* Semaphore test with cooperative semaphore (init count = 0).
* Intentional synchronization of two threads.
*/
TEST_FUNCTION(10)
{
base::Semaphore semaphore(0);
counter = 0;
GError *error = NULL;
GThread *thread = create_thread(thread_function1, &semaphore, &error);
if (thread == NULL)
{
const gchar * tmp = (error != NULL) ? error->message : "out of mem?";
fail(std::string("Thread creation failed: ") + tmp);
}
// Thread runs. Now wait for a moment. The thread does so too (via the semaphore).
g_usleep(100 * BASE_TIME);
ensure("Test counter was changed", counter == 0);
// Awake the thread and go to sleep.
semaphore.post();
g_usleep(50 * BASE_TIME); // Wait here. The thread starts working but needs longer than this time.
semaphore.wait(); // The thread awakes us here.
ensure_equals("Test counter value", counter, 10);
semaphore.post(); // Give the semaphore back so the thread can continue.
g_usleep(100 * BASE_TIME); // Wait a moment so that the thread actually gets CPU time.
// Try to allocated the semaphore (which must fail as the thread has it again).
ensure("Unexpectedly got semaphore access", !semaphore.try_wait());
ensure_equals("Test counter value", counter, 10);
// Wait for the thread to finish (will also release the semaphore).
g_thread_join(thread);
ensure_equals("Test counter value", counter, 15);
// Try a last time to allocate the semaphore. Should now succeed.
ensure("Couldn't get semaphore access", semaphore.try_wait());
semaphore.post();
}
gpointer thread_function2(gpointer data)
{
base::Semaphore *semaphore = static_cast<base::Semaphore *>(data);
semaphore->wait();
g_atomic_int_inc(&counter);
// Don't release the semaphore.
return NULL;
}
/**
* Concurrent semaphore test.
* 7 independent threads try to access 5 "counters".
*/
TEST_FUNCTION(20)
{
base::Semaphore semaphore(5);
counter = 0;
GThread *threads[7];
for (int i = 0; i < 7; ++i)
{
GError *error = NULL;
threads[i] = create_thread(thread_function2, &semaphore, &error);
if (threads[i] == NULL)
{
const gchar* tmp = (error != NULL) ? error->message : "out of mem?";
fail(std::string("Thread creation failed: ") + tmp);
}
}
try
{
g_usleep(10 * BASE_TIME);
// At this point only 5 threads can have done their job. 2 are still waiting.
ensure_equals("Wrong counter value", counter, 5);
// The threads did not release their allocation. We do this here to see if now the other
// 2 threads get their share.
semaphore.post();
g_usleep(50 * BASE_TIME);
ensure_equals("Wrong counter value", counter, 6);
g_usleep(100 * BASE_TIME); // Nothing must happen to the counter during that wait time.
ensure_equals("Wrong counter value", counter, 6);
semaphore.post();
g_usleep(50 * BASE_TIME);
ensure_equals("Wrong counter value", counter, 7);
for (int i = 0; i < 7; ++i)
g_thread_join(threads[i]);
}
catch (...)
{
// Always wait for the threads to finish or they access invalid memory which results
// in a serious error that would require user interaction on Win.
for (int i = 0; i < 7; ++i)
g_thread_join(threads[i]);
throw;
}
}
END_TESTS;
//----------------------------------------------------------------------------------------------------------------------
|