зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1288308 - Part 1: implement nsNamedPipeService; r=bagder,mayhemer
MozReview-Commit-ID: Q4shjYz2gK
This commit is contained in:
Родитель
f928a5954c
Коммит
901781de67
|
@ -895,6 +895,18 @@
|
|||
{ 0x85, 0x44, 0x5a, 0x8d, 0x1a, 0xb7, 0x95, 0x37 } \
|
||||
}
|
||||
|
||||
#ifdef XP_WIN
|
||||
#define NS_NAMEDPIPESERVICE_CONTRACTID \
|
||||
"@mozilla.org/network/named-pipe-service;1"
|
||||
#define NS_NAMEDPIPESERVICE_CID \
|
||||
{ \
|
||||
0xae298cf9, \
|
||||
0x91f4, \
|
||||
0x4337, \
|
||||
{ 0x95, 0x69, 0x61, 0x88, 0xb9, 0xd0, 0x21, 0x6e } \
|
||||
}
|
||||
#endif
|
||||
|
||||
/******************************************************************************
|
||||
* netwerk/cookie classes
|
||||
*/
|
||||
|
|
|
@ -291,6 +291,15 @@ namespace net {
|
|||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
#ifdef XP_WIN
|
||||
#include "../socket/nsNamedPipeService.h"
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(NamedPipeService, Init)
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
#endif
|
||||
|
||||
#ifdef NECKO_PROTOCOL_res
|
||||
// resource
|
||||
#include "nsResProtocolHandler.h"
|
||||
|
@ -756,6 +765,9 @@ NS_DEFINE_NAMED_CID(NS_BUFFEREDOUTPUTSTREAM_CID);
|
|||
NS_DEFINE_NAMED_CID(NS_MIMEINPUTSTREAM_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_PROTOCOLPROXYSERVICE_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_STREAMCONVERTERSERVICE_CID);
|
||||
#if defined(XP_WIN)
|
||||
NS_DEFINE_NAMED_CID(NS_NAMEDPIPESERVICE_CID);
|
||||
#endif
|
||||
NS_DEFINE_NAMED_CID(NS_DASHBOARD_CID);
|
||||
#ifdef NECKO_PROTOCOL_ftp
|
||||
NS_DEFINE_NAMED_CID(NS_FTPDIRLISTINGCONVERTER_CID);
|
||||
|
@ -902,6 +914,9 @@ static const mozilla::Module::CIDEntry kNeckoCIDs[] = {
|
|||
{ &kNS_MIMEINPUTSTREAM_CID, false, nullptr, nsMIMEInputStreamConstructor },
|
||||
{ &kNS_PROTOCOLPROXYSERVICE_CID, true, nullptr, nsProtocolProxyServiceConstructor },
|
||||
{ &kNS_STREAMCONVERTERSERVICE_CID, false, nullptr, CreateNewStreamConvServiceFactory },
|
||||
#if defined (XP_WIN)
|
||||
{ &kNS_NAMEDPIPESERVICE_CID, false, NULL, mozilla::net::NamedPipeServiceConstructor },
|
||||
#endif
|
||||
{ &kNS_DASHBOARD_CID, false, nullptr, mozilla::net::DashboardConstructor },
|
||||
#ifdef NECKO_PROTOCOL_ftp
|
||||
{ &kNS_FTPDIRLISTINGCONVERTER_CID, false, nullptr, CreateNewFTPDirListingConv },
|
||||
|
@ -1050,6 +1065,9 @@ static const mozilla::Module::ContractIDEntry kNeckoContracts[] = {
|
|||
{ NS_MIMEINPUTSTREAM_CONTRACTID, &kNS_MIMEINPUTSTREAM_CID },
|
||||
{ NS_PROTOCOLPROXYSERVICE_CONTRACTID, &kNS_PROTOCOLPROXYSERVICE_CID },
|
||||
{ NS_STREAMCONVERTERSERVICE_CONTRACTID, &kNS_STREAMCONVERTERSERVICE_CID },
|
||||
#if defined(XP_WIN)
|
||||
{ NS_NAMEDPIPESERVICE_CONTRACTID, &kNS_NAMEDPIPESERVICE_CID },
|
||||
#endif
|
||||
{ NS_DASHBOARD_CONTRACTID, &kNS_DASHBOARD_CID },
|
||||
#ifdef NECKO_PROTOCOL_ftp
|
||||
{ NS_ISTREAMCONVERTER_KEY FTP_TO_INDEX, &kNS_FTPDIRLISTINGCONVERTER_CID },
|
||||
|
|
|
@ -21,4 +21,12 @@ UNIFIED_SOURCES += [
|
|||
'nsUDPSocketProvider.cpp',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
|
||||
XPIDL_SOURCES += [
|
||||
'nsINamedPipeService.idl',
|
||||
]
|
||||
UNIFIED_SOURCES += [
|
||||
'nsNamedPipeService.cpp'
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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 "nsISupports.idl"
|
||||
#include "nsrootidl.idl"
|
||||
|
||||
/**
|
||||
* nsINamedPipeDataObserver
|
||||
*
|
||||
* This is the callback interface for nsINamedPipeService.
|
||||
* The functions are called by the internal thread in the nsINamedPipeService.
|
||||
*/
|
||||
[scriptable, uuid(de4f460b-94fd-442c-9002-1637beb2185a)]
|
||||
interface nsINamedPipeDataObserver : nsISupports
|
||||
{
|
||||
/**
|
||||
* onDataAvailable
|
||||
*
|
||||
* @param aBytesTransferred
|
||||
* Transfered bytes during last transmission.
|
||||
* @param aOverlapped
|
||||
* Corresponding overlapped structure used by the async I/O
|
||||
*/
|
||||
void onDataAvailable(in unsigned long aBytesTransferred,
|
||||
in voidPtr aOverlapped);
|
||||
|
||||
/**
|
||||
* onError
|
||||
*
|
||||
* @param aError
|
||||
* Error code of the error.
|
||||
* @param aOverlapped
|
||||
* Corresponding overlapped structure used by the async I/O
|
||||
*/
|
||||
void onError(in unsigned long aError,
|
||||
in voidPtr aOverlapped);
|
||||
};
|
||||
|
||||
/**
|
||||
* nsINamedPipeService
|
||||
*/
|
||||
[scriptable, uuid(1bf19133-5625-4ac8-836a-80b1c215f72b)]
|
||||
interface nsINamedPipeService : nsISupports
|
||||
{
|
||||
/**
|
||||
* addDataObserver
|
||||
*
|
||||
* @param aHandle
|
||||
* The handle that is going to be monitored for read/write operations.
|
||||
* Only handles that are opened with overlapped IO are supported.
|
||||
* @param aObserver
|
||||
* The observer of the handle, the service strong-refs of the observer.
|
||||
*/
|
||||
void addDataObserver(in voidPtr aHandle,
|
||||
in nsINamedPipeDataObserver aObserver);
|
||||
|
||||
/**
|
||||
* removeDataObserver
|
||||
*
|
||||
* @param aHandle
|
||||
The handle associated to the observer, and will be closed by the
|
||||
service.
|
||||
* @param aObserver
|
||||
* The observer to be removed.
|
||||
*/
|
||||
void removeDataObserver(in voidPtr aHandle,
|
||||
in nsINamedPipeDataObserver aObserver);
|
||||
|
||||
/**
|
||||
* isOnCurrentThread
|
||||
*
|
||||
* @return the caller runs within the internal thread.
|
||||
*/
|
||||
boolean isOnCurrentThread();
|
||||
};
|
|
@ -0,0 +1,324 @@
|
|||
/* -*- 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 "mozilla/Services.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsIThread.h"
|
||||
#include "nsNamedPipeService.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
static mozilla::LazyLogModule gNamedPipeServiceLog("NamedPipeWin");
|
||||
#define LOG_NPS_DEBUG(...) \
|
||||
MOZ_LOG(gNamedPipeServiceLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
|
||||
#define LOG_NPS_ERROR(...) \
|
||||
MOZ_LOG(gNamedPipeServiceLog, mozilla::LogLevel::Error, (__VA_ARGS__))
|
||||
|
||||
NS_IMPL_ISUPPORTS(NamedPipeService,
|
||||
nsINamedPipeService,
|
||||
nsIObserver,
|
||||
nsIRunnable)
|
||||
|
||||
NamedPipeService::NamedPipeService()
|
||||
: mIocp(nullptr)
|
||||
, mIsShutdown(false)
|
||||
, mLock("NamedPipeServiceLock")
|
||||
{
|
||||
}
|
||||
|
||||
nsresult
|
||||
NamedPipeService::Init()
|
||||
{
|
||||
MOZ_ASSERT(!mIsShutdown);
|
||||
|
||||
nsresult rv;
|
||||
|
||||
// nsIObserverService must be accessed in main thread.
|
||||
// register shutdown event to stop NamedPipeSrv thread.
|
||||
nsCOMPtr<nsIObserver> self(this);
|
||||
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self = Move(self)] () -> void {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsCOMPtr<nsIObserverService> svc = mozilla::services::GetObserverService();
|
||||
|
||||
if (NS_WARN_IF(!svc)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(svc->AddObserver(self,
|
||||
NS_XPCOM_SHUTDOWN_OBSERVER_ID,
|
||||
false)))) {
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
if (NS_IsMainThread()) {
|
||||
rv = r->Run();
|
||||
} else {
|
||||
rv = NS_DispatchToMainThread(r);
|
||||
}
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
mIocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 1);
|
||||
if (NS_WARN_IF(!mIocp || mIocp == INVALID_HANDLE_VALUE)) {
|
||||
Shutdown();
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
rv = NS_NewNamedThread("NamedPipeSrv", getter_AddRefs(mThread));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
Shutdown();
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
NamedPipeService::Shutdown()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// remove observer
|
||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
||||
if (obs) {
|
||||
obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
|
||||
}
|
||||
|
||||
// stop thread
|
||||
if (mThread && !mIsShutdown) {
|
||||
mIsShutdown = true;
|
||||
|
||||
// invoke ERROR_ABANDONED_WAIT_0 to |GetQueuedCompletionStatus|
|
||||
CloseHandle(mIocp);
|
||||
mIocp = nullptr;
|
||||
|
||||
mThread->Shutdown();
|
||||
}
|
||||
|
||||
// close I/O Completion Port
|
||||
if (mIocp && mIocp != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(mIocp);
|
||||
mIocp = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
NamedPipeService::RemoveRetiredObjects()
|
||||
{
|
||||
MOZ_ASSERT(NS_GetCurrentThread() == mThread);
|
||||
mLock.AssertCurrentThreadOwns();
|
||||
|
||||
if (!mRetiredHandles.IsEmpty()) {
|
||||
for (auto& handle : mRetiredHandles) {
|
||||
CloseHandle(handle);
|
||||
}
|
||||
mRetiredHandles.Clear();
|
||||
}
|
||||
|
||||
mRetiredObservers.Clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement nsINamedPipeService
|
||||
*/
|
||||
|
||||
NS_IMETHODIMP
|
||||
NamedPipeService::AddDataObserver(void* aHandle,
|
||||
nsINamedPipeDataObserver* aObserver)
|
||||
{
|
||||
if (!aHandle || aHandle == INVALID_HANDLE_VALUE || !aObserver) {
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
|
||||
HANDLE h = CreateIoCompletionPort(aHandle,
|
||||
mIocp,
|
||||
reinterpret_cast<ULONG_PTR>(aObserver),
|
||||
1);
|
||||
if (NS_WARN_IF(!h)) {
|
||||
LOG_NPS_ERROR("CreateIoCompletionPort error (%d)", GetLastError());
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
if (NS_WARN_IF(h != mIocp)) {
|
||||
LOG_NPS_ERROR("CreateIoCompletionPort got unexpected value %p (should be %p)",
|
||||
h,
|
||||
mIocp);
|
||||
CloseHandle(h);
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
MOZ_ASSERT(!mObservers.Contains(aObserver));
|
||||
|
||||
mObservers.AppendElement(aObserver);
|
||||
|
||||
// start event loop
|
||||
if (mObservers.Length() == 1) {
|
||||
rv = mThread->Dispatch(this, NS_DISPATCH_NORMAL);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
LOG_NPS_ERROR("Dispatch to thread failed (%08x)", rv);
|
||||
mObservers.Clear();
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NamedPipeService::RemoveDataObserver(void* aHandle,
|
||||
nsINamedPipeDataObserver* aObserver)
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
mObservers.RemoveElement(aObserver);
|
||||
|
||||
mRetiredHandles.AppendElement(aHandle);
|
||||
mRetiredObservers.AppendElement(aObserver);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NamedPipeService::IsOnCurrentThread(bool* aRetVal)
|
||||
{
|
||||
MOZ_ASSERT(mThread);
|
||||
MOZ_ASSERT(aRetVal);
|
||||
|
||||
if (!mThread) {
|
||||
*aRetVal = false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return mThread->IsOnCurrentThread(aRetVal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement nsIObserver
|
||||
*/
|
||||
|
||||
NS_IMETHODIMP
|
||||
NamedPipeService::Observe(nsISupports* aSubject,
|
||||
const char* aTopic,
|
||||
const char16_t* aData)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID, aTopic)) {
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement nsIRunnable
|
||||
*/
|
||||
|
||||
NS_IMETHODIMP
|
||||
NamedPipeService::Run()
|
||||
{
|
||||
MOZ_ASSERT(NS_GetCurrentThread() == mThread);
|
||||
MOZ_ASSERT(mIocp && mIocp != INVALID_HANDLE_VALUE);
|
||||
|
||||
while (!mIsShutdown) {
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
if (mObservers.IsEmpty()) {
|
||||
LOG_NPS_DEBUG("no observer, stop loop");
|
||||
break;
|
||||
}
|
||||
|
||||
RemoveRetiredObjects();
|
||||
}
|
||||
|
||||
DWORD bytesTransferred = 0;
|
||||
ULONG_PTR key = 0;
|
||||
LPOVERLAPPED overlapped = nullptr;
|
||||
BOOL success = GetQueuedCompletionStatus(mIocp,
|
||||
&bytesTransferred,
|
||||
&key,
|
||||
&overlapped,
|
||||
1000); // timeout, 1s
|
||||
auto err = GetLastError();
|
||||
if (!success) {
|
||||
if (err == WAIT_TIMEOUT) {
|
||||
continue;
|
||||
} else if (err == ERROR_ABANDONED_WAIT_0) { // mIocp was closed
|
||||
break;
|
||||
} else if (!overlapped) {
|
||||
/**
|
||||
* Did not dequeue a completion packet from the completion port, and
|
||||
* bytesTransferred/key are meaningless.
|
||||
* See remarks of |GetQueuedCompletionStatus| API.
|
||||
*/
|
||||
|
||||
LOG_NPS_ERROR("invalid overlapped (%d)", err);
|
||||
continue;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Windows doesn't provide a method to remove created I/O Completion Port,
|
||||
* all we can do is just close the handle we monitored before.
|
||||
* In some cases, there's race condition that the monitored handle has an
|
||||
* I/O status after the observer is being removed and destroyed.
|
||||
* To avoid changing the ref-count of a dangling pointer, don't use nsCOMPtr
|
||||
* here.
|
||||
*/
|
||||
nsINamedPipeDataObserver* target =
|
||||
reinterpret_cast<nsINamedPipeDataObserver*>(key);
|
||||
|
||||
nsCOMPtr<nsINamedPipeDataObserver> obs;
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
|
||||
auto idx = mObservers.IndexOf(target);
|
||||
if (idx == decltype(mObservers)::NoIndex) {
|
||||
LOG_NPS_ERROR("observer %p not found", target);
|
||||
continue;
|
||||
}
|
||||
obs = target;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(obs.get());
|
||||
|
||||
if (success) {
|
||||
LOG_NPS_DEBUG("OnDataAvailable: obs=%p, bytes=%d",
|
||||
obs.get(),
|
||||
bytesTransferred);
|
||||
obs->OnDataAvailable(bytesTransferred, overlapped);
|
||||
} else {
|
||||
LOG_NPS_ERROR("GetQueuedCompletionStatus %p failed, error=%d",
|
||||
obs.get(),
|
||||
err);
|
||||
obs->OnError(err, overlapped);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
RemoveRetiredObjects();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static NS_DEFINE_CID(kNamedPipeServiceCID, NS_NAMEDPIPESERVICE_CID);
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,59 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef mozilla_netwerk_socket_nsNamedPipeService_h
|
||||
#define mozilla_netwerk_socket_nsNamedPipeService_h
|
||||
|
||||
#include <Windows.h>
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "nsINamedPipeService.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsIRunnable.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class NamedPipeService final : public nsINamedPipeService
|
||||
, public nsIObserver
|
||||
, public nsIRunnable
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSINAMEDPIPESERVICE
|
||||
NS_DECL_NSIOBSERVER
|
||||
NS_DECL_NSIRUNNABLE
|
||||
|
||||
explicit NamedPipeService();
|
||||
|
||||
nsresult Init();
|
||||
|
||||
private:
|
||||
virtual ~NamedPipeService() = default;
|
||||
void Shutdown();
|
||||
void RemoveRetiredObjects();
|
||||
|
||||
HANDLE mIocp; // native handle to the I/O completion port.
|
||||
Atomic<bool> mIsShutdown; // set to true to stop the event loop running by mThread.
|
||||
nsCOMPtr<nsIThread> mThread; // worker thread to get I/O events.
|
||||
|
||||
/**
|
||||
* The observers is maintained in |mObservers| to ensure valid life-cycle.
|
||||
* We don't remove the handle and corresponding observer directly, instead
|
||||
* the handle and observer into a "retired" list and close/remove them in
|
||||
* the worker thread to avoid a race condition that might happen between
|
||||
* |CloseHandle()| and |GetQueuedCompletionStatus()|.
|
||||
*/
|
||||
Mutex mLock;
|
||||
nsTArray<nsCOMPtr<nsINamedPipeDataObserver>> mObservers; // protected by mLock
|
||||
nsTArray<nsCOMPtr<nsINamedPipeDataObserver>> mRetiredObservers; // protected by mLock
|
||||
nsTArray<HANDLE> mRetiredHandles; // protected by mLock
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_netwerk_socket_nsNamedPipeService_h
|
Загрузка…
Ссылка в новой задаче