/* * 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 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 WLMContextGuard; template 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 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 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 ListCell* append_to_list(List** list, const KeyType* keydata, NodeType* node = NULL) { bool found = false; ListCell* lcnode = search_list(*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 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 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