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.
4
5#include "net/socket/websocket_endpoint_lock_manager.h"
6
7#include <utility>
8
9#include "base/logging.h"
10#include "net/base/net_errors.h"
11#include "net/base/net_log.h"
12
13namespace net {
14
15WebSocketEndpointLockManager::Waiter::~Waiter() {
16  if (next()) {
17    DCHECK(previous());
18    RemoveFromList();
19  }
20}
21
22WebSocketEndpointLockManager* WebSocketEndpointLockManager::GetInstance() {
23  return Singleton<WebSocketEndpointLockManager>::get();
24}
25
26int WebSocketEndpointLockManager::LockEndpoint(const IPEndPoint& endpoint,
27                                               Waiter* waiter) {
28  LockInfoMap::value_type insert_value(endpoint, LockInfo());
29  std::pair<LockInfoMap::iterator, bool> rv =
30      lock_info_map_.insert(insert_value);
31  LockInfo& lock_info_in_map = rv.first->second;
32  if (rv.second) {
33    DVLOG(3) << "Locking endpoint " << endpoint.ToString();
34    lock_info_in_map.queue.reset(new LockInfo::WaiterQueue);
35    return OK;
36  }
37  DVLOG(3) << "Waiting for endpoint " << endpoint.ToString();
38  lock_info_in_map.queue->Append(waiter);
39  return ERR_IO_PENDING;
40}
41
42void WebSocketEndpointLockManager::RememberSocket(StreamSocket* socket,
43                                                  const IPEndPoint& endpoint) {
44  LockInfoMap::iterator lock_info_it = lock_info_map_.find(endpoint);
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;
49  DCHECK(inserted);
50  DCHECK(!lock_info_it->second.socket);
51  lock_info_it->second.socket = socket;
52  DVLOG(3) << "Remembered (StreamSocket*)" << socket << " for "
53           << endpoint.ToString() << " (" << socket_lock_info_map_.size()
54           << " socket(s) remembered)";
55}
56
57void WebSocketEndpointLockManager::UnlockSocket(StreamSocket* socket) {
58  SocketLockInfoMap::iterator socket_it = socket_lock_info_map_.find(socket);
59  if (socket_it == socket_lock_info_map_.end())
60    return;
61
62  LockInfoMap::iterator lock_info_it = socket_it->second;
63
64  DVLOG(3) << "Unlocking (StreamSocket*)" << socket << " for "
65           << lock_info_it->first.ToString() << " ("
66           << socket_lock_info_map_.size() << " socket(s) left)";
67  socket_lock_info_map_.erase(socket_it);
68  DCHECK(socket == lock_info_it->second.socket);
69  lock_info_it->second.socket = NULL;
70  UnlockEndpointByIterator(lock_info_it);
71}
72
73void WebSocketEndpointLockManager::UnlockEndpoint(const IPEndPoint& endpoint) {
74  LockInfoMap::iterator lock_info_it = lock_info_map_.find(endpoint);
75  if (lock_info_it == lock_info_map_.end())
76    return;
77
78  UnlockEndpointByIterator(lock_info_it);
79}
80
81bool WebSocketEndpointLockManager::IsEmpty() const {
82  return lock_info_map_.empty() && socket_lock_info_map_.empty();
83}
84
85WebSocketEndpointLockManager::LockInfo::LockInfo() : socket(NULL) {}
86WebSocketEndpointLockManager::LockInfo::~LockInfo() {
87  DCHECK(!socket);
88}
89
90WebSocketEndpointLockManager::LockInfo::LockInfo(const LockInfo& rhs)
91    : socket(rhs.socket) {
92  DCHECK(!rhs.queue);
93}
94
95WebSocketEndpointLockManager::WebSocketEndpointLockManager() {}
96
97WebSocketEndpointLockManager::~WebSocketEndpointLockManager() {
98  DCHECK(lock_info_map_.empty());
99  DCHECK(socket_lock_info_map_.empty());
100}
101
102void 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
122void 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;
130}
131
132}  // namespace net
133