320 lines
6.9 KiB
C++
320 lines
6.9 KiB
C++
/* Copyright (c) 2014, 2016, 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 "my_global.h"
|
|
#include "my_thread.h"
|
|
#include <gtest/gtest.h>
|
|
#include <string.h>
|
|
#include "my_timer.h"
|
|
#include "thread_utils.h"
|
|
#include "my_sys.h"
|
|
#include "thr_template.cc"
|
|
|
|
#ifdef HAVE_PSI_INTERFACE
|
|
PSI_mutex_key key_thd_timer_mutex= PSI_NOT_INSTRUMENTED;
|
|
PSI_thread_key key_thread_timer_notifier= PSI_NOT_INSTRUMENTED;
|
|
#endif
|
|
PSI_memory_key key_memory_thd_timer= PSI_NOT_INSTRUMENTED;
|
|
|
|
namespace my_timer_unittest {
|
|
|
|
typedef struct
|
|
{
|
|
my_timer_t timer;
|
|
unsigned int fired;
|
|
native_mutex_t mutex;
|
|
native_cond_t cond;
|
|
} test_timer_t;
|
|
|
|
static void timer_notify_function(my_timer_t *timer)
|
|
{
|
|
test_timer_t *test= my_container_of(timer, test_timer_t, timer);
|
|
|
|
native_mutex_lock(&test->mutex);
|
|
test->fired++;
|
|
native_cond_signal(&test->cond);
|
|
native_mutex_unlock(&test->mutex);
|
|
}
|
|
|
|
static void test_timer_create(test_timer_t *test)
|
|
{
|
|
memset(test, 0, sizeof(test_timer_t));
|
|
native_mutex_init(&test->mutex, NULL);
|
|
native_cond_init(&test->cond);
|
|
EXPECT_EQ(my_timer_create(&test->timer), 0);
|
|
test->timer.notify_function= timer_notify_function;
|
|
}
|
|
|
|
static void test_timer_destroy(test_timer_t *test)
|
|
{
|
|
native_mutex_destroy(&test->mutex);
|
|
native_cond_destroy(&test->cond);
|
|
my_timer_delete(&test->timer);
|
|
}
|
|
|
|
static void timer_set_and_wait(test_timer_t *test, unsigned int fired_count)
|
|
{
|
|
int rc, state;
|
|
|
|
rc= my_timer_set(&test->timer, 5);
|
|
EXPECT_EQ(rc, 0);
|
|
|
|
// timer not fired yet
|
|
EXPECT_TRUE((test->fired != fired_count));
|
|
|
|
while (test->fired != fired_count)
|
|
native_cond_wait(&test->cond, &test->mutex);
|
|
|
|
// timer fired
|
|
EXPECT_TRUE(test->fired == fired_count);
|
|
|
|
rc= my_timer_cancel(&test->timer, &state);
|
|
EXPECT_EQ(rc, 0);
|
|
|
|
// timer state was signaled
|
|
EXPECT_EQ(state, 0);
|
|
}
|
|
|
|
static void test_timer(void)
|
|
{
|
|
int rc;
|
|
test_timer_t test;
|
|
|
|
test_timer_create(&test);
|
|
|
|
native_mutex_lock(&test.mutex);
|
|
|
|
rc= my_timer_set(&test.timer, 5);
|
|
EXPECT_EQ(rc, 0);
|
|
|
|
/* not fired yet */
|
|
EXPECT_EQ(test.fired, (unsigned int)0);
|
|
|
|
while (!test.fired)
|
|
native_cond_wait(&test.cond, &test.mutex);
|
|
|
|
/* timer fired once */
|
|
EXPECT_EQ(test.fired, (unsigned int)1);
|
|
|
|
native_mutex_unlock(&test.mutex);
|
|
|
|
test_timer_destroy(&test);
|
|
}
|
|
|
|
extern "C" void* test_timer_per_thread(void *arg)
|
|
{
|
|
int iter= *(int *) arg;
|
|
|
|
while (iter--)
|
|
test_timer();
|
|
|
|
mysql_mutex_lock(&mutex);
|
|
if (!--running_threads)
|
|
mysql_cond_signal(&cond);
|
|
mysql_mutex_unlock(&mutex);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Test timer creation and deletion. */
|
|
TEST(Mysys, TimerCreateAndDelete)
|
|
{
|
|
int rc;
|
|
my_timer_t timer;
|
|
|
|
EXPECT_EQ(my_timer_initialize(),0);
|
|
|
|
memset(&timer, 0, sizeof(timer));
|
|
|
|
rc= my_timer_create(&timer);
|
|
EXPECT_EQ(rc, 0);
|
|
|
|
my_timer_delete(&timer);
|
|
|
|
my_timer_deinitialize();
|
|
}
|
|
|
|
/* Test single timer in one thread */
|
|
TEST(Mysys, TestTimer)
|
|
{
|
|
int rc;
|
|
test_timer_t test;
|
|
|
|
EXPECT_EQ(my_timer_initialize(),0);
|
|
|
|
test_timer_create(&test);
|
|
|
|
native_mutex_lock(&test.mutex);
|
|
|
|
rc= my_timer_set(&test.timer, 5);
|
|
EXPECT_EQ(rc, 0);
|
|
|
|
// timer not fired yet
|
|
EXPECT_EQ(test.fired, (unsigned int)0);
|
|
|
|
while (!test.fired)
|
|
native_cond_wait(&test.cond, &test.mutex);
|
|
|
|
// timer fired once
|
|
EXPECT_EQ(test.fired, (unsigned int)1);
|
|
|
|
native_mutex_unlock(&test.mutex);
|
|
|
|
test_timer_destroy(&test);
|
|
|
|
my_timer_deinitialize();
|
|
}
|
|
|
|
/* Test reset function of timer */
|
|
TEST(Mysys, TestTimerReset)
|
|
{
|
|
int rc, state;
|
|
test_timer_t test;
|
|
|
|
EXPECT_EQ(my_timer_initialize(),0);
|
|
|
|
test_timer_create(&test);
|
|
|
|
native_mutex_lock(&test.mutex);
|
|
|
|
rc= my_timer_set(&test.timer, 600000);
|
|
EXPECT_EQ(rc, 0);
|
|
|
|
// timer not fired yet
|
|
EXPECT_EQ(test.fired, (unsigned int)0);
|
|
|
|
// reset timer
|
|
rc= my_timer_cancel(&test.timer, &state);
|
|
EXPECT_EQ(rc, 0);
|
|
|
|
native_mutex_unlock(&test.mutex);
|
|
|
|
test_timer_destroy(&test);
|
|
|
|
my_timer_deinitialize();
|
|
}
|
|
|
|
/* Test multiple timers in single thread */
|
|
TEST(Mysys, TestMultipleTimers)
|
|
{
|
|
int rc, state;
|
|
test_timer_t test1, test2, test3;
|
|
|
|
EXPECT_EQ(my_timer_initialize(),0);
|
|
|
|
// Timer "test1"
|
|
test_timer_create(&test1);
|
|
native_mutex_lock(&test1.mutex);
|
|
rc= my_timer_set(&test1.timer, 3);
|
|
EXPECT_EQ(rc, 0);
|
|
|
|
// Timer "test2"
|
|
test_timer_create(&test2);
|
|
native_mutex_lock(&test2.mutex);
|
|
rc= my_timer_set(&test2.timer, 6);
|
|
EXPECT_EQ(rc, 0);
|
|
|
|
// Timer "test3"
|
|
test_timer_create(&test3);
|
|
native_mutex_lock(&test3.mutex);
|
|
rc= my_timer_set(&test3.timer, 600000);
|
|
EXPECT_EQ(rc, 0);
|
|
|
|
// Wait till test1 timer fired.
|
|
while (!test1.fired)
|
|
native_cond_wait(&test1.cond, &test1.mutex);
|
|
|
|
// Wait till test2 timer fired.
|
|
while (!test2.fired)
|
|
native_cond_wait(&test2.cond, &test2.mutex);
|
|
|
|
// timer test1 fired
|
|
EXPECT_EQ(test1.fired, (unsigned int)1);
|
|
|
|
// timer test2 fired
|
|
EXPECT_EQ(test2.fired, (unsigned int)1);
|
|
|
|
// timer test3 not fired yet
|
|
EXPECT_EQ(test3.fired, (unsigned int)0);
|
|
|
|
// reset timer test3
|
|
rc= my_timer_cancel(&test3.timer, &state);
|
|
EXPECT_EQ(rc, 0);
|
|
|
|
native_mutex_unlock(&test1.mutex);
|
|
native_mutex_unlock(&test2.mutex);
|
|
native_mutex_unlock(&test3.mutex);
|
|
|
|
test_timer_destroy(&test1);
|
|
test_timer_destroy(&test2);
|
|
test_timer_destroy(&test3);
|
|
|
|
my_timer_deinitialize();
|
|
}
|
|
|
|
/* Test timer in multiple threads */
|
|
TEST(Mysys, TestTimerPerThread)
|
|
{
|
|
mysql_mutex_init(0, &mutex, 0);
|
|
mysql_cond_init(0, &cond);
|
|
my_thread_attr_init(&thr_attr);
|
|
my_thread_attr_setdetachstate(&thr_attr, MY_THREAD_CREATE_DETACHED);
|
|
|
|
EXPECT_EQ(my_timer_initialize(),0);
|
|
|
|
test_concurrently("per-thread", test_timer_per_thread, THREADS, 5);
|
|
|
|
my_timer_deinitialize();
|
|
mysql_mutex_destroy(&mutex);
|
|
mysql_cond_destroy(&cond);
|
|
my_thread_attr_destroy(&thr_attr);
|
|
}
|
|
|
|
/* Test timer reuse functionality */
|
|
TEST(Mysys, TestTimerReuse)
|
|
{
|
|
test_timer_t test;
|
|
|
|
EXPECT_EQ(my_timer_initialize(),0);
|
|
|
|
test_timer_create(&test);
|
|
|
|
native_mutex_lock(&test.mutex);
|
|
|
|
timer_set_and_wait(&test, 1);
|
|
timer_set_and_wait(&test, 2);
|
|
timer_set_and_wait(&test, 3);
|
|
|
|
native_mutex_unlock(&test.mutex);
|
|
|
|
test_timer_destroy(&test);
|
|
|
|
my_timer_deinitialize();
|
|
}
|
|
|
|
/* Test timer module reinitialization */
|
|
TEST(Mysys, TestReinitialization)
|
|
{
|
|
EXPECT_EQ(my_timer_initialize(),0);
|
|
test_timer();
|
|
my_timer_deinitialize();
|
|
|
|
// Reinitialization
|
|
EXPECT_EQ(my_timer_initialize(),0);
|
|
test_timer();
|
|
my_timer_deinitialize();
|
|
}
|
|
}
|