diff --git a/src/brpc/ubshm/timer/timer_mgr.cpp b/src/brpc/ubshm/timer/timer_mgr.cpp index b5e0c9ef3b..926fdb9fe1 100644 --- a/src/brpc/ubshm/timer/timer_mgr.cpp +++ b/src/brpc/ubshm/timer/timer_mgr.cpp @@ -14,455 +14,254 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. - #define _GNU_SOURCE + #include -#include -#include -#include -#include -#include +#include +#include +#include #include -#include +#include +#include +#include +#include +#include "bthread/bthread.h" #include "brpc/ubshm/timer/timer_mgr.h" namespace brpc { namespace ubring { +std::unordered_map > g_timer_ctx_map; +std::mutex g_timer_ctx_mutex; +std::atomic g_total_timer_num; -int32_t g_epoll_fd = -1; -std::atomic g_total_timer_num(0); -TimerFdCtx *g_timer_fd_ctx_map = nullptr; -uint32_t g_max_system_fd = 0; -static pthread_t g_epoll_execute_thread = 0; -static int32_t g_timer_module_initialized = 0; - -#if defined(OS_MACOSX) -static int timerfd_create_macosx(int clockid, int flags); -static int timerfd_settime_macosx(int fd, int flags, - const itimerspec *new_value, - itimerspec *old_value); -#endif - -static RETURN_CODE DeleteTimerInner(uint32_t fd) { - if (g_timer_fd_ctx_map == nullptr) { - return UBRING_OK; - } - - if (pthread_spin_lock(&g_timer_fd_ctx_map[fd].spin_lock) != 0) { - return UBRING_ERR; - } - - if (g_timer_fd_ctx_map[fd].status == TIMER_CONTEXT_NOT_USING) { - pthread_spin_unlock(&g_timer_fd_ctx_map[fd].spin_lock); - return UBRING_OK; - } - - g_timer_fd_ctx_map[fd].status = TIMER_CONTEXT_NOT_USING; - g_timer_fd_ctx_map[fd].cb = nullptr; - g_timer_fd_ctx_map[fd].args = nullptr; - g_timer_fd_ctx_map[fd].periodical = 0; - g_timer_fd_ctx_map[fd].fd = 0; - - pthread_spin_unlock(&g_timer_fd_ctx_map[fd].spin_lock); +static std::atomic g_timer_id_counter(1); -#if defined(OS_LINUX) - epoll_ctl(g_epoll_fd, EPOLL_CTL_DEL, (int)fd, nullptr); -#elif defined(OS_MACOSX) - struct kevent evt; - EV_SET(&evt, fd, EVFILT_TIMER, EV_DELETE, 0, 0, nullptr); - kevent(g_epoll_fd, &evt, 1, nullptr, 0, nullptr); -#endif - - uint64_t exp = 0; - read((int)fd, &exp, sizeof(exp)); - - close((int)fd); - std::atomic_fetch_sub(&g_total_timer_num, 1U); - return UBRING_OK; +static timespec get_current_realtime() { + timespec ts{}; + clock_gettime(CLOCK_REALTIME, &ts); + return ts; } -static RETURN_CODE StartTimeEpoll(void) { -#if defined(OS_LINUX) - g_epoll_fd = epoll_create1(0); -#elif defined(OS_MACOSX) - g_epoll_fd = kqueue(); -#endif - if (UNLIKELY(g_epoll_fd == -1)) { - LOG(ERROR) << "Failed to create epoll/kqueue. errno=" << errno; - return UBRING_ERR; +static timespec add_timespec(const timespec &base, const timespec &offset) { + timespec result{}; + result.tv_sec = base.tv_sec + offset.tv_sec; + result.tv_nsec = base.tv_nsec + offset.tv_nsec; + if (result.tv_nsec >= NS_PER_SEC) { + result.tv_sec += result.tv_nsec / NS_PER_SEC; + result.tv_nsec %= NS_PER_SEC; } - - int ret = pthread_create(&g_epoll_execute_thread, nullptr, TimerEpoll, nullptr); - if (UNLIKELY(ret != 0)) { - LOG(ERROR) << "Failed to create thread err=" << ret; - return UBRING_ERR; - } - return UBRING_OK; + return result; } -static RETURN_CODE TimerSpinLocksInit(void) { - if (g_timer_fd_ctx_map == nullptr) { - LOG(ERROR) << "Timer module is not fully initialized."; - return UBRING_ERR; - } - - for (uint32_t fd = 0; fd < g_max_system_fd; fd++) { - int ret = pthread_spin_init(&g_timer_fd_ctx_map[fd].spin_lock, - PTHREAD_PROCESS_PRIVATE); - if (ret != EOK) { - LOG(ERROR) << "Failed to initialize spin lock for fd=" << fd; - for (uint32_t cleanup_fd = 0; cleanup_fd < fd; cleanup_fd++) { - pthread_spin_destroy(&g_timer_fd_ctx_map[cleanup_fd].spin_lock); - } - return UBRING_ERR; - } +static std::shared_ptr find_context(uint64_t timer_id) { + std::lock_guard lock(g_timer_ctx_mutex); + auto it = g_timer_ctx_map.find(timer_id); + if (it == g_timer_ctx_map.end()) { + return nullptr; } - return UBRING_OK; + return it->second; } -static RETURN_CODE ExecuteCallback(int32_t timer_fd) { - UnifiedCallback((void *)(&g_timer_fd_ctx_map[timer_fd])); - return UBRING_OK; -} - -static RETURN_CODE TimerCtxMapCompletion(void) { - memset(g_timer_fd_ctx_map, 0, sizeof(TimerFdCtx) * g_max_system_fd); - - RETURN_CODE ret = TimerSpinLocksInit(); - if (ret != UBRING_OK) { - LOG(ERROR) << "Failed to init spin locks for timer module."; - return UBRING_ERR; +static void remove_timer_from_map(uint64_t timer_id) { + std::lock_guard lock(g_timer_ctx_mutex); + auto it = g_timer_ctx_map.find(timer_id); + if (it == g_timer_ctx_map.end()) { + return; } - return UBRING_OK; + g_timer_ctx_map.erase(it); + --g_total_timer_num; } -RETURN_CODE TimerInit(void) { - if (g_timer_module_initialized > 0) { - return UBRING_OK; +struct TimerCallbackArgs { + std::shared_ptr ctx; + uint64_t timer_id; +}; + +static void RunTimerCallback(std::shared_ptr ctx, uint64_t timer_id) { + bool should_run_cb = false; + { + BAIDU_SCOPED_LOCK(ctx->mtx); + if (!ctx->stopped&&ctx->cb!=nullptr) { + should_run_cb = true; + } } - g_total_timer_num.store(0); - - struct rlimit rlim; - if (getrlimit(RLIMIT_NOFILE, &rlim) != UBRING_OK) { - LOG(ERROR) << "Failed to get fd"; - return UBRING_ERR; + if (should_run_cb) { + ctx->cb(ctx->args); } - g_max_system_fd = (uint32_t)rlim.rlim_cur; - - if (g_timer_fd_ctx_map == nullptr) { - g_timer_fd_ctx_map = (TimerFdCtx *)malloc(sizeof(TimerFdCtx) * g_max_system_fd); - if (UNLIKELY(!g_timer_fd_ctx_map)) { - LOG(ERROR) << "Fail to malloc space for timer modules. errno=%d", errno; - return UBRING_ERR; - } - RETURN_CODE ret = TimerCtxMapCompletion(); - if (ret != UBRING_OK) { - LOG(ERROR) << "Failed to init main data structure of Time Module. ret=" << ret; - free(g_timer_fd_ctx_map); - g_timer_fd_ctx_map = nullptr; - return UBRING_ERR; + bool need_remove = true; + { + BAIDU_SCOPED_LOCK(ctx->mtx); + --ctx->running; + ctx->worker_tid = 0; + ctx->cv.notify_all(); + if (ctx->periodical && !ctx->stopped && !ctx->no_reschedule) { + timespec abstime = add_timespec(get_current_realtime(), ctx->interval); + if (bthread_timer_add(&ctx->timer_id, abstime, TimerCallbackWrapper, + reinterpret_cast(timer_id)) == 0) { + need_remove = false; + } } } - RETURN_CODE ret = StartTimeEpoll(); - if (ret != UBRING_OK) { - LOG(ERROR) << "Failed to start Timer Epoll. ret=" << ret; - if (LIKELY(g_timer_fd_ctx_map != nullptr)) { - FREE_PTR(g_timer_fd_ctx_map); - } - return UBRING_ERR; + if (need_remove) { + remove_timer_from_map(timer_id); } - g_timer_module_initialized = 1; - return UBRING_OK; } -void *UnifiedCallback(void *args) { - TimerFdCtx *ctx = (TimerFdCtx *)args; - if (pthread_spin_lock(&ctx->spin_lock) != 0) { - return nullptr; - } - - if (ctx->status == TIMER_CONTEXT_NOT_USING) { - pthread_spin_unlock(&ctx->spin_lock); - return nullptr; - } - - void *(*cb)(void *) = ctx->cb; - void *cb_args = ctx->args; - uint32_t fd = ctx->fd; - int is_periodical = ctx->periodical; - ctx->status = TIMER_CONTEXT_CALLBACK_ONGOING; - - pthread_spin_unlock(&ctx->spin_lock); - - cb(cb_args); - - if (!is_periodical) { - DeleteTimerInner(fd); - } +static void *TimerCallbackWorker(void *arg) { + std::unique_ptr holder(static_cast(arg)); + RunTimerCallback(holder->ctx, holder->timer_id); return nullptr; } -void *TimerEpoll(void *args) { - UNREFERENCE_PARAM(args); -#if defined(OS_LINUX) - struct epoll_event ready_events[MAX_TIMER]; -#elif defined(OS_MACOSX) - struct kevent ready_events[MAX_TIMER]; -#endif - - while (1) { - if (g_timer_module_initialized <= 0) { - LOG(ERROR) << "The Timer module is not initialized."; - break; - } - -#if defined(OS_LINUX) - int32_t ready_num = epoll_wait(g_epoll_fd, ready_events, MAX_TIMER, - TIMER_EPOLL_WAIT_TIMEOUT); -#elif defined(OS_MACOSX) - struct timespec timeout = {0, TIMER_EPOLL_WAIT_TIMEOUT * 1000000}; - int32_t ready_num = kevent(g_epoll_fd, nullptr, 0, ready_events, MAX_TIMER, &timeout); -#endif - - if (UNLIKELY(ready_num == -1)) { - errno_t err = errno; - if (err == EINTR) { - LOG_EVERY_SECOND(WARNING) << "Epoll/Kqueue wait was interrupted. errno=" << err; - continue; - } else if (err == EBADF) { - LOG(WARNING) << "The Timer module is destroyed."; - break; - } - LOG(ERROR) << "Epoll/Kqueue wait internal error. errno=" << err; - break; - } - - for (int32_t i = 0; i < ready_num; i++) { -#if defined(OS_LINUX) - struct epoll_event *event = &ready_events[i]; - int32_t timer_fd = event->data.fd; -#elif defined(OS_MACOSX) - struct kevent *event = &ready_events[i]; - int32_t timer_fd = event->ident; -#endif - - uint64_t exp = 0; - if (read(timer_fd, &exp, sizeof(exp)) < 0) { - if (errno != EBADF) { - LOG(ERROR) << "Failed to read timerfd=" << timer_fd << " errno=" << errno; - } - continue; - } - if (TimerFdCtxValidate((uint32_t)timer_fd) != UBRING_OK) { - continue; - } +int TimerInit() { + return 0; +} - RETURN_CODE ret = ExecuteCallback(timer_fd); - if (ret != UBRING_OK) { - LOG(ERROR) << "Failed execute callback ret=" << ret; - DeleteTimerInner((uint32_t)timer_fd); - continue; - } +void TimerModuleDestroy() { + std::vector > contexts; + { + std::lock_guard lock(g_timer_ctx_mutex); + contexts.reserve(g_timer_ctx_map.size()); + for (auto &pair: g_timer_ctx_map) { + contexts.push_back(pair.second); } } - return nullptr; -} -void DeleteTimerSafe(uint32_t fd) { - if (g_timer_fd_ctx_map == nullptr) { - return; + for (auto &ctx: contexts) { + BAIDU_SCOPED_LOCK(ctx->mtx); + ctx->stopped = true; + bthread_timer_del(ctx->timer_id); } - if (pthread_spin_lock(&g_timer_fd_ctx_map[fd].spin_lock) != 0) { - return; + { + std::lock_guard lock(g_timer_ctx_mutex); + g_timer_ctx_map.clear(); + g_total_timer_num.store(0); } +} - if (g_timer_fd_ctx_map[fd].status == TIMER_CONTEXT_NOT_USING) { - pthread_spin_unlock(&g_timer_fd_ctx_map[fd].spin_lock); - return; +int32_t TimerStart(const itimerspec *time, TimerCallback cb, void *args) { + if (cb == nullptr) { + LOG(ERROR) << "Timer callback is nullptr"; + return -1; } - g_timer_fd_ctx_map[fd].status = TIMER_CONTEXT_NOT_USING; - g_timer_fd_ctx_map[fd].cb = nullptr; - g_timer_fd_ctx_map[fd].args = nullptr; - g_timer_fd_ctx_map[fd].periodical = 0; - g_timer_fd_ctx_map[fd].fd = 0; + auto ctx = std::make_shared(); + ctx->cb = cb; + ctx->args = args; + ctx->periodical = (time->it_interval.tv_sec > 0 || time->it_interval.tv_nsec > 0); + ctx->interval = time->it_interval; - pthread_spin_unlock(&g_timer_fd_ctx_map[fd].spin_lock); + uint64_t timer_id = g_timer_id_counter.fetch_add(1); -#if defined(OS_LINUX) - epoll_ctl(g_epoll_fd, EPOLL_CTL_DEL, (int)fd, nullptr); -#elif defined(OS_MACOSX) - struct kevent evt; - EV_SET(&evt, fd, EVFILT_TIMER, EV_DELETE, 0, 0, nullptr); - kevent(g_epoll_fd, &evt, 1, nullptr, 0, nullptr); -#endif + timespec abstime = add_timespec(get_current_realtime(), time->it_value); - uint64_t exp = 0; - read((int)fd, &exp, sizeof(exp)); + { + std::lock_guard lock(g_timer_ctx_mutex); + g_timer_ctx_map[timer_id] = ctx; + ++g_total_timer_num; + int ret = bthread_timer_add(&ctx->timer_id, abstime, TimerCallbackWrapper, reinterpret_cast(timer_id)); + if (ret != 0) { + LOG(ERROR) << "Failed to add bthread timer, ret=" << ret; + g_timer_ctx_map.erase(timer_id); + --g_total_timer_num; + return -1; + } + } - close((int)fd); - std::atomic_fetch_sub(&g_total_timer_num, 1U); + return static_cast(timer_id); } -void DeleteTimer(uint32_t fd) { - if (g_timer_fd_ctx_map == nullptr) { - LOG(WARNING) << "The timer is not initialized."; - return; - } - - g_timer_fd_ctx_map[fd].periodical = 0; +uint32_t GetActiveTimerNum() { + return g_total_timer_num.load(); } -int32_t TimerStart(const itimerspec *time, void *(*cb)(void *), void *args) { - if (g_epoll_fd == -1) { - LOG(ERROR) << "Timer epoll/kqueue encountered internal error."; - return -1; +void StopTimer(uint64_t timer_id) { + std::shared_ptr ctx; + { + std::lock_guard lock(g_timer_ctx_mutex); + auto it = g_timer_ctx_map.find(timer_id); + if (it == g_timer_ctx_map.end()) { + return; + } + ctx = it->second; } -#if defined(OS_LINUX) - int timer_fd = timerfd_create(CLOCK_MONOTONIC, 0); -#elif defined(OS_MACOSX) - int timer_fd = timerfd_create_macosx(CLOCK_MONOTONIC, 0); -#endif + BAIDU_SCOPED_LOCK(ctx->mtx); + ctx->stopped = true; + ctx->no_reschedule = true; + bthread_timer_del(ctx->timer_id); +} - if (UNLIKELY(timer_fd >= (int)g_max_system_fd || timer_fd == -1)) { - LOG(ERROR) << "Failed to create timerfd=" << timer_fd << " errno=" << errno; - return -1; +void DeleteTimerSafe(uint64_t timer_id) { + std::shared_ptr ctx; + { + std::lock_guard lock(g_timer_ctx_mutex); + auto it = g_timer_ctx_map.find(timer_id); + if (it == g_timer_ctx_map.end()) { + LOG(WARNING) << "Timer id=" << timer_id << " not found"; + return; + } + ctx = it->second; } - g_timer_fd_ctx_map[timer_fd].status = TIMER_CONTEXT_EPOLL_WAITING; - g_timer_fd_ctx_map[timer_fd].cb = cb; - g_timer_fd_ctx_map[timer_fd].args = args; - g_timer_fd_ctx_map[timer_fd].fd = (uint32_t)timer_fd; + std::unique_lock lock(ctx->mtx); + ctx->stopped = true; + ctx->no_reschedule = true; + bthread_timer_del(ctx->timer_id); - if (LIKELY(time->it_interval.tv_sec > 0 || time->it_interval.tv_nsec > 0)) { - g_timer_fd_ctx_map[timer_fd].periodical = 1; + while (ctx->running > 0) { + ctx->cv.wait(lock); } -#if defined(OS_LINUX) - struct epoll_event event = { - .events = EPOLLIN, - .data = {.fd = timer_fd} - }; - - int32_t ret = epoll_ctl(g_epoll_fd, EPOLL_CTL_ADD, timer_fd, &event); -#elif defined(OS_MACOSX) - struct kevent event; - uint64_t timeout_nsec = time->it_value.tv_sec * 1000000000ULL + time->it_value.tv_nsec; - uint64_t interval_nsec = time->it_interval.tv_sec * 1000000000ULL + time->it_interval.tv_nsec; - EV_SET(&event, timer_fd, EVFILT_TIMER, EV_ADD | EV_ENABLE, 0, - timeout_nsec / 1000000, nullptr); - int32_t ret = kevent(g_epoll_fd, &event, 1, nullptr, 0, nullptr); -#endif - - if (UNLIKELY(ret != 0)) { - CloseTimerFd(timer_fd); - LOG(ERROR) << "Failed to add event to epoll/kqueue. errno=" << errno; - return -1; - } + lock.unlock(); + remove_timer_from_map(timer_id); +} - std::atomic_fetch_add(&g_total_timer_num, 1U); - -#if defined(OS_LINUX) - ret = timerfd_settime(timer_fd, 0, time, nullptr); -#elif defined(OS_MACOSX) - ret = timerfd_settime_macosx(timer_fd, 0, time, nullptr); -#endif - - if (UNLIKELY(ret != 0)) { -#if defined(OS_LINUX) - if (epoll_ctl(g_epoll_fd, EPOLL_CTL_DEL, timer_fd, nullptr) != 0) { -#elif defined(OS_MACOSX) - struct kevent evt; - EV_SET(&evt, timer_fd, EVFILT_TIMER, EV_DELETE, 0, 0, nullptr); - if (kevent(g_epoll_fd, &evt, 1, nullptr, 0, nullptr) != 0) { -#endif - LOG(ERROR) << "Failed to delete the timer fd=" << timer_fd << " with errno=" << errno; +void DeleteTimer(uint64_t timer_id) { + std::shared_ptr ctx; + { + std::lock_guard lock(g_timer_ctx_mutex); + auto it = g_timer_ctx_map.find(timer_id); + if (it == g_timer_ctx_map.end()) { + LOG(WARNING) << "Timer id=" << timer_id << " not found"; + return; } - CloseTimerFd(timer_fd); - std::atomic_fetch_sub(&g_total_timer_num, 1U); - LOG(ERROR) << "Failed to set timer"; - return -1; + ctx = it->second; } - - return timer_fd; + BAIDU_SCOPED_LOCK(ctx->mtx); + ctx->no_reschedule = true; } -uint32_t GetActiveTimerNum(void) { - return std::atomic_load(&g_total_timer_num); -} - -void CloseTimerFd(int fd) { - g_timer_fd_ctx_map[fd].cb = nullptr; - g_timer_fd_ctx_map[fd].args = nullptr; - g_timer_fd_ctx_map[fd].status = TIMER_CONTEXT_NOT_USING; - g_timer_fd_ctx_map[fd].fd = 0; - g_timer_fd_ctx_map[fd].periodical = 0; - if (close((int)fd) != 0) { - LOG(ERROR) << "Failed to close timer fd=" << fd << " errno=" << errno; +void TimerCallbackWrapper(void *arg) { + auto timer_id = reinterpret_cast(arg); + auto ctx = find_context(timer_id); + if (ctx == nullptr) { + LOG(ERROR) << "timer_id is not found, timer_id=" << timer_id; return; } -} -void TimerModuleDestroy(void) { - uint32_t max_fd = g_max_system_fd; - if (g_timer_fd_ctx_map) { - for (uint32_t fd = 0; fd < max_fd; fd++) { - if (g_timer_fd_ctx_map[fd].status != TIMER_CONTEXT_NOT_USING) { - DeleteTimerSafe(fd); - } + { + BAIDU_SCOPED_LOCK(ctx->mtx); + if (ctx->stopped) { + return; } - } - close(g_epoll_fd); - g_epoll_fd = -1; - g_total_timer_num = 0; - g_timer_module_initialized = 0; - int32_t ret = pthread_join(g_epoll_execute_thread, nullptr); - if (ret != EOK) { - LOG(ERROR) << "Failed to join pthread, during destroying timer module. ret=" << ret; - return; - } -} - -RETURN_CODE TimerFdCtxValidate(uint32_t fd) { - if (fd >= g_max_system_fd) { - LOG(ERROR) << "TimerFd=" << fd << " is out of range=" << g_max_system_fd; - return UBRING_ERR; - } - if (g_timer_fd_ctx_map[fd].status == TIMER_CONTEXT_NOT_USING) { - LOG(ERROR) << "TimerFd=" << fd << " has wrong status=" << g_timer_fd_ctx_map[fd].status; - return UBRING_ERR; - } - if (g_timer_fd_ctx_map[fd].cb == nullptr) { - LOG(ERROR) << "The callback is not set."; - return UBRING_ERR; + ++ctx->running; } - return UBRING_OK; -} - -#if defined(OS_MACOSX) -static int timerfd_create_macosx(int clockid, int flags) { - int pipefd[2]; - if (pipe(pipefd) == -1) { - return -1; + auto *holder = new (std::nothrow) TimerCallbackArgs{ctx, timer_id}; + if (holder == nullptr) { + RunTimerCallback(ctx, timer_id); + return; } - return pipefd[0]; -} - -static int timerfd_settime_macosx(int fd, int flags, - const itimerspec *new_value, - itimerspec *old_value) { - if (old_value != nullptr) { - memset(old_value, 0, sizeof(itimerspec)); + bthread_t tid; + if (bthread_start_background(&tid, nullptr, TimerCallbackWorker, holder) != 0) { + delete holder; + RunTimerCallback(ctx, timer_id); } - return 0; } -#endif - -} // namespace ubring -} // namespace brpc \ No newline at end of file +} // namespace ubring +} // namespace brpc diff --git a/src/brpc/ubshm/timer/timer_mgr.h b/src/brpc/ubshm/timer/timer_mgr.h index 1b42caef04..a1314e7511 100644 --- a/src/brpc/ubshm/timer/timer_mgr.h +++ b/src/brpc/ubshm/timer/timer_mgr.h @@ -18,21 +18,16 @@ #ifndef BRPC_TIMER_MGR_H #define BRPC_TIMER_MGR_H #include -#include +#include +#include +#include +#include +#include +#include "bthread/types.h" +#include "bthread/unstable.h" +#include "bthread/bthread.h" #include "brpc/ubshm/common/common.h" -#if defined(OS_LINUX) -#include -#include -#elif defined(OS_MACOSX) -#include -#include -#include -#endif - -#define MAX_TIMER 1024 -#define TIMER_EPOLL_WAIT_TIMEOUT 1000 - #if defined(OS_MACOSX) struct itimerspec { @@ -40,34 +35,47 @@ struct itimerspec struct timespec it_value; }; #endif + namespace brpc { namespace ubring { -typedef enum { - TIMER_CONTEXT_NOT_USING, - TIMER_CONTEXT_EPOLL_WAITING, - TIMER_CONTEXT_CALLBACK_ONGOING -} TimerFdCtxStatus; -typedef struct { - void *(*cb)(void*); +constexpr long long NS_PER_SEC = 1000000000LL; + +typedef void * (*TimerCallback)(void *); + +struct TimerContext { + TimerContext() + : cb(nullptr), args(nullptr), periodical(false), interval(), timer_id(0), + stopped(false), no_reschedule(false), running(0), worker_tid(0) {} + + TimerCallback cb; void *args; - uint32_t fd; - TimerFdCtxStatus status; - uint32_t periodical; - pthread_spinlock_t spin_lock; -} TimerFdCtx; + bool periodical; + timespec interval; + bthread_timer_t timer_id; + bthread::Mutex mtx; + bthread::ConditionVariable cv; + bool stopped; + bool no_reschedule; + int running; + bthread_t worker_tid; +}; + +extern std::unordered_map> g_timer_ctx_map; +extern std::mutex g_timer_ctx_mutex; +extern std::atomic g_total_timer_num; -RETURN_CODE TimerInit(void); + +int TimerInit(void); void TimerModuleDestroy(void); -void *UnifiedCallback(void *args); -void *TimerEpoll(void *args); -int32_t TimerStart(const itimerspec *time, void *(*cb)(void *), void *args); +int32_t TimerStart(const itimerspec *time, TimerCallback cb, void *args); uint32_t GetActiveTimerNum(void); -void CloseTimerFd(int fd); -void DeleteTimerSafe(uint32_t fd); -void DeleteTimer(uint32_t fd); -RETURN_CODE TimerFdCtxValidate(uint32_t fd); +void StopTimer(uint64_t timer_id); +void DeleteTimerSafe(uint64_t timer_id); +void DeleteTimer(uint64_t timer_id); + +void TimerCallbackWrapper(void *arg); } } #endif //BRPC_TIMER_MGR_H \ No newline at end of file diff --git a/src/brpc/ubshm/ub_endpoint.cpp b/src/brpc/ubshm/ub_endpoint.cpp index 31539fda85..f6e0ef7ba0 100644 --- a/src/brpc/ubshm/ub_endpoint.cpp +++ b/src/brpc/ubshm/ub_endpoint.cpp @@ -848,6 +848,7 @@ int UBShmEndpoint::PollingModeInitialize(bthread_tag_t tag, while (running->load(std::memory_order_relaxed)) { while (poller->op_queue.Dequeue(op)) { if (op.type == PollerSidOp::ADD) { + poller_sids.erase(op); poller_sids.emplace(op); } else if (op.type == PollerSidOp::REMOVE) { poller_sids.erase(op); diff --git a/src/brpc/ubshm/ub_ring.cpp b/src/brpc/ubshm/ub_ring.cpp index cdc346edf4..bbeb8889ed 100644 --- a/src/brpc/ubshm/ub_ring.cpp +++ b/src/brpc/ubshm/ub_ring.cpp @@ -19,8 +19,11 @@ #include #include #include +#include #include "bthread/bthread.h" #include "butil/logging.h" +#include "butil/atomicops.h" +#include "butil/synchronization/lock.h" #include "brpc/ubshm/ub_ring.h" #include "brpc/ubshm/ub_ring_manager.h" #include "brpc/ubshm/shm/shm_ipc.h" @@ -218,40 +221,90 @@ RETURN_CODE UBRing::UbrAddHBTimer() { return UBRING_OK; } +static std::vector g_cleanup_queue; +static butil::Mutex g_cleanup_queue_mutex; +static butil::atomic g_cleanup_thread_running; +static bthread_t g_cleanup_thread_tid; +constexpr uint64_t SLEEP_US = 1000000; + +static void* CleanupOwnerThread(void*) { + while (g_cleanup_thread_running.load(butil::memory_order_acquire) == 1) { + std::vector queue_to_process; + { + BAIDU_SCOPED_LOCK(g_cleanup_queue_mutex); + if (g_cleanup_queue.empty()) { + bthread_usleep(SLEEP_US); + continue; + } + queue_to_process.swap(g_cleanup_queue); + } + + for (UbrTrx *trx : queue_to_process) { + if (trx == nullptr) { + continue; + } + + DeleteTimerSafe(trx->timer_fd); + DeleteTimerSafe(trx->hb_timer_fd); + + bthread_usleep(SLEEP_US); + + if (UNLIKELY(UBRing::UbrTrxFreeShm(trx) != UBRING_OK)) { + LOG(ERROR) << "Cleanup owner: free shm failed for " << trx->local_shm.name; + } + + if (UNLIKELY(UBRingManager::ReleaseUbrTrxFromMgr(trx) != UBRING_OK)) { + LOG(ERROR) << "Cleanup owner: release rtx failed for " << trx->local_shm.name; + } + } + } + return nullptr; +} + +static void InitCleanupOwner() { + if (g_cleanup_thread_running.load(butil::memory_order_acquire) != 0) { + return; + } + g_cleanup_thread_running.store(1, butil::memory_order_release); + bthread_t tid; + if (bthread_start_background(&tid,nullptr,CleanupOwnerThread,nullptr) != 0) { + LOG(ERROR) << "Cleanup owner: start background failed."; + g_cleanup_thread_running.store(0, butil::memory_order_release); + }else { + g_cleanup_thread_tid = tid; + } +} + +static void RequestTrxCleanup(UbrTrx *trx) { + int expected = 0; + if (!ATOMIC_COMPARE_EXCHANGE_STRONG(trx->cleanup_pending,expected,1)) { + LOG(ERROR) << "Cleanup already pending for trx, name=" << trx->local_shm.name; + return; + } + + InitCleanupOwner(); + { + BAIDU_SCOPED_LOCK(g_cleanup_queue_mutex); + g_cleanup_queue.push_back(trx); + } +} + RETURN_CODE UBRing::UbrPassiveClearTrx(UbrTrx *trx, int fd, PASSIVE_DISC_TYPE type) { RETURN_CODE passive_close_check_rc = UbrTrxCloseCheck(trx); if (UNLIKELY(passive_close_check_rc != UBRING_OK)) { if (passive_close_check_rc == UBRING_REENTRY) { LOG(INFO) << "Passive close skipped, active close in progress, name=" << trx->local_shm.name; - uint64_t start_time = GetCurNanoSeconds(); - return ClearTrxResource(trx, start_time, UBR_CALL_BACK_CLOSE); + return UBRING_OK; } return UBRING_ERR; } trx->ubr_tx.trx_state = UBR_STATE_CLOSED; trx->ubr_rx.trx_state = UBR_STATE_CLOSED; - DeleteTimerSafe((uint32_t)trx->timer_fd); - const char *type_name = nullptr; - if (type == UBR_HEARTBEAT) { - DeleteTimer((uint32_t)trx->hb_timer_fd); - type_name = "Trx heartbeat"; - } else if (type == UBR_UB_EVENT) { - DeleteTimerSafe((uint32_t)trx->hb_timer_fd); - type_name = "Ub event callback"; - } - constexpr int64_t kMicrosecondsPerSecond = 1000000LL; - bthread_usleep(FLAGS_ub_flying_io_timeout_s * kMicrosecondsPerSecond); - - int rc = ShmLocalFree(&trx->remote_shm); - if (rc != UBRING_OK) { - LOG(ERROR) << type_name << ", delete remote shm failed. ret=" << rc; - } - rc = ShmLocalFree(&trx->local_shm); - if (rc != UBRING_OK) { - LOG(ERROR) << type_name << ", delete local shm failed. ret=" << rc; - } - UBRingManager::ReleaseUbrTrxFromMgr(trx); + StopTimer(trx->timer_fd); + StopTimer(trx->hb_timer_fd); + + RequestTrxCleanup(trx); return UBRING_OK; } @@ -1006,12 +1059,8 @@ RETURN_CODE UBRing::UbrClearResourceCheck(UbrTrx *trx, uint64_t start_time, UbrC local_tx_event_q->flag = UBR_STATE_CLOSING; } - if (close_type == UBR_SEND_CLOSE) { - DeleteTimerSafe((uint32_t)trx->timer_fd); - } else { - DeleteTimer((uint32_t)trx->timer_fd); - } - DeleteTimerSafe((uint32_t)trx->hb_timer_fd); + StopTimer(trx->timer_fd); + StopTimer(trx->hb_timer_fd); if (local_tx_event_q->flag == UBR_STATE_CLOSING) { local_tx_event_q->flag = UBR_STATE_CLOSED; @@ -1028,12 +1077,7 @@ RETURN_CODE UBRing::ClearTrxResource(UbrTrx *trx, uint64_t start_time, UbrCloseT return rc; } - rc = UbrAddAsynClearTimer(trx); - if (rc != UBRING_OK) { - LOG(ERROR) << "Trx close, add " << trx->local_shm.name << " close clear timer failed."; - return UBRING_ERR; - } - + RequestTrxCleanup(trx); return UBRING_OK; } diff --git a/src/brpc/ubshm/ubr_trx.h b/src/brpc/ubshm/ubr_trx.h index b4e89645ff..9ff8c4c66b 100644 --- a/src/brpc/ubshm/ubr_trx.h +++ b/src/brpc/ubshm/ubr_trx.h @@ -140,6 +140,7 @@ typedef struct TagUbrTrx { int clear_timer_fd; AtomicInt close_cnt; AtomicInt close_state; + AtomicInt cleanup_pending; } UbrTrx; typedef struct TagFileLock {