diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /dom/system/gonk/mozstumbler/WriteStumbleOnThread.cpp | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'dom/system/gonk/mozstumbler/WriteStumbleOnThread.cpp')
-rw-r--r-- | dom/system/gonk/mozstumbler/WriteStumbleOnThread.cpp | 321 |
1 files changed, 321 insertions, 0 deletions
diff --git a/dom/system/gonk/mozstumbler/WriteStumbleOnThread.cpp b/dom/system/gonk/mozstumbler/WriteStumbleOnThread.cpp new file mode 100644 index 000000000..e58e771c4 --- /dev/null +++ b/dom/system/gonk/mozstumbler/WriteStumbleOnThread.cpp @@ -0,0 +1,321 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=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 "WriteStumbleOnThread.h" +#include "StumblerLogging.h" +#include "UploadStumbleRunnable.h" +#include "nsDumpUtils.h" +#include "nsGZFileWriter.h" +#include "nsIFileStreams.h" +#include "nsIInputStream.h" +#include "nsPrintfCString.h" + +#define MAXFILESIZE_KB (15 * 1024) +#define ONEDAY_IN_MSEC (24 * 60 * 60 * 1000) +#define MAX_UPLOAD_ATTEMPTS 20 + +mozilla::Atomic<bool> WriteStumbleOnThread::sIsFileWaitingForUpload(false); +mozilla::Atomic<bool> WriteStumbleOnThread::sIsAlreadyRunning(false); +WriteStumbleOnThread::UploadFreqGuard WriteStumbleOnThread::sUploadFreqGuard = {0}; + +#define FILENAME_INPROGRESS NS_LITERAL_CSTRING("stumbles.json.gz") +#define FILENAME_COMPLETED NS_LITERAL_CSTRING("stumbles.done.json.gz") +#define OUTPUT_DIR NS_LITERAL_CSTRING("mozstumbler") + +class DeleteRunnable : public Runnable +{ + public: + DeleteRunnable() {} + + NS_IMETHOD + Run() override + { + nsCOMPtr<nsIFile> tmpFile; + nsresult rv = nsDumpUtils::OpenTempFile(FILENAME_COMPLETED, + getter_AddRefs(tmpFile), + OUTPUT_DIR, + nsDumpUtils::CREATE); + if (NS_SUCCEEDED(rv)) { + tmpFile->Remove(true); + } + // critically, this sets this flag to false so writing can happen again + WriteStumbleOnThread::sIsAlreadyRunning = false; + WriteStumbleOnThread::sIsFileWaitingForUpload = false; + return NS_OK; + } + + private: + ~DeleteRunnable() {} +}; + +bool +WriteStumbleOnThread::IsFileWaitingForUpload() +{ + return sIsFileWaitingForUpload; +} + +void +WriteStumbleOnThread::UploadEnded(bool deleteUploadFile) +{ + if (!deleteUploadFile) { + sIsAlreadyRunning = false; + return; + } + + nsCOMPtr<nsIEventTarget> target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID); + MOZ_ASSERT(target); + nsCOMPtr<nsIRunnable> event = new DeleteRunnable(); + target->Dispatch(event, NS_DISPATCH_NORMAL); +} + +void +WriteStumbleOnThread::WriteJSON(Partition aPart) +{ + MOZ_ASSERT(!NS_IsMainThread()); + + nsCOMPtr<nsIFile> tmpFile; + nsresult rv; + rv = nsDumpUtils::OpenTempFile(FILENAME_INPROGRESS, getter_AddRefs(tmpFile), + OUTPUT_DIR, nsDumpUtils::CREATE); + if (NS_WARN_IF(NS_FAILED(rv))) { + STUMBLER_ERR("Open a file for stumble failed"); + return; + } + + RefPtr<nsGZFileWriter> gzWriter = new nsGZFileWriter(nsGZFileWriter::Append); + rv = gzWriter->Init(tmpFile); + if (NS_WARN_IF(NS_FAILED(rv))) { + STUMBLER_ERR("gzWriter init failed"); + return; + } + + /* + The json format is like below. + {items:[ + {item}, + {item}, + {item} + ]} + */ + + // Need to add "]}" after the last item + if (aPart == Partition::End) { + rv = gzWriter->Write("]}"); + if (NS_WARN_IF(NS_FAILED(rv))) { + STUMBLER_ERR("gzWriter Write failed"); + } + + rv = gzWriter->Finish(); + if (NS_WARN_IF(NS_FAILED(rv))) { + STUMBLER_ERR("ostream finish failed"); + } + + nsCOMPtr<nsIFile> targetFile; + nsresult rv = nsDumpUtils::OpenTempFile(FILENAME_COMPLETED, getter_AddRefs(targetFile), + OUTPUT_DIR, nsDumpUtils::CREATE); + nsAutoString targetFilename; + rv = targetFile->GetLeafName(targetFilename); + if (NS_WARN_IF(NS_FAILED(rv))) { + STUMBLER_ERR("Get Filename failed"); + return; + } + rv = targetFile->Remove(true); + if (NS_WARN_IF(NS_FAILED(rv))) { + STUMBLER_ERR("Remove File failed"); + return; + } + // Rename tmpfile + rv = tmpFile->MoveTo(/* directory */ nullptr, targetFilename); + if (NS_WARN_IF(NS_FAILED(rv))) { + STUMBLER_ERR("Rename File failed"); + return; + } + return; + } + + // Need to add "{items:[" before the first item + if (aPart == Partition::Begining) { + rv = gzWriter->Write("{\"items\":[{"); + if (NS_WARN_IF(NS_FAILED(rv))) { + STUMBLER_ERR("ostream write begining failed"); + } + } else if (aPart == Partition::Middle) { + rv = gzWriter->Write(",{"); + if (NS_WARN_IF(NS_FAILED(rv))) { + STUMBLER_ERR("ostream write middle failed"); + } + } + rv = gzWriter->Write(mDesc.get()); + if (NS_WARN_IF(NS_FAILED(rv))) { + STUMBLER_ERR("ostream write mDesc failed"); + } + // one item is ended with '}' (e.g. {item}) + rv = gzWriter->Write("}"); + if (NS_WARN_IF(NS_FAILED(rv))) { + STUMBLER_ERR("ostream write end failed"); + } + + rv = gzWriter->Finish(); + if (NS_WARN_IF(NS_FAILED(rv))) { + STUMBLER_ERR("ostream finish failed"); + } + + // check if it is the end of this file + int64_t fileSize = 0; + rv = tmpFile->GetFileSize(&fileSize); + if (NS_WARN_IF(NS_FAILED(rv))) { + STUMBLER_ERR("GetFileSize failed"); + return; + } + if (fileSize >= MAXFILESIZE_KB) { + WriteJSON(Partition::End); + return; + } +} + +WriteStumbleOnThread::Partition +WriteStumbleOnThread::GetWritePosition() +{ + MOZ_ASSERT(!NS_IsMainThread()); + + nsCOMPtr<nsIFile> tmpFile; + nsresult rv = nsDumpUtils::OpenTempFile(FILENAME_INPROGRESS, getter_AddRefs(tmpFile), + OUTPUT_DIR, nsDumpUtils::CREATE); + if (NS_WARN_IF(NS_FAILED(rv))) { + STUMBLER_ERR("Open a file for stumble failed"); + return Partition::Unknown; + } + + int64_t fileSize = 0; + rv = tmpFile->GetFileSize(&fileSize); + if (NS_WARN_IF(NS_FAILED(rv))) { + STUMBLER_ERR("GetFileSize failed"); + return Partition::Unknown; + } + + if (fileSize == 0) { + return Partition::Begining; + } else if (fileSize >= MAXFILESIZE_KB) { + return Partition::End; + } else { + return Partition::Middle; + } +} + +NS_IMETHODIMP +WriteStumbleOnThread::Run() +{ + MOZ_ASSERT(!NS_IsMainThread()); + + bool b = sIsAlreadyRunning.exchange(true); + if (b) { + return NS_OK; + } + + UploadFileStatus status = GetUploadFileStatus(); + + if (UploadFileStatus::NoFile != status) { + if (UploadFileStatus::ExistsAndReadyToUpload == status) { + sIsFileWaitingForUpload = true; + Upload(); + return NS_OK; + } + } else { + Partition partition = GetWritePosition(); + if (partition == Partition::Unknown) { + STUMBLER_ERR("GetWritePosition failed, skip once"); + } else { + WriteJSON(partition); + } + } + + sIsFileWaitingForUpload = false; + sIsAlreadyRunning = false; + return NS_OK; +} + + +/* + If the upload file exists, then check if it is one day old. + • if it is a day old -> ExistsAndReadyToUpload + • if it is less than the current day old -> Exists + • otherwise -> NoFile + + The Exists case means that the upload and the stumbling is rate limited + per-day to the size of the one file. + */ +WriteStumbleOnThread::UploadFileStatus +WriteStumbleOnThread::GetUploadFileStatus() +{ + nsCOMPtr<nsIFile> tmpFile; + nsresult rv = nsDumpUtils::OpenTempFile(FILENAME_COMPLETED, getter_AddRefs(tmpFile), + OUTPUT_DIR, nsDumpUtils::CREATE); + int64_t fileSize; + rv = tmpFile->GetFileSize(&fileSize); + if (NS_WARN_IF(NS_FAILED(rv))) { + STUMBLER_ERR("GetFileSize failed"); + return UploadFileStatus::NoFile; + } + if (fileSize <= 0) { + tmpFile->Remove(true); + return UploadFileStatus::NoFile; + } + + PRTime lastModifiedTime; + tmpFile->GetLastModifiedTime(&lastModifiedTime); + if ((PR_Now() / PR_USEC_PER_MSEC) - lastModifiedTime >= ONEDAY_IN_MSEC) { + return UploadFileStatus::ExistsAndReadyToUpload; + } + return UploadFileStatus::Exists; +} + +void +WriteStumbleOnThread::Upload() +{ + MOZ_ASSERT(!NS_IsMainThread()); + + time_t seconds = time(0); + int day = seconds / (60 * 60 * 24); + + if (sUploadFreqGuard.daySinceEpoch < day) { + sUploadFreqGuard.daySinceEpoch = day; + sUploadFreqGuard.attempts = 0; + } + + sUploadFreqGuard.attempts++; + if (sUploadFreqGuard.attempts > MAX_UPLOAD_ATTEMPTS) { + STUMBLER_ERR("Too many upload attempts today"); + sIsAlreadyRunning = false; + return; + } + + nsCOMPtr<nsIFile> tmpFile; + nsresult rv = nsDumpUtils::OpenTempFile(FILENAME_COMPLETED, getter_AddRefs(tmpFile), + OUTPUT_DIR, nsDumpUtils::CREATE); + int64_t fileSize; + rv = tmpFile->GetFileSize(&fileSize); + if (NS_WARN_IF(NS_FAILED(rv))) { + STUMBLER_ERR("GetFileSize failed"); + sIsAlreadyRunning = false; + return; + } + + if (fileSize <= 0) { + sIsAlreadyRunning = false; + return; + } + + // prepare json into nsIInputStream + nsCOMPtr<nsIInputStream> inStream; + rv = NS_NewLocalFileInputStream(getter_AddRefs(inStream), tmpFile); + if (NS_FAILED(rv)) { + sIsAlreadyRunning = false; + return; + } + + RefPtr<nsIRunnable> uploader = new UploadStumbleRunnable(inStream); + NS_DispatchToMainThread(uploader); +} |