OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "net/socket/websocket_endpoint_lock_manager.h" | 5 #include "net/socket/websocket_endpoint_lock_manager.h" |
6 | 6 |
7 #include <utility> | 7 #include <utility> |
8 | 8 |
9 #include "base/logging.h" | 9 #include "base/logging.h" |
10 #include "net/base/net_errors.h" | 10 #include "net/base/net_errors.h" |
11 #include "net/base/net_log.h" | 11 #include "net/base/net_log.h" |
12 | 12 |
13 namespace net { | 13 namespace net { |
14 | 14 |
15 WebSocketEndpointLockManager::Waiter::~Waiter() { | 15 WebSocketEndpointLockManager::Waiter::~Waiter() { |
16 if (next()) { | 16 if (next()) { |
17 DCHECK(previous()); | 17 DCHECK(previous()); |
18 RemoveFromList(); | 18 RemoveFromList(); |
19 } | 19 } |
20 } | 20 } |
21 | 21 |
22 WebSocketEndpointLockManager* WebSocketEndpointLockManager::GetInstance() { | 22 WebSocketEndpointLockManager* WebSocketEndpointLockManager::GetInstance() { |
23 return Singleton<WebSocketEndpointLockManager>::get(); | 23 return Singleton<WebSocketEndpointLockManager>::get(); |
24 } | 24 } |
25 | 25 |
26 int WebSocketEndpointLockManager::LockEndpoint(const IPEndPoint& endpoint, | 26 int WebSocketEndpointLockManager::LockEndpoint(const IPEndPoint& endpoint, |
27 Waiter* waiter) { | 27 Waiter* waiter) { |
28 EndPointWaiterMap::value_type insert_value(endpoint, NULL); | 28 LockInfoMap::value_type insert_value(endpoint, LockInfo()); |
29 std::pair<EndPointWaiterMap::iterator, bool> rv = | 29 std::pair<LockInfoMap::iterator, bool> rv = |
30 endpoint_waiter_map_.insert(insert_value); | 30 lock_info_map_.insert(insert_value); |
| 31 LockInfo& lock_info_in_map = rv.first->second; |
31 if (rv.second) { | 32 if (rv.second) { |
32 DVLOG(3) << "Locking endpoint " << endpoint.ToString(); | 33 DVLOG(3) << "Locking endpoint " << endpoint.ToString(); |
33 rv.first->second = new ConnectJobQueue; | 34 lock_info_in_map.queue.reset(new LockInfo::WaiterQueue); |
34 return OK; | 35 return OK; |
35 } | 36 } |
36 DVLOG(3) << "Waiting for endpoint " << endpoint.ToString(); | 37 DVLOG(3) << "Waiting for endpoint " << endpoint.ToString(); |
37 rv.first->second->Append(waiter); | 38 lock_info_in_map.queue->Append(waiter); |
38 return ERR_IO_PENDING; | 39 return ERR_IO_PENDING; |
39 } | 40 } |
40 | 41 |
41 void WebSocketEndpointLockManager::RememberSocket(StreamSocket* socket, | 42 void WebSocketEndpointLockManager::RememberSocket(StreamSocket* socket, |
42 const IPEndPoint& endpoint) { | 43 const IPEndPoint& endpoint) { |
43 bool inserted = socket_endpoint_map_.insert(SocketEndPointMap::value_type( | 44 LockInfoMap::iterator lock_info_it = lock_info_map_.find(endpoint); |
44 socket, endpoint)).second; | 45 CHECK(lock_info_it != lock_info_map_.end()); |
| 46 bool inserted = |
| 47 socket_lock_info_map_.insert(SocketLockInfoMap::value_type( |
| 48 socket, lock_info_it)).second; |
45 DCHECK(inserted); | 49 DCHECK(inserted); |
46 DCHECK(endpoint_waiter_map_.find(endpoint) != endpoint_waiter_map_.end()); | 50 DCHECK(!lock_info_it->second.socket); |
| 51 lock_info_it->second.socket = socket; |
47 DVLOG(3) << "Remembered (StreamSocket*)" << socket << " for " | 52 DVLOG(3) << "Remembered (StreamSocket*)" << socket << " for " |
48 << endpoint.ToString() << " (" << socket_endpoint_map_.size() | 53 << endpoint.ToString() << " (" << socket_lock_info_map_.size() |
49 << " sockets remembered)"; | 54 << " socket(s) remembered)"; |
50 } | 55 } |
51 | 56 |
52 void WebSocketEndpointLockManager::UnlockSocket(StreamSocket* socket) { | 57 void WebSocketEndpointLockManager::UnlockSocket(StreamSocket* socket) { |
53 SocketEndPointMap::iterator socket_it = socket_endpoint_map_.find(socket); | 58 SocketLockInfoMap::iterator socket_it = socket_lock_info_map_.find(socket); |
54 if (socket_it == socket_endpoint_map_.end()) { | 59 if (socket_it == socket_lock_info_map_.end()) |
55 DVLOG(3) << "Ignoring request to unlock already-unlocked socket" | |
56 "(StreamSocket*)" << socket; | |
57 return; | 60 return; |
58 } | 61 |
59 const IPEndPoint& endpoint = socket_it->second; | 62 LockInfoMap::iterator lock_info_it = socket_it->second; |
| 63 |
60 DVLOG(3) << "Unlocking (StreamSocket*)" << socket << " for " | 64 DVLOG(3) << "Unlocking (StreamSocket*)" << socket << " for " |
61 << endpoint.ToString() << " (" << socket_endpoint_map_.size() | 65 << lock_info_it->first.ToString() << " (" |
62 << " sockets left)"; | 66 << socket_lock_info_map_.size() << " socket(s) left)"; |
63 UnlockEndpoint(endpoint); | 67 socket_lock_info_map_.erase(socket_it); |
64 socket_endpoint_map_.erase(socket_it); | 68 DCHECK(socket == lock_info_it->second.socket); |
| 69 lock_info_it->second.socket = NULL; |
| 70 UnlockEndpointByIterator(lock_info_it); |
65 } | 71 } |
66 | 72 |
67 void WebSocketEndpointLockManager::UnlockEndpoint(const IPEndPoint& endpoint) { | 73 void WebSocketEndpointLockManager::UnlockEndpoint(const IPEndPoint& endpoint) { |
68 EndPointWaiterMap::iterator found_it = endpoint_waiter_map_.find(endpoint); | 74 LockInfoMap::iterator lock_info_it = lock_info_map_.find(endpoint); |
69 CHECK(found_it != endpoint_waiter_map_.end()); // Security critical | 75 if (lock_info_it == lock_info_map_.end()) |
70 ConnectJobQueue* queue = found_it->second; | 76 return; |
71 if (queue->empty()) { | 77 |
72 DVLOG(3) << "Unlocking endpoint " << endpoint.ToString(); | 78 UnlockEndpointByIterator(lock_info_it); |
73 delete queue; | |
74 endpoint_waiter_map_.erase(found_it); | |
75 } else { | |
76 DVLOG(3) << "Unlocking endpoint " << endpoint.ToString() | |
77 << " and activating next waiter"; | |
78 Waiter* next_job = queue->head()->value(); | |
79 next_job->RemoveFromList(); | |
80 next_job->GotEndpointLock(); | |
81 } | |
82 } | 79 } |
83 | 80 |
84 bool WebSocketEndpointLockManager::IsEmpty() const { | 81 bool WebSocketEndpointLockManager::IsEmpty() const { |
85 return endpoint_waiter_map_.empty() && socket_endpoint_map_.empty(); | 82 return lock_info_map_.empty() && socket_lock_info_map_.empty(); |
| 83 } |
| 84 |
| 85 WebSocketEndpointLockManager::LockInfo::LockInfo() : socket(NULL) {} |
| 86 WebSocketEndpointLockManager::LockInfo::~LockInfo() { |
| 87 DCHECK(!socket); |
| 88 } |
| 89 |
| 90 WebSocketEndpointLockManager::LockInfo::LockInfo(const LockInfo& rhs) |
| 91 : socket(rhs.socket) { |
| 92 DCHECK(!rhs.queue); |
86 } | 93 } |
87 | 94 |
88 WebSocketEndpointLockManager::WebSocketEndpointLockManager() {} | 95 WebSocketEndpointLockManager::WebSocketEndpointLockManager() {} |
89 | 96 |
90 WebSocketEndpointLockManager::~WebSocketEndpointLockManager() { | 97 WebSocketEndpointLockManager::~WebSocketEndpointLockManager() { |
91 DCHECK(endpoint_waiter_map_.empty()); | 98 DCHECK(lock_info_map_.empty()); |
92 DCHECK(socket_endpoint_map_.empty()); | 99 DCHECK(socket_lock_info_map_.empty()); |
| 100 } |
| 101 |
| 102 void WebSocketEndpointLockManager::UnlockEndpointByIterator( |
| 103 LockInfoMap::iterator lock_info_it) { |
| 104 if (lock_info_it->second.socket) |
| 105 EraseSocket(lock_info_it); |
| 106 LockInfo::WaiterQueue* queue = lock_info_it->second.queue.get(); |
| 107 DCHECK(queue); |
| 108 if (queue->empty()) { |
| 109 DVLOG(3) << "Unlocking endpoint " << lock_info_it->first.ToString(); |
| 110 lock_info_map_.erase(lock_info_it); |
| 111 return; |
| 112 } |
| 113 |
| 114 DVLOG(3) << "Unlocking endpoint " << lock_info_it->first.ToString() |
| 115 << " and activating next waiter"; |
| 116 Waiter* next_job = queue->head()->value(); |
| 117 next_job->RemoveFromList(); |
| 118 // This must be last to minimise the excitement caused by re-entrancy. |
| 119 next_job->GotEndpointLock(); |
| 120 } |
| 121 |
| 122 void WebSocketEndpointLockManager::EraseSocket( |
| 123 LockInfoMap::iterator lock_info_it) { |
| 124 DVLOG(3) << "Removing (StreamSocket*)" << lock_info_it->second.socket |
| 125 << " for " << lock_info_it->first.ToString() << " (" |
| 126 << socket_lock_info_map_.size() << " socket(s) left)"; |
| 127 size_t erased = socket_lock_info_map_.erase(lock_info_it->second.socket); |
| 128 DCHECK_EQ(1U, erased); |
| 129 lock_info_it->second.socket = NULL; |
93 } | 130 } |
94 | 131 |
95 } // namespace net | 132 } // namespace net |
OLD | NEW |