ipc/unixsocket/UnixSocket.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
michael@0 2 /* vim: set ts=2 et sw=2 tw=80: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "UnixSocket.h"
michael@0 8 #include "nsTArray.h"
michael@0 9 #include "nsXULAppAPI.h"
michael@0 10 #include <fcntl.h>
michael@0 11
michael@0 12 #ifdef MOZ_TASK_TRACER
michael@0 13 #include "GeckoTaskTracer.h"
michael@0 14 using namespace mozilla::tasktracer;
michael@0 15 #endif
michael@0 16
michael@0 17 static const size_t MAX_READ_SIZE = 1 << 16;
michael@0 18
michael@0 19 namespace mozilla {
michael@0 20 namespace ipc {
michael@0 21
michael@0 22 class UnixSocketImpl : public UnixSocketWatcher
michael@0 23 {
michael@0 24 public:
michael@0 25 UnixSocketImpl(MessageLoop* mIOLoop,
michael@0 26 UnixSocketConsumer* aConsumer, UnixSocketConnector* aConnector,
michael@0 27 const nsACString& aAddress)
michael@0 28 : UnixSocketWatcher(mIOLoop)
michael@0 29 , mConsumer(aConsumer)
michael@0 30 , mConnector(aConnector)
michael@0 31 , mShuttingDownOnIOThread(false)
michael@0 32 , mAddress(aAddress)
michael@0 33 , mDelayedConnectTask(nullptr)
michael@0 34 {
michael@0 35 }
michael@0 36
michael@0 37 ~UnixSocketImpl()
michael@0 38 {
michael@0 39 MOZ_ASSERT(NS_IsMainThread());
michael@0 40 MOZ_ASSERT(IsShutdownOnMainThread());
michael@0 41 }
michael@0 42
michael@0 43 void QueueWriteData(UnixSocketRawData* aData)
michael@0 44 {
michael@0 45 mOutgoingQ.AppendElement(aData);
michael@0 46 AddWatchers(WRITE_WATCHER, false);
michael@0 47 }
michael@0 48
michael@0 49 bool IsShutdownOnMainThread()
michael@0 50 {
michael@0 51 MOZ_ASSERT(NS_IsMainThread());
michael@0 52 return mConsumer == nullptr;
michael@0 53 }
michael@0 54
michael@0 55 void ShutdownOnMainThread()
michael@0 56 {
michael@0 57 MOZ_ASSERT(NS_IsMainThread());
michael@0 58 MOZ_ASSERT(!IsShutdownOnMainThread());
michael@0 59 mConsumer = nullptr;
michael@0 60 }
michael@0 61
michael@0 62 bool IsShutdownOnIOThread()
michael@0 63 {
michael@0 64 return mShuttingDownOnIOThread;
michael@0 65 }
michael@0 66
michael@0 67 void ShutdownOnIOThread()
michael@0 68 {
michael@0 69 MOZ_ASSERT(!NS_IsMainThread());
michael@0 70 MOZ_ASSERT(!mShuttingDownOnIOThread);
michael@0 71
michael@0 72 Close(); // will also remove fd from I/O loop
michael@0 73 mShuttingDownOnIOThread = true;
michael@0 74 }
michael@0 75
michael@0 76 void SetDelayedConnectTask(CancelableTask* aTask)
michael@0 77 {
michael@0 78 MOZ_ASSERT(NS_IsMainThread());
michael@0 79 mDelayedConnectTask = aTask;
michael@0 80 }
michael@0 81
michael@0 82 void ClearDelayedConnectTask()
michael@0 83 {
michael@0 84 MOZ_ASSERT(NS_IsMainThread());
michael@0 85 mDelayedConnectTask = nullptr;
michael@0 86 }
michael@0 87
michael@0 88 void CancelDelayedConnectTask()
michael@0 89 {
michael@0 90 MOZ_ASSERT(NS_IsMainThread());
michael@0 91 if (!mDelayedConnectTask) {
michael@0 92 return;
michael@0 93 }
michael@0 94 mDelayedConnectTask->Cancel();
michael@0 95 ClearDelayedConnectTask();
michael@0 96 }
michael@0 97
michael@0 98 /**
michael@0 99 * Connect to a socket
michael@0 100 */
michael@0 101 void Connect();
michael@0 102
michael@0 103 /**
michael@0 104 * Run bind/listen to prepare for further runs of accept()
michael@0 105 */
michael@0 106 void Listen();
michael@0 107
michael@0 108 void GetSocketAddr(nsAString& aAddrStr)
michael@0 109 {
michael@0 110 if (!mConnector) {
michael@0 111 NS_WARNING("No connector to get socket address from!");
michael@0 112 aAddrStr.Truncate();
michael@0 113 return;
michael@0 114 }
michael@0 115 mConnector->GetSocketAddr(mAddr, aAddrStr);
michael@0 116 }
michael@0 117
michael@0 118 /**
michael@0 119 * Consumer pointer. Non-thread safe RefPtr, so should only be manipulated
michael@0 120 * directly from main thread. All non-main-thread accesses should happen with
michael@0 121 * mImpl as container.
michael@0 122 */
michael@0 123 RefPtr<UnixSocketConsumer> mConsumer;
michael@0 124
michael@0 125 void OnAccepted(int aFd, const sockaddr_any* aAddr,
michael@0 126 socklen_t aAddrLen) MOZ_OVERRIDE;
michael@0 127 void OnConnected() MOZ_OVERRIDE;
michael@0 128 void OnError(const char* aFunction, int aErrno) MOZ_OVERRIDE;
michael@0 129 void OnListening() MOZ_OVERRIDE;
michael@0 130 void OnSocketCanReceiveWithoutBlocking() MOZ_OVERRIDE;
michael@0 131 void OnSocketCanSendWithoutBlocking() MOZ_OVERRIDE;
michael@0 132
michael@0 133 private:
michael@0 134 // Set up flags on whatever our current file descriptor is.
michael@0 135 static bool SetSocketFlags(int aFd);
michael@0 136
michael@0 137 void FireSocketError();
michael@0 138
michael@0 139 /**
michael@0 140 * Raw data queue. Must be pushed/popped from IO thread only.
michael@0 141 */
michael@0 142 typedef nsTArray<UnixSocketRawData* > UnixSocketRawDataQueue;
michael@0 143 UnixSocketRawDataQueue mOutgoingQ;
michael@0 144
michael@0 145 /**
michael@0 146 * Connector object used to create the connection we are currently using.
michael@0 147 */
michael@0 148 nsAutoPtr<UnixSocketConnector> mConnector;
michael@0 149
michael@0 150 /**
michael@0 151 * If true, do not requeue whatever task we're running
michael@0 152 */
michael@0 153 bool mShuttingDownOnIOThread;
michael@0 154
michael@0 155 /**
michael@0 156 * Address we are connecting to, assuming we are creating a client connection.
michael@0 157 */
michael@0 158 nsCString mAddress;
michael@0 159
michael@0 160 /**
michael@0 161 * Size of the socket address struct
michael@0 162 */
michael@0 163 socklen_t mAddrSize;
michael@0 164
michael@0 165 /**
michael@0 166 * Address struct of the socket currently in use
michael@0 167 */
michael@0 168 sockaddr_any mAddr;
michael@0 169
michael@0 170 /**
michael@0 171 * Task member for delayed connect task. Should only be access on main thread.
michael@0 172 */
michael@0 173 CancelableTask* mDelayedConnectTask;
michael@0 174 };
michael@0 175
michael@0 176 template<class T>
michael@0 177 class DeleteInstanceRunnable : public nsRunnable
michael@0 178 {
michael@0 179 public:
michael@0 180 DeleteInstanceRunnable(T* aInstance)
michael@0 181 : mInstance(aInstance)
michael@0 182 { }
michael@0 183
michael@0 184 NS_IMETHOD Run()
michael@0 185 {
michael@0 186 delete mInstance;
michael@0 187
michael@0 188 return NS_OK;
michael@0 189 }
michael@0 190
michael@0 191 private:
michael@0 192 T* mInstance;
michael@0 193 };
michael@0 194
michael@0 195 class UnixSocketImplRunnable : public nsRunnable
michael@0 196 {
michael@0 197 public:
michael@0 198 UnixSocketImpl* GetImpl() const
michael@0 199 {
michael@0 200 return mImpl;
michael@0 201 }
michael@0 202 protected:
michael@0 203 UnixSocketImplRunnable(UnixSocketImpl* aImpl)
michael@0 204 : mImpl(aImpl)
michael@0 205 {
michael@0 206 MOZ_ASSERT(aImpl);
michael@0 207 }
michael@0 208 virtual ~UnixSocketImplRunnable()
michael@0 209 { }
michael@0 210 private:
michael@0 211 UnixSocketImpl* mImpl;
michael@0 212 };
michael@0 213
michael@0 214 class OnSocketEventRunnable : public UnixSocketImplRunnable
michael@0 215 {
michael@0 216 public:
michael@0 217 enum SocketEvent {
michael@0 218 CONNECT_SUCCESS,
michael@0 219 CONNECT_ERROR,
michael@0 220 DISCONNECT
michael@0 221 };
michael@0 222
michael@0 223 OnSocketEventRunnable(UnixSocketImpl* aImpl, SocketEvent e)
michael@0 224 : UnixSocketImplRunnable(aImpl)
michael@0 225 , mEvent(e)
michael@0 226 {
michael@0 227 MOZ_ASSERT(!NS_IsMainThread());
michael@0 228 }
michael@0 229
michael@0 230 NS_IMETHOD Run() MOZ_OVERRIDE
michael@0 231 {
michael@0 232 MOZ_ASSERT(NS_IsMainThread());
michael@0 233
michael@0 234 UnixSocketImpl* impl = GetImpl();
michael@0 235
michael@0 236 if (impl->IsShutdownOnMainThread()) {
michael@0 237 NS_WARNING("CloseSocket has already been called!");
michael@0 238 // Since we've already explicitly closed and the close happened before
michael@0 239 // this, this isn't really an error. Since we've warned, return OK.
michael@0 240 return NS_OK;
michael@0 241 }
michael@0 242 if (mEvent == CONNECT_SUCCESS) {
michael@0 243 impl->mConsumer->NotifySuccess();
michael@0 244 } else if (mEvent == CONNECT_ERROR) {
michael@0 245 impl->mConsumer->NotifyError();
michael@0 246 } else if (mEvent == DISCONNECT) {
michael@0 247 impl->mConsumer->NotifyDisconnect();
michael@0 248 }
michael@0 249 return NS_OK;
michael@0 250 }
michael@0 251 private:
michael@0 252 SocketEvent mEvent;
michael@0 253 };
michael@0 254
michael@0 255 class SocketReceiveRunnable : public UnixSocketImplRunnable
michael@0 256 {
michael@0 257 public:
michael@0 258 SocketReceiveRunnable(UnixSocketImpl* aImpl, UnixSocketRawData* aData)
michael@0 259 : UnixSocketImplRunnable(aImpl)
michael@0 260 , mRawData(aData)
michael@0 261 {
michael@0 262 MOZ_ASSERT(aData);
michael@0 263 }
michael@0 264
michael@0 265 NS_IMETHOD Run() MOZ_OVERRIDE
michael@0 266 {
michael@0 267 MOZ_ASSERT(NS_IsMainThread());
michael@0 268
michael@0 269 UnixSocketImpl* impl = GetImpl();
michael@0 270
michael@0 271 if (impl->IsShutdownOnMainThread()) {
michael@0 272 NS_WARNING("mConsumer is null, aborting receive!");
michael@0 273 // Since we've already explicitly closed and the close happened before
michael@0 274 // this, this isn't really an error. Since we've warned, return OK.
michael@0 275 return NS_OK;
michael@0 276 }
michael@0 277
michael@0 278 MOZ_ASSERT(impl->mConsumer);
michael@0 279 impl->mConsumer->ReceiveSocketData(mRawData);
michael@0 280 return NS_OK;
michael@0 281 }
michael@0 282 private:
michael@0 283 nsAutoPtr<UnixSocketRawData> mRawData;
michael@0 284 };
michael@0 285
michael@0 286 class RequestClosingSocketRunnable : public UnixSocketImplRunnable
michael@0 287 {
michael@0 288 public:
michael@0 289 RequestClosingSocketRunnable(UnixSocketImpl* aImpl)
michael@0 290 : UnixSocketImplRunnable(aImpl)
michael@0 291 { }
michael@0 292
michael@0 293 NS_IMETHOD Run() MOZ_OVERRIDE
michael@0 294 {
michael@0 295 MOZ_ASSERT(NS_IsMainThread());
michael@0 296
michael@0 297 UnixSocketImpl* impl = GetImpl();
michael@0 298 if (impl->IsShutdownOnMainThread()) {
michael@0 299 NS_WARNING("CloseSocket has already been called!");
michael@0 300 // Since we've already explicitly closed and the close happened before
michael@0 301 // this, this isn't really an error. Since we've warned, return OK.
michael@0 302 return NS_OK;
michael@0 303 }
michael@0 304
michael@0 305 // Start from here, same handling flow as calling CloseSocket() from
michael@0 306 // upper layer
michael@0 307 impl->mConsumer->CloseSocket();
michael@0 308 return NS_OK;
michael@0 309 }
michael@0 310 };
michael@0 311
michael@0 312 class UnixSocketImplTask : public CancelableTask
michael@0 313 {
michael@0 314 public:
michael@0 315 UnixSocketImpl* GetImpl() const
michael@0 316 {
michael@0 317 return mImpl;
michael@0 318 }
michael@0 319 void Cancel() MOZ_OVERRIDE
michael@0 320 {
michael@0 321 mImpl = nullptr;
michael@0 322 }
michael@0 323 bool IsCanceled() const
michael@0 324 {
michael@0 325 return !mImpl;
michael@0 326 }
michael@0 327 protected:
michael@0 328 UnixSocketImplTask(UnixSocketImpl* aImpl)
michael@0 329 : mImpl(aImpl)
michael@0 330 {
michael@0 331 MOZ_ASSERT(mImpl);
michael@0 332 }
michael@0 333 private:
michael@0 334 UnixSocketImpl* mImpl;
michael@0 335 };
michael@0 336
michael@0 337 class SocketSendTask : public UnixSocketImplTask
michael@0 338 {
michael@0 339 public:
michael@0 340 SocketSendTask(UnixSocketImpl* aImpl,
michael@0 341 UnixSocketConsumer* aConsumer,
michael@0 342 UnixSocketRawData* aData)
michael@0 343 : UnixSocketImplTask(aImpl)
michael@0 344 , mConsumer(aConsumer)
michael@0 345 , mData(aData)
michael@0 346 {
michael@0 347 MOZ_ASSERT(aConsumer);
michael@0 348 MOZ_ASSERT(aData);
michael@0 349 }
michael@0 350 void Run() MOZ_OVERRIDE
michael@0 351 {
michael@0 352 MOZ_ASSERT(!NS_IsMainThread());
michael@0 353 MOZ_ASSERT(!IsCanceled());
michael@0 354
michael@0 355 UnixSocketImpl* impl = GetImpl();
michael@0 356 MOZ_ASSERT(!impl->IsShutdownOnIOThread());
michael@0 357
michael@0 358 impl->QueueWriteData(mData);
michael@0 359 }
michael@0 360 private:
michael@0 361 nsRefPtr<UnixSocketConsumer> mConsumer;
michael@0 362 UnixSocketRawData* mData;
michael@0 363 };
michael@0 364
michael@0 365 class SocketListenTask : public UnixSocketImplTask
michael@0 366 {
michael@0 367 public:
michael@0 368 SocketListenTask(UnixSocketImpl* aImpl)
michael@0 369 : UnixSocketImplTask(aImpl)
michael@0 370 { }
michael@0 371
michael@0 372 void Run() MOZ_OVERRIDE
michael@0 373 {
michael@0 374 MOZ_ASSERT(!NS_IsMainThread());
michael@0 375 if (!IsCanceled()) {
michael@0 376 GetImpl()->Listen();
michael@0 377 }
michael@0 378 }
michael@0 379 };
michael@0 380
michael@0 381 class SocketConnectTask : public UnixSocketImplTask
michael@0 382 {
michael@0 383 public:
michael@0 384 SocketConnectTask(UnixSocketImpl* aImpl)
michael@0 385 : UnixSocketImplTask(aImpl)
michael@0 386 { }
michael@0 387
michael@0 388 void Run() MOZ_OVERRIDE
michael@0 389 {
michael@0 390 MOZ_ASSERT(!NS_IsMainThread());
michael@0 391 MOZ_ASSERT(!IsCanceled());
michael@0 392 GetImpl()->Connect();
michael@0 393 }
michael@0 394 };
michael@0 395
michael@0 396 class SocketDelayedConnectTask : public UnixSocketImplTask
michael@0 397 {
michael@0 398 public:
michael@0 399 SocketDelayedConnectTask(UnixSocketImpl* aImpl)
michael@0 400 : UnixSocketImplTask(aImpl)
michael@0 401 { }
michael@0 402
michael@0 403 void Run() MOZ_OVERRIDE
michael@0 404 {
michael@0 405 MOZ_ASSERT(NS_IsMainThread());
michael@0 406 if (IsCanceled()) {
michael@0 407 return;
michael@0 408 }
michael@0 409 UnixSocketImpl* impl = GetImpl();
michael@0 410 if (impl->IsShutdownOnMainThread()) {
michael@0 411 return;
michael@0 412 }
michael@0 413 impl->ClearDelayedConnectTask();
michael@0 414 XRE_GetIOMessageLoop()->PostTask(FROM_HERE, new SocketConnectTask(impl));
michael@0 415 }
michael@0 416 };
michael@0 417
michael@0 418 class ShutdownSocketTask : public UnixSocketImplTask
michael@0 419 {
michael@0 420 public:
michael@0 421 ShutdownSocketTask(UnixSocketImpl* aImpl)
michael@0 422 : UnixSocketImplTask(aImpl)
michael@0 423 { }
michael@0 424
michael@0 425 void Run() MOZ_OVERRIDE
michael@0 426 {
michael@0 427 MOZ_ASSERT(!NS_IsMainThread());
michael@0 428 MOZ_ASSERT(!IsCanceled());
michael@0 429
michael@0 430 UnixSocketImpl* impl = GetImpl();
michael@0 431
michael@0 432 // At this point, there should be no new events on the IO thread after this
michael@0 433 // one with the possible exception of a SocketListenTask that
michael@0 434 // ShutdownOnIOThread will cancel for us. We are now fully shut down, so we
michael@0 435 // can send a message to the main thread that will delete impl safely knowing
michael@0 436 // that no more tasks reference it.
michael@0 437 impl->ShutdownOnIOThread();
michael@0 438
michael@0 439 nsRefPtr<nsIRunnable> r(new DeleteInstanceRunnable<UnixSocketImpl>(impl));
michael@0 440 nsresult rv = NS_DispatchToMainThread(r);
michael@0 441 NS_ENSURE_SUCCESS_VOID(rv);
michael@0 442 }
michael@0 443 };
michael@0 444
michael@0 445 void
michael@0 446 UnixSocketImpl::FireSocketError()
michael@0 447 {
michael@0 448 MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
michael@0 449
michael@0 450 // Clean up watchers, statuses, fds
michael@0 451 Close();
michael@0 452
michael@0 453 // Tell the main thread we've errored
michael@0 454 nsRefPtr<OnSocketEventRunnable> r =
michael@0 455 new OnSocketEventRunnable(this, OnSocketEventRunnable::CONNECT_ERROR);
michael@0 456 NS_DispatchToMainThread(r);
michael@0 457 }
michael@0 458
michael@0 459 void
michael@0 460 UnixSocketImpl::Listen()
michael@0 461 {
michael@0 462 MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
michael@0 463 MOZ_ASSERT(mConnector);
michael@0 464
michael@0 465 // This will set things we don't particularly care about, but it will hand
michael@0 466 // back the correct structure size which is what we do care about.
michael@0 467 if (!mConnector->CreateAddr(true, mAddrSize, mAddr, nullptr)) {
michael@0 468 NS_WARNING("Cannot create socket address!");
michael@0 469 FireSocketError();
michael@0 470 return;
michael@0 471 }
michael@0 472
michael@0 473 if (!IsOpen()) {
michael@0 474 int fd = mConnector->Create();
michael@0 475 if (fd < 0) {
michael@0 476 NS_WARNING("Cannot create socket fd!");
michael@0 477 FireSocketError();
michael@0 478 return;
michael@0 479 }
michael@0 480 if (!SetSocketFlags(fd)) {
michael@0 481 NS_WARNING("Cannot set socket flags!");
michael@0 482 FireSocketError();
michael@0 483 return;
michael@0 484 }
michael@0 485 SetFd(fd);
michael@0 486
michael@0 487 // calls OnListening on success, or OnError otherwise
michael@0 488 nsresult rv = UnixSocketWatcher::Listen(
michael@0 489 reinterpret_cast<struct sockaddr*>(&mAddr), mAddrSize);
michael@0 490 NS_WARN_IF(NS_FAILED(rv));
michael@0 491 }
michael@0 492 }
michael@0 493
michael@0 494 void
michael@0 495 UnixSocketImpl::Connect()
michael@0 496 {
michael@0 497 MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
michael@0 498 MOZ_ASSERT(mConnector);
michael@0 499
michael@0 500 if (!IsOpen()) {
michael@0 501 int fd = mConnector->Create();
michael@0 502 if (fd < 0) {
michael@0 503 NS_WARNING("Cannot create socket fd!");
michael@0 504 FireSocketError();
michael@0 505 return;
michael@0 506 }
michael@0 507 if (!SetSocketFlags(fd)) {
michael@0 508 NS_WARNING("Cannot set socket flags!");
michael@0 509 FireSocketError();
michael@0 510 return;
michael@0 511 }
michael@0 512 SetFd(fd);
michael@0 513 }
michael@0 514
michael@0 515 if (!mConnector->CreateAddr(false, mAddrSize, mAddr, mAddress.get())) {
michael@0 516 NS_WARNING("Cannot create socket address!");
michael@0 517 FireSocketError();
michael@0 518 return;
michael@0 519 }
michael@0 520
michael@0 521 // calls OnConnected() on success, or OnError() otherwise
michael@0 522 nsresult rv = UnixSocketWatcher::Connect(
michael@0 523 reinterpret_cast<struct sockaddr*>(&mAddr), mAddrSize);
michael@0 524 NS_WARN_IF(NS_FAILED(rv));
michael@0 525 }
michael@0 526
michael@0 527 bool
michael@0 528 UnixSocketImpl::SetSocketFlags(int aFd)
michael@0 529 {
michael@0 530 // Set socket addr to be reused even if kernel is still waiting to close
michael@0 531 int n = 1;
michael@0 532 if (setsockopt(aFd, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n)) < 0) {
michael@0 533 return false;
michael@0 534 }
michael@0 535
michael@0 536 // Set close-on-exec bit.
michael@0 537 int flags = TEMP_FAILURE_RETRY(fcntl(aFd, F_GETFD));
michael@0 538 if (-1 == flags) {
michael@0 539 return false;
michael@0 540 }
michael@0 541 flags |= FD_CLOEXEC;
michael@0 542 if (-1 == TEMP_FAILURE_RETRY(fcntl(aFd, F_SETFD, flags))) {
michael@0 543 return false;
michael@0 544 }
michael@0 545
michael@0 546 // Set non-blocking status flag.
michael@0 547 flags = TEMP_FAILURE_RETRY(fcntl(aFd, F_GETFL));
michael@0 548 if (-1 == flags) {
michael@0 549 return false;
michael@0 550 }
michael@0 551 flags |= O_NONBLOCK;
michael@0 552 if (-1 == TEMP_FAILURE_RETRY(fcntl(aFd, F_SETFL, flags))) {
michael@0 553 return false;
michael@0 554 }
michael@0 555
michael@0 556 return true;
michael@0 557 }
michael@0 558
michael@0 559 void
michael@0 560 UnixSocketImpl::OnAccepted(int aFd,
michael@0 561 const sockaddr_any* aAddr,
michael@0 562 socklen_t aAddrLen)
michael@0 563 {
michael@0 564 MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
michael@0 565 MOZ_ASSERT(GetConnectionStatus() == SOCKET_IS_LISTENING);
michael@0 566 MOZ_ASSERT(aAddr);
michael@0 567 MOZ_ASSERT(aAddrLen <= sizeof(mAddr));
michael@0 568
michael@0 569 memcpy (&mAddr, aAddr, aAddrLen);
michael@0 570 mAddrSize = aAddrLen;
michael@0 571
michael@0 572 if (!mConnector->SetUp(aFd)) {
michael@0 573 NS_WARNING("Could not set up socket!");
michael@0 574 return;
michael@0 575 }
michael@0 576
michael@0 577 RemoveWatchers(READ_WATCHER|WRITE_WATCHER);
michael@0 578 Close();
michael@0 579 if (!SetSocketFlags(aFd)) {
michael@0 580 return;
michael@0 581 }
michael@0 582 SetSocket(aFd, SOCKET_IS_CONNECTED);
michael@0 583
michael@0 584 nsRefPtr<OnSocketEventRunnable> r =
michael@0 585 new OnSocketEventRunnable(this, OnSocketEventRunnable::CONNECT_SUCCESS);
michael@0 586 NS_DispatchToMainThread(r);
michael@0 587
michael@0 588 AddWatchers(READ_WATCHER, true);
michael@0 589 if (!mOutgoingQ.IsEmpty()) {
michael@0 590 AddWatchers(WRITE_WATCHER, false);
michael@0 591 }
michael@0 592 }
michael@0 593
michael@0 594 void
michael@0 595 UnixSocketImpl::OnConnected()
michael@0 596 {
michael@0 597 MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
michael@0 598 MOZ_ASSERT(GetConnectionStatus() == SOCKET_IS_CONNECTED);
michael@0 599
michael@0 600 if (!SetSocketFlags(GetFd())) {
michael@0 601 NS_WARNING("Cannot set socket flags!");
michael@0 602 FireSocketError();
michael@0 603 return;
michael@0 604 }
michael@0 605
michael@0 606 if (!mConnector->SetUp(GetFd())) {
michael@0 607 NS_WARNING("Could not set up socket!");
michael@0 608 FireSocketError();
michael@0 609 return;
michael@0 610 }
michael@0 611
michael@0 612 nsRefPtr<OnSocketEventRunnable> r =
michael@0 613 new OnSocketEventRunnable(this, OnSocketEventRunnable::CONNECT_SUCCESS);
michael@0 614 NS_DispatchToMainThread(r);
michael@0 615
michael@0 616 AddWatchers(READ_WATCHER, true);
michael@0 617 if (!mOutgoingQ.IsEmpty()) {
michael@0 618 AddWatchers(WRITE_WATCHER, false);
michael@0 619 }
michael@0 620 }
michael@0 621
michael@0 622 void
michael@0 623 UnixSocketImpl::OnListening()
michael@0 624 {
michael@0 625 MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
michael@0 626 MOZ_ASSERT(GetConnectionStatus() == SOCKET_IS_LISTENING);
michael@0 627
michael@0 628 if (!mConnector->SetUpListenSocket(GetFd())) {
michael@0 629 NS_WARNING("Could not set up listen socket!");
michael@0 630 FireSocketError();
michael@0 631 return;
michael@0 632 }
michael@0 633
michael@0 634 AddWatchers(READ_WATCHER, true);
michael@0 635 }
michael@0 636
michael@0 637 void
michael@0 638 UnixSocketImpl::OnError(const char* aFunction, int aErrno)
michael@0 639 {
michael@0 640 MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
michael@0 641
michael@0 642 UnixFdWatcher::OnError(aFunction, aErrno);
michael@0 643 FireSocketError();
michael@0 644 }
michael@0 645
michael@0 646 void
michael@0 647 UnixSocketImpl::OnSocketCanReceiveWithoutBlocking()
michael@0 648 {
michael@0 649 MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
michael@0 650 MOZ_ASSERT(GetConnectionStatus() == SOCKET_IS_CONNECTED); // see bug 990984
michael@0 651
michael@0 652 // Read all of the incoming data.
michael@0 653 while (true) {
michael@0 654 nsAutoPtr<UnixSocketRawData> incoming(new UnixSocketRawData(MAX_READ_SIZE));
michael@0 655
michael@0 656 ssize_t ret = read(GetFd(), incoming->mData, incoming->mSize);
michael@0 657 if (ret <= 0) {
michael@0 658 if (ret == -1) {
michael@0 659 if (errno == EINTR) {
michael@0 660 continue; // retry system call when interrupted
michael@0 661 }
michael@0 662 if (errno == EAGAIN || errno == EWOULDBLOCK) {
michael@0 663 return; // no data available: return and re-poll
michael@0 664 }
michael@0 665
michael@0 666 #ifdef DEBUG
michael@0 667 NS_WARNING("Cannot read from network");
michael@0 668 #endif
michael@0 669 // else fall through to error handling on other errno's
michael@0 670 }
michael@0 671
michael@0 672 // We're done with our descriptors. Ensure that spurious events don't
michael@0 673 // cause us to end up back here.
michael@0 674 RemoveWatchers(READ_WATCHER|WRITE_WATCHER);
michael@0 675 nsRefPtr<RequestClosingSocketRunnable> r =
michael@0 676 new RequestClosingSocketRunnable(this);
michael@0 677 NS_DispatchToMainThread(r);
michael@0 678 return;
michael@0 679 }
michael@0 680
michael@0 681 #ifdef MOZ_TASK_TRACER
michael@0 682 // Make unix socket creation events to be the source events of TaskTracer,
michael@0 683 // and originate the rest correlation tasks from here.
michael@0 684 AutoSourceEvent taskTracerEvent(SourceEventType::UNIXSOCKET);
michael@0 685 #endif
michael@0 686
michael@0 687 incoming->mSize = ret;
michael@0 688 nsRefPtr<SocketReceiveRunnable> r =
michael@0 689 new SocketReceiveRunnable(this, incoming.forget());
michael@0 690 NS_DispatchToMainThread(r);
michael@0 691
michael@0 692 // If ret is less than MAX_READ_SIZE, there's no
michael@0 693 // more data in the socket for us to read now.
michael@0 694 if (ret < ssize_t(MAX_READ_SIZE)) {
michael@0 695 return;
michael@0 696 }
michael@0 697 }
michael@0 698 }
michael@0 699
michael@0 700 void
michael@0 701 UnixSocketImpl::OnSocketCanSendWithoutBlocking()
michael@0 702 {
michael@0 703 MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
michael@0 704 MOZ_ASSERT(GetConnectionStatus() == SOCKET_IS_CONNECTED); // see bug 990984
michael@0 705
michael@0 706 // Try to write the bytes of mCurrentRilRawData. If all were written, continue.
michael@0 707 //
michael@0 708 // Otherwise, save the byte position of the next byte to write
michael@0 709 // within mCurrentWriteOffset, and request another write when the
michael@0 710 // system won't block.
michael@0 711 //
michael@0 712 while (true) {
michael@0 713 UnixSocketRawData* data;
michael@0 714 if (mOutgoingQ.IsEmpty()) {
michael@0 715 return;
michael@0 716 }
michael@0 717 data = mOutgoingQ.ElementAt(0);
michael@0 718 const uint8_t *toWrite;
michael@0 719 toWrite = data->mData;
michael@0 720
michael@0 721 while (data->mCurrentWriteOffset < data->mSize) {
michael@0 722 ssize_t write_amount = data->mSize - data->mCurrentWriteOffset;
michael@0 723 ssize_t written;
michael@0 724 written = write (GetFd(), toWrite + data->mCurrentWriteOffset,
michael@0 725 write_amount);
michael@0 726 if (written > 0) {
michael@0 727 data->mCurrentWriteOffset += written;
michael@0 728 }
michael@0 729 if (written != write_amount) {
michael@0 730 break;
michael@0 731 }
michael@0 732 }
michael@0 733
michael@0 734 if (data->mCurrentWriteOffset != data->mSize) {
michael@0 735 AddWatchers(WRITE_WATCHER, false);
michael@0 736 return;
michael@0 737 }
michael@0 738 mOutgoingQ.RemoveElementAt(0);
michael@0 739 delete data;
michael@0 740 }
michael@0 741 }
michael@0 742
michael@0 743 UnixSocketConsumer::UnixSocketConsumer() : mImpl(nullptr)
michael@0 744 , mConnectionStatus(SOCKET_DISCONNECTED)
michael@0 745 , mConnectTimestamp(0)
michael@0 746 , mConnectDelayMs(0)
michael@0 747 {
michael@0 748 }
michael@0 749
michael@0 750 UnixSocketConsumer::~UnixSocketConsumer()
michael@0 751 {
michael@0 752 MOZ_ASSERT(mConnectionStatus == SOCKET_DISCONNECTED);
michael@0 753 MOZ_ASSERT(!mImpl);
michael@0 754 }
michael@0 755
michael@0 756 bool
michael@0 757 UnixSocketConsumer::SendSocketData(UnixSocketRawData* aData)
michael@0 758 {
michael@0 759 MOZ_ASSERT(NS_IsMainThread());
michael@0 760 if (!mImpl) {
michael@0 761 return false;
michael@0 762 }
michael@0 763
michael@0 764 MOZ_ASSERT(!mImpl->IsShutdownOnMainThread());
michael@0 765 XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
michael@0 766 new SocketSendTask(mImpl, this, aData));
michael@0 767 return true;
michael@0 768 }
michael@0 769
michael@0 770 bool
michael@0 771 UnixSocketConsumer::SendSocketData(const nsACString& aStr)
michael@0 772 {
michael@0 773 MOZ_ASSERT(NS_IsMainThread());
michael@0 774 if (!mImpl) {
michael@0 775 return false;
michael@0 776 }
michael@0 777 if (aStr.Length() > MAX_READ_SIZE) {
michael@0 778 return false;
michael@0 779 }
michael@0 780
michael@0 781 MOZ_ASSERT(!mImpl->IsShutdownOnMainThread());
michael@0 782 UnixSocketRawData* d = new UnixSocketRawData(aStr.BeginReading(),
michael@0 783 aStr.Length());
michael@0 784 XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
michael@0 785 new SocketSendTask(mImpl, this, d));
michael@0 786 return true;
michael@0 787 }
michael@0 788
michael@0 789 void
michael@0 790 UnixSocketConsumer::CloseSocket()
michael@0 791 {
michael@0 792 MOZ_ASSERT(NS_IsMainThread());
michael@0 793 if (!mImpl) {
michael@0 794 return;
michael@0 795 }
michael@0 796
michael@0 797 mImpl->CancelDelayedConnectTask();
michael@0 798
michael@0 799 // From this point on, we consider mImpl as being deleted.
michael@0 800 // We sever the relationship here so any future calls to listen or connect
michael@0 801 // will create a new implementation.
michael@0 802 mImpl->ShutdownOnMainThread();
michael@0 803
michael@0 804 XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
michael@0 805 new ShutdownSocketTask(mImpl));
michael@0 806
michael@0 807 mImpl = nullptr;
michael@0 808
michael@0 809 NotifyDisconnect();
michael@0 810 }
michael@0 811
michael@0 812 void
michael@0 813 UnixSocketConsumer::GetSocketAddr(nsAString& aAddrStr)
michael@0 814 {
michael@0 815 aAddrStr.Truncate();
michael@0 816 if (!mImpl || mConnectionStatus != SOCKET_CONNECTED) {
michael@0 817 NS_WARNING("No socket currently open!");
michael@0 818 return;
michael@0 819 }
michael@0 820 mImpl->GetSocketAddr(aAddrStr);
michael@0 821 }
michael@0 822
michael@0 823 void
michael@0 824 UnixSocketConsumer::NotifySuccess()
michael@0 825 {
michael@0 826 MOZ_ASSERT(NS_IsMainThread());
michael@0 827 mConnectionStatus = SOCKET_CONNECTED;
michael@0 828 mConnectTimestamp = PR_IntervalNow();
michael@0 829 OnConnectSuccess();
michael@0 830 }
michael@0 831
michael@0 832 void
michael@0 833 UnixSocketConsumer::NotifyError()
michael@0 834 {
michael@0 835 MOZ_ASSERT(NS_IsMainThread());
michael@0 836 mConnectionStatus = SOCKET_DISCONNECTED;
michael@0 837 mConnectDelayMs = CalculateConnectDelayMs();
michael@0 838 OnConnectError();
michael@0 839 }
michael@0 840
michael@0 841 void
michael@0 842 UnixSocketConsumer::NotifyDisconnect()
michael@0 843 {
michael@0 844 MOZ_ASSERT(NS_IsMainThread());
michael@0 845 mConnectionStatus = SOCKET_DISCONNECTED;
michael@0 846 mConnectDelayMs = CalculateConnectDelayMs();
michael@0 847 OnDisconnect();
michael@0 848 }
michael@0 849
michael@0 850 bool
michael@0 851 UnixSocketConsumer::ConnectSocket(UnixSocketConnector* aConnector,
michael@0 852 const char* aAddress,
michael@0 853 int aDelayMs)
michael@0 854 {
michael@0 855 MOZ_ASSERT(aConnector);
michael@0 856 MOZ_ASSERT(NS_IsMainThread());
michael@0 857
michael@0 858 nsAutoPtr<UnixSocketConnector> connector(aConnector);
michael@0 859
michael@0 860 if (mImpl) {
michael@0 861 NS_WARNING("Socket already connecting/connected!");
michael@0 862 return false;
michael@0 863 }
michael@0 864
michael@0 865 nsCString addr(aAddress);
michael@0 866 MessageLoop* ioLoop = XRE_GetIOMessageLoop();
michael@0 867 mImpl = new UnixSocketImpl(ioLoop, this, connector.forget(), addr);
michael@0 868 mConnectionStatus = SOCKET_CONNECTING;
michael@0 869 if (aDelayMs > 0) {
michael@0 870 SocketDelayedConnectTask* connectTask = new SocketDelayedConnectTask(mImpl);
michael@0 871 mImpl->SetDelayedConnectTask(connectTask);
michael@0 872 MessageLoop::current()->PostDelayedTask(FROM_HERE, connectTask, aDelayMs);
michael@0 873 } else {
michael@0 874 ioLoop->PostTask(FROM_HERE, new SocketConnectTask(mImpl));
michael@0 875 }
michael@0 876 return true;
michael@0 877 }
michael@0 878
michael@0 879 bool
michael@0 880 UnixSocketConsumer::ListenSocket(UnixSocketConnector* aConnector)
michael@0 881 {
michael@0 882 MOZ_ASSERT(aConnector);
michael@0 883 MOZ_ASSERT(NS_IsMainThread());
michael@0 884
michael@0 885 nsAutoPtr<UnixSocketConnector> connector(aConnector);
michael@0 886
michael@0 887 if (mImpl) {
michael@0 888 NS_WARNING("Socket already connecting/connected!");
michael@0 889 return false;
michael@0 890 }
michael@0 891
michael@0 892 mImpl = new UnixSocketImpl(XRE_GetIOMessageLoop(), this, connector.forget(),
michael@0 893 EmptyCString());
michael@0 894 mConnectionStatus = SOCKET_LISTENING;
michael@0 895 XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
michael@0 896 new SocketListenTask(mImpl));
michael@0 897 return true;
michael@0 898 }
michael@0 899
michael@0 900 uint32_t
michael@0 901 UnixSocketConsumer::CalculateConnectDelayMs() const
michael@0 902 {
michael@0 903 MOZ_ASSERT(NS_IsMainThread());
michael@0 904
michael@0 905 uint32_t connectDelayMs = mConnectDelayMs;
michael@0 906
michael@0 907 if ((PR_IntervalNow()-mConnectTimestamp) > connectDelayMs) {
michael@0 908 // reset delay if connection has been opened for a while, or...
michael@0 909 connectDelayMs = 0;
michael@0 910 } else if (!connectDelayMs) {
michael@0 911 // ...start with a delay of ~1 sec, or...
michael@0 912 connectDelayMs = 1<<10;
michael@0 913 } else if (connectDelayMs < (1<<16)) {
michael@0 914 // ...otherwise increase delay by a factor of 2
michael@0 915 connectDelayMs <<= 1;
michael@0 916 }
michael@0 917 return connectDelayMs;
michael@0 918 }
michael@0 919
michael@0 920 } // namespace ipc
michael@0 921 } // namespace mozilla

mercurial