/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim:set ts=2 sw=2 sts=2 et cindent: */ /* 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 "mozilla/Logging.h" #include "WindowsMutex.h" namespace mozilla::default_agent { using mozilla::LogLevel; static LazyLogModule gWindowsMutexLog("WindowsMutex"); NS_IMPL_ISUPPORTS(WindowsMutexFactory, nsIWindowsMutexFactory) NS_IMETHODIMP WindowsMutexFactory::CreateMutex(const nsAString& aName, nsIWindowsMutex** aWindowsMutex) { nsAutoHandle mutex; auto name = PromiseFlatString(aName); mutex.own(CreateMutexW(nullptr, FALSE, name.get())); if (mutex.get() == nullptr) { MOZ_LOG(gWindowsMutexLog, LogLevel::Error, ("Couldn't open mutex \"%s\": %#lX", NS_ConvertUTF16toUTF8(name).get(), GetLastError())); return NS_ERROR_NOT_AVAILABLE; } RefPtr nsMutex = new WindowsMutex(name, mutex); nsMutex.forget(aWindowsMutex); return NS_OK; } WindowsMutex::WindowsMutex(const nsString& aName, nsAutoHandle& aMutex) : mName(NS_ConvertUTF16toUTF8(aName)), mLocked(false) { mMutex.steal(aMutex); } WindowsMutex::~WindowsMutex() { Unlock(); // nsAutoHandle will take care of closing the mutex's handle. } NS_IMPL_ISUPPORTS(WindowsMutex, nsIWindowsMutex) NS_IMETHODIMP WindowsMutex::TryLock() { // This object may be used on the main thread, so don't wait if it's // not signaled. DWORD mutexStatus = WaitForSingleObject(mMutex.get(), 0); if (mutexStatus == WAIT_OBJECT_0) { mLocked = true; } else if (mutexStatus == WAIT_TIMEOUT) { MOZ_LOG(gWindowsMutexLog, LogLevel::Warning, ("Timed out waiting for mutex \"%s\"", mName.get())); } else if (mutexStatus == WAIT_ABANDONED) { // This status code means that we are supposed to check our data for // consistency as the last locking process didn't signal intentional // unlocking which might indicate it crashed mid-operation. Current uses of // this `WindowsMutex` don't need to worry about corruption of the locked // object, if needed the `nsIWindowsMutex` interface should be extended. MOZ_LOG(gWindowsMutexLog, LogLevel::Warning, ("Found abandoned mutex \"%s\". Continuing...", mName.get())); mLocked = true; } else { // The only other documented status code is WAIT_FAILED. In the case that // we somehow get some other code, that is also an error. MOZ_LOG(gWindowsMutexLog, LogLevel::Error, ("Failed to wait on mutex: mName: %s, error %#lX", mName.get(), GetLastError())); } return mLocked ? NS_OK : NS_ERROR_NOT_AVAILABLE; } NS_IMETHODIMP WindowsMutex::IsLocked(bool* aLocked) { *aLocked = mLocked; return NS_OK; } NS_IMETHODIMP WindowsMutex::Unlock() { nsresult rv = NS_OK; if (mLocked) { BOOL success = ReleaseMutex(mMutex.get()); if (!success) { MOZ_LOG(gWindowsMutexLog, LogLevel::Error, ("Failed to release mutex \"%s\"", mName.get())); rv = NS_ERROR_UNEXPECTED; } mLocked = false; } return rv; } } // namespace mozilla::default_agent