mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 03:52:35 +01:00
214 lines
6.8 KiB
C++
214 lines
6.8 KiB
C++
/*
|
|
Copyright (c) 2015, Facebook, Inc.
|
|
|
|
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 Street, Fifth Floor, Boston, MA 02111-1301 USA */
|
|
|
|
#include <my_global.h>
|
|
|
|
/* This C++ file's header file */
|
|
#include "./rdb_mutex_wrapper.h"
|
|
|
|
/* The following are for THD_ENTER_COND: */
|
|
#define MYSQL_SERVER 1
|
|
#include "sql_priv.h"
|
|
#include "my_decimal.h"
|
|
#include "sql_class.h"
|
|
//psergey-merge-todo: does MariaDB have/need: #include "../sql/replication.h"
|
|
|
|
|
|
/* MyRocks header files */
|
|
#include "./ha_rocksdb.h"
|
|
#include "./rdb_utils.h"
|
|
|
|
|
|
using namespace rocksdb;
|
|
|
|
namespace myrocks {
|
|
|
|
static PSI_stage_info stage_waiting_on_row_lock2 = {0, "Waiting for row lock",
|
|
0};
|
|
|
|
static const int64_t ONE_SECOND_IN_MICROSECS = 1000 * 1000;
|
|
// A timeout as long as one full non-leap year worth of microseconds is as
|
|
// good as infinite timeout.
|
|
static const int64_t ONE_YEAR_IN_MICROSECS =
|
|
ONE_SECOND_IN_MICROSECS * 60 * 60 * 24 * 365;
|
|
|
|
Rdb_cond_var::Rdb_cond_var() { mysql_cond_init(0, &m_cond, nullptr); }
|
|
|
|
Rdb_cond_var::~Rdb_cond_var() { mysql_cond_destroy(&m_cond); }
|
|
|
|
Status Rdb_cond_var::Wait(const std::shared_ptr<TransactionDBMutex> mutex_arg) {
|
|
return WaitFor(mutex_arg, ONE_YEAR_IN_MICROSECS);
|
|
}
|
|
|
|
/*
|
|
@brief
|
|
Wait on condition variable. The caller must make sure that we own
|
|
*mutex_ptr. The mutex is released and re-acquired by the wait function.
|
|
|
|
@param
|
|
timeout_micros Timeout in microseconds. Negative value means no timeout.
|
|
|
|
@return
|
|
Status::OK() - Wait successfull
|
|
Status::TimedOut() - Timed out or wait killed (the caller can check
|
|
thd_killed() to determine which occurred)
|
|
*/
|
|
|
|
Status Rdb_cond_var::WaitFor(
|
|
const std::shared_ptr<TransactionDBMutex> mutex_arg,
|
|
int64_t timeout_micros) {
|
|
auto *mutex_obj = reinterpret_cast<Rdb_mutex *>(mutex_arg.get());
|
|
DBUG_ASSERT(mutex_obj != nullptr);
|
|
|
|
mysql_mutex_t *const mutex_ptr = &mutex_obj->m_mutex;
|
|
|
|
int res = 0;
|
|
struct timespec wait_timeout;
|
|
|
|
if (timeout_micros < 0) timeout_micros = ONE_YEAR_IN_MICROSECS;
|
|
set_timespec_nsec(wait_timeout, timeout_micros * 1000);
|
|
|
|
#ifndef STANDALONE_UNITTEST
|
|
PSI_stage_info old_stage;
|
|
mysql_mutex_assert_owner(mutex_ptr);
|
|
|
|
if (current_thd && mutex_obj->m_old_stage_info.count(current_thd) == 0) {
|
|
THD_ENTER_COND(current_thd, &m_cond, mutex_ptr, &stage_waiting_on_row_lock2,
|
|
&old_stage);
|
|
/*
|
|
After the mysql_cond_timedwait we need make this call
|
|
|
|
THD_EXIT_COND(thd, &old_stage);
|
|
|
|
to inform the SQL layer that KILLable wait has ended. However,
|
|
that will cause mutex to be released. Defer the release until the mutex
|
|
that is unlocked by RocksDB's Pessimistic Transactions system.
|
|
*/
|
|
mutex_obj->set_unlock_action(&old_stage);
|
|
}
|
|
|
|
#endif
|
|
bool killed = false;
|
|
|
|
do {
|
|
res = mysql_cond_timedwait(&m_cond, mutex_ptr, &wait_timeout);
|
|
|
|
#ifndef STANDALONE_UNITTEST
|
|
if (current_thd) killed = thd_killed(current_thd);
|
|
#endif
|
|
} while (!killed && res == EINTR);
|
|
|
|
if (res || killed) {
|
|
return Status::TimedOut();
|
|
} else {
|
|
return Status::OK();
|
|
}
|
|
}
|
|
|
|
/*
|
|
|
|
@note
|
|
This function may be called while not holding the mutex that is used to wait
|
|
on the condition variable.
|
|
|
|
The manual page says ( http://linux.die.net/man/3/pthread_cond_signal):
|
|
|
|
The pthread_cond_broadcast() or pthread_cond_signal() functions may be called
|
|
by a thread whether or not it currently owns the mutex that threads calling
|
|
pthread_cond_wait() or pthread_cond_timedwait() have associated with the
|
|
condition variable during their waits; however, IF PREDICTABLE SCHEDULING
|
|
BEHAVIOR IS REQUIRED, THEN THAT MUTEX SHALL BE LOCKED by the thread calling
|
|
pthread_cond_broadcast() or pthread_cond_signal().
|
|
|
|
What's "predicate scheduling" and do we need it? The explanation is here:
|
|
|
|
https://groups.google.com/forum/?hl=ky#!msg/comp.programming.threads/wEUgPq541v8/ZByyyS8acqMJ
|
|
"The problem (from the realtime side) with condition variables is that
|
|
if you can signal/broadcast without holding the mutex, and any thread
|
|
currently running can acquire an unlocked mutex and check a predicate
|
|
without reference to the condition variable, then you can have an
|
|
indirect priority inversion."
|
|
|
|
Another possible consequence is that one can create spurious wake-ups when
|
|
there are multiple threads signaling the condition.
|
|
|
|
None of this looks like a problem for our use case.
|
|
*/
|
|
|
|
void Rdb_cond_var::Notify() { mysql_cond_signal(&m_cond); }
|
|
|
|
/*
|
|
@note
|
|
This is called without holding the mutex that's used for waiting on the
|
|
condition. See ::Notify().
|
|
*/
|
|
void Rdb_cond_var::NotifyAll() { mysql_cond_broadcast(&m_cond); }
|
|
|
|
Rdb_mutex::Rdb_mutex() {
|
|
mysql_mutex_init(0 /* Don't register in P_S. */, &m_mutex,
|
|
MY_MUTEX_INIT_FAST);
|
|
}
|
|
|
|
Rdb_mutex::~Rdb_mutex() { mysql_mutex_destroy(&m_mutex); }
|
|
|
|
Status Rdb_mutex::Lock() {
|
|
RDB_MUTEX_LOCK_CHECK(m_mutex);
|
|
DBUG_ASSERT(m_old_stage_info.count(current_thd) == 0);
|
|
return Status::OK();
|
|
}
|
|
|
|
// Attempt to acquire lock. If timeout is non-negative, operation may be
|
|
// failed after this many milliseconds.
|
|
// If implementing a custom version of this class, the implementation may
|
|
// choose to ignore the timeout.
|
|
// Return OK on success, or other Status on failure.
|
|
Status Rdb_mutex::TryLockFor(int64_t timeout_time MY_ATTRIBUTE((__unused__))) {
|
|
/*
|
|
Note: PThreads API has pthread_mutex_timedlock(), but mysql's
|
|
mysql_mutex_* wrappers do not wrap that function.
|
|
*/
|
|
RDB_MUTEX_LOCK_CHECK(m_mutex);
|
|
return Status::OK();
|
|
}
|
|
|
|
#ifndef STANDALONE_UNITTEST
|
|
void Rdb_mutex::set_unlock_action(const PSI_stage_info *const old_stage_arg) {
|
|
DBUG_ASSERT(old_stage_arg != nullptr);
|
|
|
|
mysql_mutex_assert_owner(&m_mutex);
|
|
DBUG_ASSERT(m_old_stage_info.count(current_thd) == 0);
|
|
|
|
m_old_stage_info[current_thd] =
|
|
std::make_shared<PSI_stage_info>(*old_stage_arg);
|
|
}
|
|
#endif
|
|
|
|
// Unlock Mutex that was successfully locked by Lock() or TryLockUntil()
|
|
void Rdb_mutex::UnLock() {
|
|
#ifndef STANDALONE_UNITTEST
|
|
if (m_old_stage_info.count(current_thd) > 0) {
|
|
const std::shared_ptr<PSI_stage_info> old_stage =
|
|
m_old_stage_info[current_thd];
|
|
m_old_stage_info.erase(current_thd);
|
|
/* The following will call mysql_mutex_unlock */
|
|
THD_EXIT_COND(current_thd, old_stage.get());
|
|
return;
|
|
}
|
|
#endif
|
|
RDB_MUTEX_UNLOCK_CHECK(m_mutex);
|
|
}
|
|
|
|
} // namespace myrocks
|