Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / net / socket / websocket_endpoint_lock_manager.cc
blob8a218dfb649326ca345be3abbe76f2bddf3797eb
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/socket/websocket_endpoint_lock_manager.h"
7 #include <utility>
9 #include "base/bind.h"
10 #include "base/location.h"
11 #include "base/logging.h"
12 #include "base/single_thread_task_runner.h"
13 #include "base/thread_task_runner_handle.h"
14 #include "net/base/net_errors.h"
15 #include "net/log/net_log.h"
17 namespace net {
19 namespace {
21 // This delay prevents DoS attacks.
22 // TODO(ricea): Replace this with randomised truncated exponential backoff.
23 // See crbug.com/377613.
24 const int kUnlockDelayInMs = 10;
26 } // namespace
28 WebSocketEndpointLockManager::Waiter::~Waiter() {
29 if (next()) {
30 DCHECK(previous());
31 RemoveFromList();
35 WebSocketEndpointLockManager* WebSocketEndpointLockManager::GetInstance() {
36 return Singleton<WebSocketEndpointLockManager>::get();
39 int WebSocketEndpointLockManager::LockEndpoint(const IPEndPoint& endpoint,
40 Waiter* waiter) {
41 LockInfoMap::value_type insert_value(endpoint, LockInfo());
42 std::pair<LockInfoMap::iterator, bool> rv =
43 lock_info_map_.insert(insert_value);
44 LockInfo& lock_info_in_map = rv.first->second;
45 if (rv.second) {
46 DVLOG(3) << "Locking endpoint " << endpoint.ToString();
47 lock_info_in_map.queue.reset(new LockInfo::WaiterQueue);
48 return OK;
50 DVLOG(3) << "Waiting for endpoint " << endpoint.ToString();
51 lock_info_in_map.queue->Append(waiter);
52 return ERR_IO_PENDING;
55 void WebSocketEndpointLockManager::RememberSocket(StreamSocket* socket,
56 const IPEndPoint& endpoint) {
57 LockInfoMap::iterator lock_info_it = lock_info_map_.find(endpoint);
58 CHECK(lock_info_it != lock_info_map_.end());
59 bool inserted =
60 socket_lock_info_map_.insert(SocketLockInfoMap::value_type(
61 socket, lock_info_it)).second;
62 DCHECK(inserted);
63 DCHECK(!lock_info_it->second.socket);
64 lock_info_it->second.socket = socket;
65 DVLOG(3) << "Remembered (StreamSocket*)" << socket << " for "
66 << endpoint.ToString() << " (" << socket_lock_info_map_.size()
67 << " socket(s) remembered)";
70 void WebSocketEndpointLockManager::UnlockSocket(StreamSocket* socket) {
71 SocketLockInfoMap::iterator socket_it = socket_lock_info_map_.find(socket);
72 if (socket_it == socket_lock_info_map_.end())
73 return;
75 LockInfoMap::iterator lock_info_it = socket_it->second;
77 DVLOG(3) << "Unlocking (StreamSocket*)" << socket << " for "
78 << lock_info_it->first.ToString() << " ("
79 << socket_lock_info_map_.size() << " socket(s) left)";
80 socket_lock_info_map_.erase(socket_it);
81 DCHECK_EQ(socket, lock_info_it->second.socket);
82 lock_info_it->second.socket = NULL;
83 UnlockEndpointAfterDelay(lock_info_it->first);
86 void WebSocketEndpointLockManager::UnlockEndpoint(const IPEndPoint& endpoint) {
87 LockInfoMap::iterator lock_info_it = lock_info_map_.find(endpoint);
88 if (lock_info_it == lock_info_map_.end())
89 return;
90 if (lock_info_it->second.socket)
91 EraseSocket(lock_info_it);
92 UnlockEndpointAfterDelay(endpoint);
95 bool WebSocketEndpointLockManager::IsEmpty() const {
96 return lock_info_map_.empty() && socket_lock_info_map_.empty();
99 base::TimeDelta WebSocketEndpointLockManager::SetUnlockDelayForTesting(
100 base::TimeDelta new_delay) {
101 base::TimeDelta old_delay = unlock_delay_;
102 unlock_delay_ = new_delay;
103 return old_delay;
106 WebSocketEndpointLockManager::LockInfo::LockInfo() : socket(NULL) {}
107 WebSocketEndpointLockManager::LockInfo::~LockInfo() {
108 DCHECK(!socket);
111 WebSocketEndpointLockManager::LockInfo::LockInfo(const LockInfo& rhs)
112 : socket(rhs.socket) {
113 DCHECK(!rhs.queue);
116 WebSocketEndpointLockManager::WebSocketEndpointLockManager()
117 : unlock_delay_(base::TimeDelta::FromMilliseconds(kUnlockDelayInMs)),
118 pending_unlock_count_(0),
119 weak_factory_(this) {
122 WebSocketEndpointLockManager::~WebSocketEndpointLockManager() {
123 DCHECK_EQ(lock_info_map_.size(), pending_unlock_count_);
124 DCHECK(socket_lock_info_map_.empty());
127 void WebSocketEndpointLockManager::UnlockEndpointAfterDelay(
128 const IPEndPoint& endpoint) {
129 DVLOG(3) << "Delaying " << unlock_delay_.InMilliseconds()
130 << "ms before unlocking endpoint " << endpoint.ToString();
131 ++pending_unlock_count_;
132 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
133 FROM_HERE,
134 base::Bind(&WebSocketEndpointLockManager::DelayedUnlockEndpoint,
135 weak_factory_.GetWeakPtr(), endpoint),
136 unlock_delay_);
139 void WebSocketEndpointLockManager::DelayedUnlockEndpoint(
140 const IPEndPoint& endpoint) {
141 LockInfoMap::iterator lock_info_it = lock_info_map_.find(endpoint);
142 DCHECK_GT(pending_unlock_count_, 0U);
143 --pending_unlock_count_;
144 if (lock_info_it == lock_info_map_.end())
145 return;
146 DCHECK(!lock_info_it->second.socket);
147 LockInfo::WaiterQueue* queue = lock_info_it->second.queue.get();
148 DCHECK(queue);
149 if (queue->empty()) {
150 DVLOG(3) << "Unlocking endpoint " << lock_info_it->first.ToString();
151 lock_info_map_.erase(lock_info_it);
152 return;
155 DVLOG(3) << "Unlocking endpoint " << lock_info_it->first.ToString()
156 << " and activating next waiter";
157 Waiter* next_job = queue->head()->value();
158 next_job->RemoveFromList();
159 next_job->GotEndpointLock();
162 void WebSocketEndpointLockManager::EraseSocket(
163 LockInfoMap::iterator lock_info_it) {
164 DVLOG(3) << "Removing (StreamSocket*)" << lock_info_it->second.socket
165 << " for " << lock_info_it->first.ToString() << " ("
166 << socket_lock_info_map_.size() << " socket(s) left)";
167 size_t erased = socket_lock_info_map_.erase(lock_info_it->second.socket);
168 DCHECK_EQ(1U, erased);
169 lock_info_it->second.socket = NULL;
172 } // namespace net