Backed out changeset 2450366cf7ca (bug 1891629) for causing win msix mochitest failures
[gecko.git] / netwerk / socket / nsNamedPipeService.cpp
bloba713a34c1d1b39d0486383ab99e42bcb9cc55601
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "mozilla/Services.h"
7 #include "nsCOMPtr.h"
8 #include "nsIObserverService.h"
9 #include "nsNamedPipeService.h"
10 #include "nsNetCID.h"
11 #include "nsThreadUtils.h"
12 #include "mozilla/ClearOnShutdown.h"
13 #include "mozilla/Logging.h"
15 namespace mozilla {
16 namespace net {
18 static mozilla::LazyLogModule gNamedPipeServiceLog("NamedPipeWin");
19 #define LOG_NPS_DEBUG(...) \
20 MOZ_LOG(gNamedPipeServiceLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
21 #define LOG_NPS_ERROR(...) \
22 MOZ_LOG(gNamedPipeServiceLog, mozilla::LogLevel::Error, (__VA_ARGS__))
24 StaticRefPtr<NamedPipeService> NamedPipeService::gSingleton;
26 NS_IMPL_ISUPPORTS(NamedPipeService, nsINamedPipeService, nsIObserver,
27 nsIRunnable)
29 NamedPipeService::NamedPipeService()
30 : mIocp(nullptr), mIsShutdown(false), mLock("NamedPipeServiceLock") {}
32 nsresult NamedPipeService::Init() {
33 MOZ_ASSERT(!mIsShutdown);
35 nsresult rv;
37 // nsIObserverService must be accessed in main thread.
38 // register shutdown event to stop NamedPipeSrv thread.
39 nsCOMPtr<nsIObserver> self(this);
40 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
41 "NamedPipeService::Init", [self = std::move(self)]() -> void {
42 MOZ_ASSERT(NS_IsMainThread());
44 nsCOMPtr<nsIObserverService> svc =
45 mozilla::services::GetObserverService();
47 if (NS_WARN_IF(!svc)) {
48 return;
51 if (NS_WARN_IF(NS_FAILED(svc->AddObserver(
52 self, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false)))) {
53 return;
55 });
57 if (NS_IsMainThread()) {
58 rv = r->Run();
59 } else {
60 rv = NS_DispatchToMainThread(r);
62 if (NS_WARN_IF(NS_FAILED(rv))) {
63 return rv;
66 mIocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 1);
67 if (NS_WARN_IF(!mIocp || mIocp == INVALID_HANDLE_VALUE)) {
68 Shutdown();
69 return NS_ERROR_FAILURE;
72 rv = NS_NewNamedThread("NamedPipeSrv", getter_AddRefs(mThread));
73 if (NS_WARN_IF(NS_FAILED(rv))) {
74 Shutdown();
75 return rv;
78 return NS_OK;
81 // static
82 already_AddRefed<nsINamedPipeService> NamedPipeService::GetOrCreate() {
83 MOZ_ASSERT(NS_IsMainThread());
85 RefPtr<NamedPipeService> inst;
86 if (gSingleton) {
87 inst = gSingleton;
88 } else {
89 inst = new NamedPipeService();
90 nsresult rv = inst->Init();
91 NS_ENSURE_SUCCESS(rv, nullptr);
92 gSingleton = inst;
93 ClearOnShutdown(&gSingleton);
96 return inst.forget();
99 void NamedPipeService::Shutdown() {
100 MOZ_ASSERT(NS_IsMainThread());
102 // remove observer
103 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
104 if (obs) {
105 obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
108 // stop thread
109 if (mThread && !mIsShutdown) {
110 mIsShutdown = true;
112 // invoke ERROR_ABANDONED_WAIT_0 to |GetQueuedCompletionStatus|
113 CloseHandle(mIocp);
114 mIocp = nullptr;
116 mThread->Shutdown();
119 // close I/O Completion Port
120 if (mIocp && mIocp != INVALID_HANDLE_VALUE) {
121 CloseHandle(mIocp);
122 mIocp = nullptr;
126 void NamedPipeService::RemoveRetiredObjects() {
127 MOZ_ASSERT(NS_GetCurrentThread() == mThread);
128 mLock.AssertCurrentThreadOwns();
130 if (!mRetiredHandles.IsEmpty()) {
131 for (auto& handle : mRetiredHandles) {
132 CloseHandle(handle);
134 mRetiredHandles.Clear();
137 mRetiredObservers.Clear();
141 * Implement nsINamedPipeService
144 NS_IMETHODIMP
145 NamedPipeService::AddDataObserver(void* aHandle,
146 nsINamedPipeDataObserver* aObserver) {
147 if (!aHandle || aHandle == INVALID_HANDLE_VALUE || !aObserver) {
148 return NS_ERROR_ILLEGAL_VALUE;
151 nsresult rv;
153 HANDLE h = CreateIoCompletionPort(aHandle, mIocp,
154 reinterpret_cast<ULONG_PTR>(aObserver), 1);
155 if (NS_WARN_IF(!h)) {
156 LOG_NPS_ERROR("CreateIoCompletionPort error (%lu)", GetLastError());
157 return NS_ERROR_FAILURE;
159 if (NS_WARN_IF(h != mIocp)) {
160 LOG_NPS_ERROR(
161 "CreateIoCompletionPort got unexpected value %p (should be %p)", h,
162 mIocp);
163 CloseHandle(h);
164 return NS_ERROR_FAILURE;
168 MutexAutoLock lock(mLock);
169 MOZ_ASSERT(!mObservers.Contains(aObserver));
171 mObservers.AppendElement(aObserver);
173 // start event loop
174 if (mObservers.Length() == 1) {
175 rv = mThread->Dispatch(this, NS_DISPATCH_NORMAL);
176 if (NS_WARN_IF(NS_FAILED(rv))) {
177 LOG_NPS_ERROR("Dispatch to thread failed (%08x)", uint32_t(rv));
178 mObservers.Clear();
179 return rv;
184 return NS_OK;
187 NS_IMETHODIMP
188 NamedPipeService::RemoveDataObserver(void* aHandle,
189 nsINamedPipeDataObserver* aObserver) {
190 MutexAutoLock lock(mLock);
191 mObservers.RemoveElement(aObserver);
193 mRetiredHandles.AppendElement(aHandle);
194 mRetiredObservers.AppendElement(aObserver);
196 return NS_OK;
199 NS_IMETHODIMP
200 NamedPipeService::IsOnCurrentThread(bool* aRetVal) {
201 MOZ_ASSERT(mThread);
202 MOZ_ASSERT(aRetVal);
204 if (!mThread) {
205 *aRetVal = false;
206 return NS_OK;
209 return mThread->IsOnCurrentThread(aRetVal);
213 * Implement nsIObserver
216 NS_IMETHODIMP
217 NamedPipeService::Observe(nsISupports* aSubject, const char* aTopic,
218 const char16_t* aData) {
219 MOZ_ASSERT(NS_IsMainThread());
221 if (!strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID, aTopic)) {
222 Shutdown();
225 return NS_OK;
229 * Implement nsIRunnable
232 NS_IMETHODIMP
233 NamedPipeService::Run() {
234 MOZ_ASSERT(NS_GetCurrentThread() == mThread);
235 MOZ_ASSERT(mIocp && mIocp != INVALID_HANDLE_VALUE);
237 while (!mIsShutdown) {
239 MutexAutoLock lock(mLock);
240 if (mObservers.IsEmpty()) {
241 LOG_NPS_DEBUG("no observer, stop loop");
242 break;
245 RemoveRetiredObjects();
248 DWORD bytesTransferred = 0;
249 ULONG_PTR key = 0;
250 LPOVERLAPPED overlapped = nullptr;
251 BOOL success =
252 GetQueuedCompletionStatus(mIocp, &bytesTransferred, &key, &overlapped,
253 1000); // timeout, 1s
254 auto err = GetLastError();
255 if (!success) {
256 if (err == WAIT_TIMEOUT) {
257 continue;
258 } else if (err == ERROR_ABANDONED_WAIT_0) { // mIocp was closed
259 break;
260 } else if (!overlapped) {
262 * Did not dequeue a completion packet from the completion port, and
263 * bytesTransferred/key are meaningless.
264 * See remarks of |GetQueuedCompletionStatus| API.
267 LOG_NPS_ERROR("invalid overlapped (%lu)", err);
268 continue;
271 MOZ_ASSERT(key);
275 * Windows doesn't provide a method to remove created I/O Completion Port,
276 * all we can do is just close the handle we monitored before.
277 * In some cases, there's race condition that the monitored handle has an
278 * I/O status after the observer is being removed and destroyed.
279 * To avoid changing the ref-count of a dangling pointer, don't use nsCOMPtr
280 * here.
282 nsINamedPipeDataObserver* target =
283 reinterpret_cast<nsINamedPipeDataObserver*>(key);
285 nsCOMPtr<nsINamedPipeDataObserver> obs;
287 MutexAutoLock lock(mLock);
289 auto idx = mObservers.IndexOf(target);
290 if (idx == decltype(mObservers)::NoIndex) {
291 LOG_NPS_ERROR("observer %p not found", target);
292 continue;
294 obs = target;
297 MOZ_ASSERT(obs.get());
299 if (success) {
300 LOG_NPS_DEBUG("OnDataAvailable: obs=%p, bytes=%lu", obs.get(),
301 bytesTransferred);
302 obs->OnDataAvailable(bytesTransferred, overlapped);
303 } else {
304 LOG_NPS_ERROR("GetQueuedCompletionStatus %p failed, error=%lu", obs.get(),
305 err);
306 obs->OnError(err, overlapped);
311 MutexAutoLock lock(mLock);
312 RemoveRetiredObjects();
315 return NS_OK;
318 } // namespace net
319 } // namespace mozilla