netwerk/base/Tickler.cpp
author Andrew Osmond <aosmond@mozilla.com>
Fri, 17 Nov 2017 06:45:28 -0500
changeset 392351 b9a29d94ccac646c9336fa75e084bbc8581501ad
parent 386356 fa53cb4191939e1d66446f5e45175e0079197082
child 410183 751dd87a6fa10d6deddf34698597c17467fd97ed
permissions -rw-r--r--
Bug 1368776 - Part 15. Cache flags passed to ImageResource::GetImageContainerImpl for consistency. r=tnikkel When FLAG_HIGH_QUALITY_SCALING is used, we need to make sure we continue using that flag when we update the container. We should also use it for comparing whether or not an existing image container is equivalent.

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "Tickler.h"

#ifdef MOZ_USE_WIFI_TICKLER
#include "nsComponentManagerUtils.h"
#include "nsINamed.h"
#include "nsIPrefBranch.h"
#include "nsIPrefService.h"
#include "nsServiceManagerUtils.h"
#include "nsThreadUtils.h"
#include "prnetdb.h"

#include "mozilla/jni/Utils.h"
#include "GeneratedJNIWrappers.h"

namespace mozilla {
namespace net {

NS_IMPL_ISUPPORTS(Tickler, nsISupportsWeakReference, Tickler)

Tickler::Tickler()
    : mLock("Tickler::mLock")
    , mActive(false)
    , mCanceled(false)
    , mEnabled(false)
    , mDelay(16)
    , mDuration(TimeDuration::FromMilliseconds(400))
    , mFD(nullptr)
{
  MOZ_ASSERT(NS_IsMainThread());
}

Tickler::~Tickler()
{
  // non main thread uses of the tickler should hold weak
  // references to it if they must hold a reference at all
  MOZ_ASSERT(NS_IsMainThread());

  if (mThread) {
    mThread->AsyncShutdown();
    mThread = nullptr;
  }

  if (mTimer)
    mTimer->Cancel();
  if (mFD)
    PR_Close(mFD);
}

nsresult
Tickler::Init()
{
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(!mTimer);
  MOZ_ASSERT(!mActive);
  MOZ_ASSERT(!mThread);
  MOZ_ASSERT(!mFD);

  if (jni::IsAvailable()) {
      java::GeckoAppShell::EnableNetworkNotifications();
  }

  mFD = PR_OpenUDPSocket(PR_AF_INET);
  if (!mFD)
    return NS_ERROR_FAILURE;

  // make sure new socket has a ttl of 1
  // failure is not fatal.
  PRSocketOptionData opt;
  opt.option = PR_SockOpt_IpTimeToLive;
  opt.value.ip_ttl = 1;
  PR_SetSocketOption(mFD, &opt);

  nsresult rv = NS_NewNamedThread("wifi tickler",
                                  getter_AddRefs(mThread));
  if (NS_FAILED(rv))
    return rv;

  nsCOMPtr<nsITimer> tmpTimer = NS_NewTimer(mThread);
  if (!tmpTimer)
    return NS_ERROR_OUT_OF_MEMORY;

  mTimer.swap(tmpTimer);

  mAddr.inet.family = PR_AF_INET;
  mAddr.inet.port = PR_htons (4886);
  mAddr.inet.ip = 0;

  return NS_OK;
}

void Tickler::Tickle()
{
  MutexAutoLock lock(mLock);
  MOZ_ASSERT(mThread);
  mLastTickle = TimeStamp::Now();
  if (!mActive)
    MaybeStartTickler();
}

void Tickler::PostCheckTickler()
{
  mLock.AssertCurrentThreadOwns();
  mThread->Dispatch(NewRunnableMethod("net::Tickler::CheckTickler",
                                      this, &Tickler::CheckTickler),
                    NS_DISPATCH_NORMAL);
  return;
}

void Tickler::MaybeStartTicklerUnlocked()
{
  MutexAutoLock lock(mLock);
  MaybeStartTickler();
}

void Tickler::MaybeStartTickler()
{
  mLock.AssertCurrentThreadOwns();
  if (!NS_IsMainThread()) {
    NS_DispatchToMainThread(
      NewRunnableMethod("net::Tickler::MaybeStartTicklerUnlocked",
                        this, &Tickler::MaybeStartTicklerUnlocked));
    return;
  }

  if (!mPrefs)
    mPrefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
  if (mPrefs) {
    int32_t val;
    bool boolVal;

    if (NS_SUCCEEDED(mPrefs->GetBoolPref("network.tickle-wifi.enabled", &boolVal)))
      mEnabled = boolVal;

    if (NS_SUCCEEDED(mPrefs->GetIntPref("network.tickle-wifi.duration", &val))) {
      if (val < 1)
        val = 1;
      if (val > 100000)
        val = 100000;
      mDuration = TimeDuration::FromMilliseconds(val);
    }

    if (NS_SUCCEEDED(mPrefs->GetIntPref("network.tickle-wifi.delay", &val))) {
      if (val < 1)
        val = 1;
      if (val > 1000)
        val = 1000;
      mDelay = static_cast<uint32_t>(val);
    }
  }

  PostCheckTickler();
}

void Tickler::CheckTickler()
{
  MutexAutoLock lock(mLock);
  MOZ_ASSERT(mThread == NS_GetCurrentThread());

  bool shouldRun = (!mCanceled) &&
    ((TimeStamp::Now() - mLastTickle) <= mDuration);

  if ((shouldRun && mActive) || (!shouldRun && !mActive))
    return; // no change in state

  if (mActive)
    StopTickler();
  else
    StartTickler();
}

void Tickler::Cancel()
{
  MutexAutoLock lock(mLock);
  MOZ_ASSERT(NS_IsMainThread());
  mCanceled = true;
  if (mThread)
    PostCheckTickler();
}

void Tickler::StopTickler()
{
  mLock.AssertCurrentThreadOwns();
  MOZ_ASSERT(mThread == NS_GetCurrentThread());
  MOZ_ASSERT(mTimer);
  MOZ_ASSERT(mActive);

  mTimer->Cancel();
  mActive = false;
}

class TicklerTimer final : public nsITimerCallback, public nsINamed
{
  NS_DECL_THREADSAFE_ISUPPORTS
  NS_DECL_NSITIMERCALLBACK

  TicklerTimer(Tickler *aTickler)
  {
    mTickler = do_GetWeakReference(aTickler);
  }

  // nsINamed
  NS_IMETHOD GetName(nsACString& aName) override
  {
    aName.AssignLiteral("TicklerTimer");
    return NS_OK;
  }

private:
  ~TicklerTimer() {}

  nsWeakPtr mTickler;
};

void Tickler::StartTickler()
{
  mLock.AssertCurrentThreadOwns();
  MOZ_ASSERT(mThread == NS_GetCurrentThread());
  MOZ_ASSERT(!mActive);
  MOZ_ASSERT(mTimer);

  if (NS_SUCCEEDED(mTimer->InitWithCallback(new TicklerTimer(this),
                                            mEnabled ? mDelay : 1000,
                                            nsITimer::TYPE_REPEATING_SLACK)))
    mActive = true;
}

// argument should be in network byte order
void Tickler::SetIPV4Address(uint32_t address)
{
  mAddr.inet.ip = address;
}

// argument should be in network byte order
void Tickler::SetIPV4Port(uint16_t port)
{
  mAddr.inet.port = port;
}

NS_IMPL_ISUPPORTS(TicklerTimer, nsITimerCallback, nsINamed)

NS_IMETHODIMP TicklerTimer::Notify(nsITimer *timer)
{
  RefPtr<Tickler> tickler = do_QueryReferent(mTickler);
  if (!tickler)
    return NS_ERROR_FAILURE;
  MutexAutoLock lock(tickler->mLock);

  if (!tickler->mFD) {
    tickler->StopTickler();
    return NS_ERROR_FAILURE;
  }

  if (tickler->mCanceled ||
      ((TimeStamp::Now() - tickler->mLastTickle) > tickler->mDuration)) {
    tickler->StopTickler();
    return NS_OK;
  }

  if (!tickler->mEnabled)
    return NS_OK;

  PR_SendTo(tickler->mFD, "", 0, 0, &tickler->mAddr, 0);
  return NS_OK;
}

} // namespace mozilla::net
} // namespace mozilla

#else // not defined MOZ_USE_WIFI_TICKLER

namespace mozilla {
namespace net {
NS_IMPL_ISUPPORTS0(Tickler)
} // namespace net
} // namespace mozilla

#endif // defined MOZ_USE_WIFI_TICKLER