Files
openGauss-server/src/include/workload/ctxctl.h
2021-09-23 15:19:37 +08:00

522 lines
16 KiB
C++

/*
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
*
* openGauss is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* ---------------------------------------------------------------------------------------
*
* ctxctl.h
* definitions for context control functions
*
* IDENTIFICATION
* src/include/workload/ctxctl.h
*
* ---------------------------------------------------------------------------------------
*/
#ifndef CTXCTL_H
#define CTXCTL_H
#include "postgres.h"
#include "knl/knl_variable.h"
#include "gstrace/gstrace_infra.h"
#define THREADID (gettid())
#define PROCESSID (getpid())
#define DUP_ARRAY_POINTER(p, n) pobjdup(p, n)
#define DUP_POINTER(p) DUP_ARRAY_POINTER(p, 1)
/* use a mutext lock */
#define USE_CONTEXT_LOCK(mutex) \
WLMContextLock sp_mutex_lock(mutex); \
sp_mutex_lock.Lock()
/* use a lwlock */
#define USE_AUTO_LWLOCK(lockid, lockmode) \
WLMAutoLWLock sp_lwlock(lockid, lockmode); \
sp_lwlock.AutoLWLockAcquire()
/* use a memory context */
#define USE_MEMORY_CONTEXT(context) \
WLMContextGuard spcxt((CleanHandler)MemoryContextSwitchTo, MemoryContextSwitchTo(context))
/* use a smart context guard */
#define USE_CONTEXT_GUARD(handler, object) WLMContextGuard sp_guard((CleanHandler)handler, object)
/* make ptr a smart pointer */
#define MAKE_SMART_POINTER(ptr) USE_CONTEXT_GUARD(pfree, ptr)
#define RELEASE_CONTEXT_LOCK() sp_mutex_lock.UnLock()
#define RELEASE_AUTO_LWLOCK() sp_lwlock.AutoLWLockRelease()
#define REVERT_MEMORY_CONTEXT() spcxt.handle()
#define USE_LOCK_TO_ADD(mutex, src, inc) \
do { \
USE_CONTEXT_LOCK(&mutex); \
src += inc; \
if (src < 0) \
src = 0; \
} while (0);
#define HANDLE_CONTEXT_GUARD() sp_guard.handle()
#define IS_MUTEX_HELD(mutex, pid) ((mutex)->__data.__owner == pid)
#define securec_check_errval(errno, express, elevel) \
do { \
errno_t resno = errno; \
if (EOK != resno) { \
express; \
switch (resno) { \
case EINVAL: \
elog(elevel, \
"%s : %d : The destination buffer is NULL or not terminated. The second case only occures in " \
"function strcat_s/strncat_s.", \
__FILE__, \
__LINE__); \
break; \
case EINVAL_AND_RESET: \
elog(elevel, "%s : %d : The Source Buffer is NULL.", __FILE__, __LINE__); \
break; \
case ERANGE: \
elog(elevel, \
"%s : %d : The parameter destMax is equal to zero or larger than the macro : " \
"SECUREC_STRING_MAX_LEN.", \
__FILE__, \
__LINE__); \
break; \
case ERANGE_AND_RESET: \
elog(elevel, \
"%s : %d : The parameter destMax is too small or parameter count is larger than macro " \
"parameter SECUREC_STRING_MAX_LEN. The second case only occures in functions " \
"strncat_s/strncpy_s.", \
__FILE__, \
__LINE__); \
break; \
case EOVERLAP_AND_RESET: \
elog(elevel, \
"%s : %d : The destination buffer and source buffer are overlapped.", \
__FILE__, \
__LINE__); \
break; \
default: \
elog(elevel, "%s : %d : Unrecognized return type.", __FILE__, __LINE__); \
break; \
} \
} \
} while (0)
#define securec_check_ssval(errno, express, elevel) \
do { \
errno_t resno = errno; \
if (resno == -1) { \
express; \
elog(elevel, \
"%s : %d : The destination buffer or format is a NULL pointer or the invalid parameter handle is " \
"invoked.", \
__FILE__, \
__LINE__); \
} \
} while (0)
typedef void (*CleanHandler)(void*);
extern void* palloc0_noexcept(Size size);
template <typename T>
struct smart_ptr {
smart_ptr() : m_handler(NULL), m_ptr(NULL)
{}
explicit smart_ptr(T* ptr) : m_handler(pfree), m_ptr(ptr)
{}
smart_ptr(CleanHandler myHandler, T* ptr) : m_handler(myHandler), m_ptr(ptr)
{}
~smart_ptr()
{
handle();
}
T* set(Size size)
{
m_handler = pfree;
m_ptr = palloc0(size);
return m_ptr;
}
void set(T* ptr)
{
m_handler = pfree;
m_ptr = ptr;
}
void set(CleanHandler handler, T* ptr)
{
m_handler = handler;
m_ptr = ptr;
}
void reset()
{
m_handler = NULL;
m_ptr = NULL;
}
T* ptr()
{
return m_ptr;
}
T* operator->() const
{
return m_ptr;
}
void handle()
{
if (m_handler)
m_handler(m_ptr);
reset();
}
protected:
smart_ptr(const smart_ptr&);
smart_ptr& operator=(const smart_ptr&);
private:
CleanHandler m_handler;
T* m_ptr;
};
struct WLMContextLock {
WLMContextLock(pthread_mutex_t* mutex)
: m_mutex(mutex), m_isLocked(false), m_exitUnlock(true), m_clean(NULL), m_ptr(NULL), m_tid(THREADID)
{}
WLMContextLock(pthread_mutex_t* mutex, bool thread_exit_unlock)
: m_mutex(mutex),
m_isLocked(false),
m_exitUnlock(thread_exit_unlock),
m_clean(NULL),
m_ptr(NULL),
m_tid(THREADID)
{}
~WLMContextLock()
{
if (t_thrd.proc_cxt.proc_exit_inprogress && !m_exitUnlock) {
m_isLocked = false;
}
UnLock();
}
void Lock(bool isForce = false)
{
if (isForce || !m_isLocked) {
if (!IsOwner())
(void)pthread_mutex_lock(m_mutex);
m_isLocked = true;
}
}
bool TryLock()
{
if (!m_isLocked) {
int ret = pthread_mutex_trylock(m_mutex);
if (ret == 0)
m_isLocked = true;
}
return m_isLocked;
}
void UnLock(bool isSafe = false)
{
if (m_isLocked) {
clean();
if (!isSafe || IsOwner())
(void)pthread_mutex_unlock(m_mutex);
m_isLocked = false;
}
}
bool IsOwner()
{
return IS_MUTEX_HELD(m_mutex, m_tid);
}
void ReleaseLock(pthread_mutex_t* mutex)
{
pthread_mutex_t* old_mutex = m_mutex;
m_mutex = mutex;
if (IsOwner())
(void)pthread_mutex_unlock(m_mutex);
m_mutex = old_mutex;
}
bool replace(pthread_mutex_t* mutex)
{
if (!m_isLocked) {
m_mutex = mutex;
return true;
}
return false;
}
void set(CleanHandler func, void* ptr)
{
m_clean = func;
m_ptr = ptr;
}
/*
* function name: reset
* description : reset params to handle for context lock.
* return value : void
*/
void reset()
{
m_clean = NULL;
m_ptr = NULL;
}
/*
* function name: clean
* description : execute the clean handler for context lock.
* return value : void
*/
void clean()
{
if (m_clean) {
m_clean(m_ptr);
reset();
}
}
/*
* function name: ConditionWait
* description : execute the condition wait
* return value : void
*/
void ConditionWait(pthread_cond_t* condition)
{
(void)pthread_cond_wait(condition, m_mutex);
}
void ConditionTimedWait(pthread_cond_t* condition, int seconds)
{
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += seconds;
(void)pthread_cond_timedwait(condition, m_mutex, &ts);
}
void ConditionWakeUp(pthread_cond_t* condition)
{
(void)pthread_cond_signal(condition);
}
private:
pthread_mutex_t* m_mutex;
bool m_isLocked;
bool m_exitUnlock;
CleanHandler m_clean;
void* m_ptr;
pid_t m_tid;
};
#define WORKLOAD_LOCK_NUM ((int)WorkloadNodeGroupLock + 10)
class WLMAutoLWLock {
public:
WLMAutoLWLock(LWLock *lock, LWLockMode lockMode) : m_lockId(lock), m_lockMode(lockMode), m_isLocked(false)
{}
~WLMAutoLWLock()
{
AutoLWLockRelease();
}
inline void AutoLWLockAcquire()
{
// Guard against recursive lock
if (!m_isLocked) {
LWLockAcquire(m_lockId, m_lockMode);
m_isLocked = true;
}
}
inline void AutoLWLockRelease()
{
if (m_isLocked && !t_thrd.port_cxt.thread_is_exiting) {
/* maybe there is try-catch operation,
* it resets the t_thrd.int_cxt.InterruptHoldoffCount as 0.
* So reset it again to avoid core issue */
if (t_thrd.int_cxt.InterruptHoldoffCount == 0)
HOLD_INTERRUPTS();
LWLockRelease(m_lockId);
m_isLocked = false;
}
}
private:
LWLock *m_lockId;
LWLockMode m_lockMode;
bool m_isLocked;
};
typedef smart_ptr<void> WLMContextGuard;
template <typename T>
T* pobjdup(T* ptr, size_t n)
{
if (ptr == NULL)
return ptr;
T* duptr = (T*)palloc0_noexcept(sizeof(T) * n);
if (duptr == NULL)
return duptr;
errno_t errval = memcpy_s(duptr, n * sizeof(T), ptr, n * sizeof(T));
securec_check_errval(errval, , LOG);
return duptr;
}
template <class NodeType, class KeyType>
NodeType* search_node(const List* list, const KeyType* keydata)
{
foreach_cell(cell, list)
{
NodeType* node = (NodeType*)lfirst(cell);
if (node->equals(keydata))
return node;
}
return NULL;
}
template <typename KeyType, class NodeType>
ListCell* search_list(const List* list, const KeyType* keydata, bool* found)
{
ListCell* curr = NULL;
ListCell* prev = NULL;
NodeType* node = NULL;
if (found != NULL)
*found = false;
foreach (curr, list) {
node = (NodeType*)lfirst(curr);
int comp = node->compare(keydata);
if (comp == 0) {
if (found != NULL)
*found = true;
return curr;
} else if (comp > 0)
return prev;
prev = curr;
}
return prev;
}
template <typename KeyType, class NodeType, bool ToAlloc>
ListCell* append_to_list(List** list, const KeyType* keydata, NodeType* node = NULL)
{
bool found = false;
ListCell* lcnode = search_list<KeyType, NodeType>(*list, keydata, &found);
if (found)
return lcnode;
/* cannot find the node with this priority, we must create a new one. */
if (ToAlloc)
node = (NodeType*)palloc0_noexcept(sizeof(NodeType));
if (node == NULL)
return NULL;
PG_TRY();
{
if (lcnode == NULL) {
*list = lcons(node, *list);
lcnode = list_head(*list);
} else {
lcnode = lappend_cell(*list, lcnode, node);
}
}
PG_CATCH();
{
lcnode = NULL;
if (ToAlloc)
pfree(node);
FlushErrorState();
}
PG_END_TRY();
return lcnode;
}
/*
* @Description: scan hash table, assign all the info "is_dirty" true
* @IN htab: hash table
* @Return: void
* @See also:
*/
template <class DataType>
void AssignHTabInfoDirtyWithLock(HTAB* htab, LWLock *lwlock)
{
DataType* hdata = NULL;
HASH_SEQ_STATUS hash_seq;
USE_AUTO_LWLOCK(lwlock, LW_EXCLUSIVE);
hash_seq_init(&hash_seq, htab);
while ((hdata = (DataType*)hash_seq_search(&hash_seq)) != NULL)
hdata->is_dirty = true;
}
template <class DataType>
void ProcessHTabRecordWithLock(HTAB* htab, LWLock *lwlock, CleanHandler handle)
{
DataType* hdata = NULL;
HASH_SEQ_STATUS hash_seq;
USE_AUTO_LWLOCK(lwlock, LW_EXCLUSIVE);
hash_seq_init(&hash_seq, htab);
while ((hdata = (DataType*)hash_seq_search(&hash_seq)) != NULL)
handle(hdata);
}
#endif