Files
tubestation/toolkit/components/url-classifier/tests/gtest/Common.cpp
Henry Chang 4543c61a48 Bug 1339050 - Asynchronously apply safebrowsing DB update. r=francois,gcp
A new function Classifier::AsyncApplyUpdates() is implemented for async update.
Besides, all public Classifier interfaces become "worker thread only" and
we remove DBServiceWorker::ApplyUpdatesBackground/Foreground.

In DBServiceWorker::FinishUpdate, instead of calling Classifier::ApplyUpdates,
we call Classifier::AsyncApplyUpdates and install a callback for notifying
the update observer when update is finished. The callback will occur on the
caller thread (i.e. worker thread.)

As for the shutdown issue, when the main thread is notified to shut down,
we at first *synchronously* dispatch an event to the worker thread to
shut down the update thread. After getting synchronized with all other
threads, we send last two events "CancelUpdate" and "CloseDb" to notify
dangling update (i.e. BeginUpdate is called but FinishUpdate isn't)
and do cleanup work.

MozReview-Commit-ID: DXZvA2eFKlc
2017-04-06 07:07:56 +08:00

160 lines
4.4 KiB
C++

#include "Common.h"
#include "HashStore.h"
#include "Classifier.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsTArray.h"
#include "nsIThread.h"
#include "nsThreadUtils.h"
#include "nsUrlClassifierUtils.h"
using namespace mozilla;
using namespace mozilla::safebrowsing;
template<typename Function>
void RunTestInNewThread(Function&& aFunction) {
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(mozilla::Forward<Function>(aFunction));
nsCOMPtr<nsIThread> testingThread;
nsresult rv =
NS_NewNamedThread("Testing Thread", getter_AddRefs(testingThread), r);
ASSERT_EQ(rv, NS_OK);
testingThread->Shutdown();
}
nsresult SyncApplyUpdates(Classifier* aClassifier,
nsTArray<TableUpdate*>* aUpdates)
{
// We need to spin a new thread specifically because the callback
// will be on the caller thread. If we call Classifier::AsyncApplyUpdates
// and wait on the same thread, this function will never return.
nsresult ret;
bool done = false;
auto onUpdateComplete = [&done, &ret](nsresult rv) {
ret = rv;
done = true;
};
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([&]() {
nsresult rv = aClassifier->AsyncApplyUpdates(aUpdates, onUpdateComplete);
if (NS_FAILED(rv)) {
onUpdateComplete(rv);
}
});
nsCOMPtr<nsIThread> testingThread;
NS_NewNamedThread("ApplyUpdates", getter_AddRefs(testingThread));
if (!testingThread) {
return NS_ERROR_FAILURE;
}
testingThread->Dispatch(r, NS_DISPATCH_NORMAL);
while (!done) {
// NS_NewCheckSummedOutputStream in HashStore::WriteFile
// will synchronously init NS_CRYPTO_HASH_CONTRACTID on
// the main thread. As a result we have to keep processing
// pending event until |done| becomes true. If there's no
// more pending event, what we only can do is wait.
// Condition variable doesn't work here because instrusively
// notifying the from NS_NewCheckSummedOutputStream() or
// HashStore::WriteFile() is weird.
if (!NS_ProcessNextEvent(NS_GetCurrentThread(), false)) {
PR_Sleep(PR_MillisecondsToInterval(100));
}
}
return ret;
}
already_AddRefed<nsIFile>
GetFile(const nsTArray<nsString>& path)
{
nsCOMPtr<nsIFile> file;
nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(file));
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
}
for (uint32_t i = 0; i < path.Length(); i++) {
file->Append(path[i]);
}
return file.forget();
}
void ApplyUpdate(nsTArray<TableUpdate*>& updates)
{
nsCOMPtr<nsIFile> file;
NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(file));
UniquePtr<Classifier> classifier(new Classifier());
classifier->Open(*file);
{
// Force nsIUrlClassifierUtils loading on main thread
// because nsIUrlClassifierDBService will not run in advance
// in gtest.
nsresult rv;
nsCOMPtr<nsIUrlClassifierUtils> dummy =
do_GetService(NS_URLCLASSIFIERUTILS_CONTRACTID, &rv);
ASSERT_TRUE(NS_SUCCEEDED(rv));
}
SyncApplyUpdates(classifier.get(), &updates);
}
void ApplyUpdate(TableUpdate* update)
{
nsTArray<TableUpdate*> updates = { update };
ApplyUpdate(updates);
}
void
PrefixArrayToPrefixStringMap(const nsTArray<nsCString>& prefixArray,
PrefixStringMap& out)
{
out.Clear();
for (uint32_t i = 0; i < prefixArray.Length(); i++) {
const nsCString& prefix = prefixArray[i];
nsCString* prefixString = out.LookupOrAdd(prefix.Length());
prefixString->Append(prefix.BeginReading(), prefix.Length());
}
}
nsresult
PrefixArrayToAddPrefixArrayV2(const nsTArray<nsCString>& prefixArray,
AddPrefixArray& out)
{
out.Clear();
for (size_t i = 0; i < prefixArray.Length(); i++) {
// Create prefix hash from string
Prefix hash;
static_assert(sizeof(hash.buf) == PREFIX_SIZE, "Prefix must be 4 bytes length");
memcpy(hash.buf, prefixArray[i].BeginReading(), PREFIX_SIZE);
MOZ_ASSERT(prefixArray[i].Length() == PREFIX_SIZE);
AddPrefix *add = out.AppendElement(fallible);
if (!add) {
return NS_ERROR_OUT_OF_MEMORY;
}
add->addChunk = i;
add->prefix = hash;
}
return NS_OK;
}
nsCString
GeneratePrefix(const nsCString& aFragment, uint8_t aLength)
{
Completion complete;
nsCOMPtr<nsICryptoHash> cryptoHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID);
complete.FromPlaintext(aFragment, cryptoHash);
nsCString hash;
hash.Assign((const char *)complete.buf, aLength);
return hash;
}