Mercurial > hg > release > heapstats-2.1
view agent/src/heapstats-engines/timer.cpp @ 273:7649620bcf09
Bug 3764: Agent thread might not be stopped when it works
Reviewed-by: ykubota
https://github.com/HeapStats/heapstats/pull/151
author | Yasumasa Suenaga <yasuenag@gmail.com> |
---|---|
date | Mon, 11 Nov 2019 17:55:25 +0900 |
parents | b7dba538ad10 |
children |
line wrap: on
line source
/*! * \file timer.cpp * \brief This file is used to take interval snapshot. * Copyright (C) 2011-2019 Nippon Telegraph and Telephone Corporation * * 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; either version 2 * of the License, or (at your option) any later version. * * 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 02110-1301, USA. * */ #include "globals.hpp" #include "timer.hpp" /*! * \brief TTimer constructor. * \param event [in] Callback is used by interval calling. * \param timerName [in] Unique name of timer. */ TTimer::TTimer(TTimerEventFunc event, const char *timerName) : TAgentThread(timerName) { /* Sanity check. */ if (event == NULL) { throw "Event callback is NULL."; } /* Setting param. */ this->timerInterval = 0; this->_eventFunc = event; this->_isInterrupted = false; /* Create semphore. */ if (unlikely(sem_init(&this->timerSem, 0, 0))) { throw "Couldn't create semphore."; } } /*! * \brief TTimer destructor. */ TTimer::~TTimer(void) { /* Destroy semphore. */ sem_destroy(&this->timerSem); } /*! * \brief JThread entry point called by interval or nofity. * \param jvmti [in] JVMTI environment object. * \param jni [in] JNI environment object. * \param data [in] Pointer of TTimer. */ void JNICALL TTimer::entryPoint(jvmtiEnv *jvmti, JNIEnv *jni, void *data) { /* Get self. */ TTimer *controller = (TTimer *)data; /* Create increment time. */ struct timespec incTs = {0}; incTs.tv_sec = controller->timerInterval / 1000; incTs.tv_nsec = (controller->timerInterval % 1000) * 1000; /* Change running state. */ controller->_isRunning = true; /* Loop for agent run. */ while (true) { /* Reset timer interrupt flag. */ controller->_isInterrupted = false; { TMutexLocker locker(&controller->mutex); if (unlikely(controller->_terminateRequest)) { break; } /* Create limit datetime. */ struct timespec limitTs = {0}; struct timeval nowTv = {0}; gettimeofday(&nowTv, NULL); TIMEVAL_TO_TIMESPEC(&nowTv, &limitTs); limitTs.tv_sec += incTs.tv_sec; limitTs.tv_nsec += incTs.tv_nsec; /* Wait for notification, termination or timeout. */ pthread_cond_timedwait(&controller->mutexCond, &controller->mutex, &limitTs); } /* If waiting finished by timeout. */ if (!controller->_isInterrupted) { /* Call event callback. */ (*controller->_eventFunc)(jvmti, jni, Interval); } } /* Change running state */ controller->_isRunning = false; } /*! * \brief JThread entry point called by notify only. * \param jvmti [in] JVMTI environment object. * \param jni [in] JNI environment object. * \param data [in] Pointer of TTimer. */ void JNICALL TTimer::entryPointByCall(jvmtiEnv *jvmti, JNIEnv *jni, void *data) { /* Get self. */ TTimer *controller = (TTimer *)data; /* Change running state. */ controller->_isRunning = true; /* Loop for agent run. */ while (!controller->_terminateRequest) { /* Reset timer interrupt flag. */ controller->_isInterrupted = false; /* Wait notify or terminate. */ sem_wait(&controller->timerSem); /* If waiting finished by timeout. */ if (!controller->_isInterrupted) { /* Call event callback. */ (*controller->_eventFunc)(jvmti, jni, Interval); } } /* Change running state */ controller->_isRunning = false; } /*! * \brief Make and begin Jthread. * \param jvmti [in] JVMTI environment object. * \param env [in] JNI environment object. * \param interval [in] Interval of invoke timer event. */ void TTimer::start(jvmtiEnv *jvmti, JNIEnv *env, jlong interval) { /* Set timer interval. */ this->timerInterval = interval; if (interval == 0) { /* Notify process mode. Using semaphore. */ TAgentThread::start(jvmti, env, TTimer::entryPointByCall, this, JVMTI_THREAD_MAX_PRIORITY); } else { /* Interval process mode. Using pthread monitor. */ TAgentThread::start(jvmti, env, TTimer::entryPoint, this, JVMTI_THREAD_MAX_PRIORITY); } } /*! * \brief Notify reset timing to this thread from other thread. */ void TTimer::notify(void) { if (this->timerInterval == 0) { /* Notify process to waiting thread. */ sem_post(&this->timerSem); } else { /* Set interrupt flag and notify. */ TMutexLocker locker(&this->mutex); this->_isInterrupted = true; pthread_cond_signal(&this->mutexCond); } } /*! * \brief Notify stop to this thread from other thread. */ void TTimer::stop(void) { /* Terminate follow entry callback type. */ if (this->timerInterval == 0) { /* Sanity check. */ if (!this->_isRunning) { logger->printWarnMsg("AgentThread already finished."); return; } /* Send notification and count notify. */ this->_isInterrupted = true; this->_terminateRequest = true; sem_post(&this->timerSem); /* SpinLock for AgentThread termination. */ while (this->_isRunning) { ; /* none. */ } /* Clean termination flag. */ this->_terminateRequest = false; } else { /* Set interrupt flag and notify termination. */ this->_isInterrupted = true; TAgentThread::stop(); } }