profiling: Add SocketPool.

Change-Id: I3bda8969ee71df2d47eb6e7f306a530bc388af27
diff --git a/Android.bp b/Android.bp
index 9ab67fd..0915ccf 100644
--- a/Android.bp
+++ b/Android.bp
@@ -3693,6 +3693,10 @@
     "src/perfetto_cmd/perfetto_cmd.cc",
     "src/perfetto_cmd/rate_limiter.cc",
     "src/perfetto_cmd/rate_limiter_unittest.cc",
+    "src/profiling/memory/bookkeeping.cc",
+    "src/profiling/memory/bookkeeping_unittest.cc",
+    "src/profiling/memory/client.cc",
+    "src/profiling/memory/client_unittest.cc",
     "src/profiling/memory/record_reader.cc",
     "src/profiling/memory/record_reader_unittest.cc",
     "src/profiling/memory/socket_listener.cc",
diff --git a/src/profiling/memory/BUILD.gn b/src/profiling/memory/BUILD.gn
index e70f7a7..0ccc573 100644
--- a/src/profiling/memory/BUILD.gn
+++ b/src/profiling/memory/BUILD.gn
@@ -86,6 +86,22 @@
   ]
 }
 
+source_set("client") {
+  public_configs = [
+    "../../../gn:default_config",
+    "../../../buildtools:libunwindstack_config",
+  ]
+  deps = [
+    "../../../buildtools:libunwindstack",
+    "../../../gn:default_deps",
+    "../../base",
+  ]
+  sources = [
+    "client.cc",
+    "client.h",
+  ]
+}
+
 source_set("unittests") {
   public_configs = [
     "../../../gn:default_config",
@@ -94,6 +110,7 @@
   testonly = true
   deps = [
     ":bookkeeping",
+    ":client",
     ":record_reader",
     ":socket_listener",
     ":string_interner",
@@ -104,6 +121,7 @@
   ]
   sources = [
     "bookkeeping_unittest.cc",
+    "client_unittest.cc",
     "record_reader_unittest.cc",
     "socket_listener_unittest.cc",
     "string_interner_unittest.cc",
diff --git a/src/profiling/memory/client.cc b/src/profiling/memory/client.cc
new file mode 100644
index 0000000..d0e8f61
--- /dev/null
+++ b/src/profiling/memory/client.cc
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/profiling/memory/client.h"
+
+#include <inttypes.h>
+#include <sys/socket.h>
+
+#include <atomic>
+
+#include "perfetto/base/logging.h"
+#include "perfetto/base/utils.h"
+#include "src/profiling/memory/transport_data.h"
+
+namespace perfetto {
+
+BorrowedSocket::BorrowedSocket(base::ScopedFile fd, SocketPool* socket_pool)
+    : fd_(std::move(fd)), socket_pool_(socket_pool) {}
+
+int BorrowedSocket::operator*() {
+  return get();
+}
+
+int BorrowedSocket::get() {
+  return *fd_;
+}
+
+void BorrowedSocket::Close() {
+  fd_.reset();
+}
+
+BorrowedSocket::~BorrowedSocket() {
+  if (socket_pool_ != nullptr)
+    socket_pool_->Return(std::move(fd_));
+}
+
+SocketPool::SocketPool(std::vector<base::ScopedFile> sockets)
+    : sockets_(std::move(sockets)), available_sockets_(sockets_.size()) {}
+
+BorrowedSocket SocketPool::Borrow() {
+  std::unique_lock<std::mutex> lck_(mtx_);
+  if (available_sockets_ == 0)
+    cv_.wait(lck_, [this] { return available_sockets_ > 0; });
+  PERFETTO_CHECK(available_sockets_ > 0);
+  return {std::move(sockets_[--available_sockets_]), this};
+}
+
+void SocketPool::Return(base::ScopedFile sock) {
+  std::unique_lock<std::mutex> lck_(mtx_);
+  PERFETTO_CHECK(available_sockets_ < sockets_.size());
+  sockets_[available_sockets_++] = std::move(sock);
+  if (available_sockets_ == 1) {
+    lck_.unlock();
+    cv_.notify_one();
+  }
+}
+
+}  // namespace perfetto
diff --git a/src/profiling/memory/client.h b/src/profiling/memory/client.h
new file mode 100644
index 0000000..a3aa5c6
--- /dev/null
+++ b/src/profiling/memory/client.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SRC_PROFILING_MEMORY_CLIENT_H_
+#define SRC_PROFILING_MEMORY_CLIENT_H_
+
+#include <stddef.h>
+#include <mutex>
+#include <vector>
+
+#include "perfetto/base/scoped_file.h"
+
+namespace perfetto {
+
+class SocketPool;
+
+class BorrowedSocket {
+ public:
+  BorrowedSocket(const BorrowedSocket&) = delete;
+  BorrowedSocket& operator=(const BorrowedSocket&) = delete;
+  BorrowedSocket(BorrowedSocket&& other) {
+    fd_ = std::move(other.fd_);
+    socket_pool_ = other.socket_pool_;
+    other.socket_pool_ = nullptr;
+  }
+
+  BorrowedSocket(base::ScopedFile fd, SocketPool* socket_pool);
+  int operator*();
+  int get();
+  void Close();
+  ~BorrowedSocket();
+
+ private:
+  base::ScopedFile fd_;
+  SocketPool* socket_pool_ = nullptr;
+};
+
+class SocketPool {
+ public:
+  friend class BorrowedSocket;
+  SocketPool(std::vector<base::ScopedFile> sockets);
+
+  BorrowedSocket Borrow();
+
+ private:
+  void Return(base::ScopedFile fd);
+  std::mutex mtx_;
+  std::condition_variable cv_;
+  std::vector<base::ScopedFile> sockets_;
+  size_t available_sockets_;
+};
+
+}  // namespace perfetto
+
+#endif  // SRC_PROFILING_MEMORY_CLIENT_H_
diff --git a/src/profiling/memory/client_unittest.cc b/src/profiling/memory/client_unittest.cc
new file mode 100644
index 0000000..a69bae3
--- /dev/null
+++ b/src/profiling/memory/client_unittest.cc
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/profiling/memory/client.h"
+
+#include "gtest/gtest.h"
+
+#include <thread>
+
+namespace perfetto {
+namespace {
+
+TEST(SocketPoolTest, Basic) {
+  std::vector<base::ScopedFile> files;
+  files.emplace_back(open("/dev/null", O_RDONLY));
+  SocketPool pool(std::move(files));
+  BorrowedSocket sock = pool.Borrow();
+}
+
+TEST(SocketPoolTest, Multiple) {
+  std::vector<base::ScopedFile> files;
+  files.emplace_back(open("/dev/null", O_RDONLY));
+  files.emplace_back(open("/dev/null", O_RDONLY));
+  SocketPool pool(std::move(files));
+  BorrowedSocket sock = pool.Borrow();
+  BorrowedSocket sock_2 = pool.Borrow();
+}
+
+TEST(SocketPoolTest, Blocked) {
+  std::vector<base::ScopedFile> files;
+  files.emplace_back(open("/dev/null", O_RDONLY));
+  SocketPool pool(std::move(files));
+  BorrowedSocket sock = pool.Borrow();
+  std::thread t([&pool] { pool.Borrow(); });
+  {
+    // Return fd to unblock thread.
+    BorrowedSocket temp = std::move(sock);
+  }
+  t.join();
+}
+
+TEST(SocketPoolTest, MultipleBlocked) {
+  std::vector<base::ScopedFile> files;
+  files.emplace_back(open("/dev/null", O_RDONLY));
+  SocketPool pool(std::move(files));
+  BorrowedSocket sock = pool.Borrow();
+  std::thread t([&pool] { pool.Borrow(); });
+  std::thread t2([&pool] { pool.Borrow(); });
+  {
+    // Return fd to unblock thread.
+    BorrowedSocket temp = std::move(sock);
+  }
+  t.join();
+  t2.join();
+}
+
+}  // namespace
+}  // namespace perfetto