/** * @file llheartbeat.cpp * @brief Class encapsulating logic for telling a watchdog that we live. * * $LicenseInfo:firstyear=2008&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #include <errno.h> #include <signal.h> #include "linden_common.h" #include "llapp.h" #include "llheartbeat.h" LLHeartbeat::LLHeartbeat(F32 secs_between_heartbeat, F32 aggressive_heartbeat_panic_secs, F32 aggressive_heartbeat_max_blocking_secs) : mSecsBetweenHeartbeat(secs_between_heartbeat), mAggressiveHeartbeatPanicSecs(aggressive_heartbeat_panic_secs), mAggressiveHeartbeatMaxBlockingSecs(aggressive_heartbeat_max_blocking_secs), mSuppressed(false) { mBeatTimer.reset(); mBeatTimer.setTimerExpirySec(mSecsBetweenHeartbeat); mPanicTimer.reset(); mPanicTimer.setTimerExpirySec(mAggressiveHeartbeatPanicSecs); } LLHeartbeat::~LLHeartbeat() { // do nothing. } void LLHeartbeat::setSuppressed(bool is_suppressed) { mSuppressed = is_suppressed; } // returns 0 on success, -1 on permanent failure, 1 on temporary failure int LLHeartbeat::rawSend() { #if LL_WINDOWS return 0; // Pretend we succeeded. #else if (mSuppressed) return 0; // Pretend we succeeded. int result; #ifndef LL_DARWIN union sigval dummy; result = sigqueue(getppid(), LL_HEARTBEAT_SIGNAL, dummy); #else result = kill(getppid(), LL_HEARTBEAT_SIGNAL); #endif if (result == 0) return 0; // success int err = errno; if (err == EAGAIN) return 1; // failed to queue, try again return -1; // other failure. #endif } int LLHeartbeat::rawSendWithTimeout(F32 timeout_sec) { int result = 0; // Spin tightly until our heartbeat is digested by the watchdog // or we time-out. We don't really want to sleep because our // wake-up time might be undesirably synchronised to a hidden // clock by the system's scheduler. mTimeoutTimer.reset(); mTimeoutTimer.setTimerExpirySec(timeout_sec); do { result = rawSend(); //LL_INFOS() << " HEARTSENDc=" << result << LL_ENDL; } while (result==1 && !mTimeoutTimer.hasExpired()); return result; } bool LLHeartbeat::send(F32 timeout_sec) { bool total_success = false; int result = 1; if (timeout_sec > 0.f) { // force a spin until success or timeout result = rawSendWithTimeout(timeout_sec); } else { if (mBeatTimer.hasExpired()) { // zero-timeout; we don't care too much whether our // heartbeat was digested. result = rawSend(); //LL_INFOS() << " HEARTSENDb=" << result << LL_ENDL; } } if (result == -1) { // big failure. } else if (result == 0) { total_success = true; } else { // need to retry at some point } if (total_success) { mBeatTimer.reset(); mBeatTimer.setTimerExpirySec(mSecsBetweenHeartbeat); // reset the time until we start panicking about lost // heartbeats again. mPanicTimer.reset(); mPanicTimer.setTimerExpirySec(mAggressiveHeartbeatPanicSecs); } else { // leave mBeatTimer as expired so we'll lazily poke the // watchdog again next time through. } if (mPanicTimer.hasExpired()) { // It's been ages since we successfully had a heartbeat // digested by the watchdog. Sit here and spin a while // in the hope that we can force it through. LL_WARNS() << "Unable to deliver heartbeat to launcher for " << mPanicTimer.getElapsedTimeF32() << " seconds. Going to try very hard for up to " << mAggressiveHeartbeatMaxBlockingSecs << " seconds." << LL_ENDL; result = rawSendWithTimeout(mAggressiveHeartbeatMaxBlockingSecs); if (result == 0) { total_success = true; } else { // we couldn't even force it through. That's bad, // but we'll try again in a while. LL_WARNS() << "Could not deliver heartbeat to launcher even after trying very hard for " << mAggressiveHeartbeatMaxBlockingSecs << " seconds." << LL_ENDL; } // in any case, reset the panic timer. mPanicTimer.reset(); mPanicTimer.setTimerExpirySec(mAggressiveHeartbeatPanicSecs); } return total_success; }