/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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 "BenchmarkStorageParent.h" #include "KeyValueStorage.h" namespace mozilla { /* Moving average window size. */ const int32_t AVG_WINDOW = 20; /* Calculate the moving average for the new value aValue for the current window * aWindow and the already existing average aAverage. When the method returns * aAverage will contain the new average and aWindow will contain the new * window.*/ void BenchmarkStorageParent::MovingAverage(int32_t& aAverage, int32_t& aWindow, const int32_t aValue) { if (aWindow < AVG_WINDOW) { aAverage = (aAverage * aWindow + aValue) / (aWindow + 1); aWindow++; return; } MOZ_ASSERT(aWindow == AVG_WINDOW); aAverage = (aAverage - aAverage / aWindow) + (aValue / aWindow); } /* In order to decrease the number of times the database is accessed when the * moving average is stored or retrieved we use the same value to store _both_ * the window and the average. The range of the average is limited since it is * a percentage (0-100), and the range of the window is limited * (1-20). Thus the number that is stored in the database is in the form * (digits): WWAAA. For example, the value stored when an average(A) of 88 * corresponds to a window(W) 7 is 7088. The average of 100 that corresponds to * a window of 20 is 20100. The following methods are helpers to extract or * construct the stored value according to the above. */ /* Stored value will be in the form WWAAA(19098). We need to extract the window * (19) and the average score (98). The aValue will * be parsed, the aWindow will contain the window (of the moving average) and * the return value will contain the average itself. */ int32_t BenchmarkStorageParent::ParseStoredValue(int32_t aValue, int32_t& aWindow) { MOZ_ASSERT(aValue > 999); MOZ_ASSERT(aValue < 100000); int32_t score = aValue % 1000; aWindow = (aValue / 1000) % 100; return score; } int32_t BenchmarkStorageParent::PrepareStoredValue(int32_t aScore, int32_t aWindow) { MOZ_ASSERT(aScore >= 0); MOZ_ASSERT(aScore <= 100); MOZ_ASSERT(aWindow > 0); MOZ_ASSERT(aWindow < 21); return aWindow * 1000 + aScore; } BenchmarkStorageParent::BenchmarkStorageParent() : mStorage(new KeyValueStorage) {} IPCResult BenchmarkStorageParent::RecvPut(const nsCString& aDbName, const nsCString& aKey, const int32_t& aValue) { // In order to calculate and store the new moving average, we need to get the // stored value and window first, to calculate the new score and window, and // then to store the new aggregated value. mStorage->Get(aDbName, aKey) ->Then( GetCurrentSerialEventTarget(), __func__, [storage = mStorage, aDbName, aKey, aValue](int32_t aResult) { int32_t window = 0; int32_t average = 0; if (aResult >= 0) { // The key found. average = ParseStoredValue(aResult, window); } MovingAverage(average, window, aValue); int32_t newValue = PrepareStoredValue(average, window); // Avoid storing if the values are the same. This is an optimization // to minimize the disk usage. if (aResult != newValue) { storage->Put(aDbName, aKey, newValue); } }, [](nsresult rv) { /*do nothing*/ }); return IPC_OK(); } IPCResult BenchmarkStorageParent::RecvGet(const nsCString& aDbName, const nsCString& aKey, GetResolver&& aResolve) { mStorage->Get(aDbName, aKey) ->Then( GetCurrentSerialEventTarget(), __func__, [aResolve](int32_t aResult) { int32_t window = 0; // not used aResolve(aResult < 0 ? -1 : ParseStoredValue(aResult, window)); }, [aResolve](nsresult rv) { aResolve(-1); }); return IPC_OK(); } IPCResult BenchmarkStorageParent::RecvCheckVersion(const nsCString& aDbName, int32_t aVersion) { mStorage->Get(aDbName, "Version"_ns) ->Then( GetCurrentSerialEventTarget(), __func__, [storage = mStorage, aDbName, aVersion](int32_t aResult) { if (aVersion != aResult) { storage->Clear(aDbName)->Then( GetCurrentSerialEventTarget(), __func__, [storage, aDbName, aVersion](bool) { storage->Put(aDbName, "Version"_ns, aVersion); }, [](nsresult rv) { /*do nothing*/ }); } }, [](nsresult rv) { /*do nothing*/ }); return IPC_OK(); } }; // namespace mozilla