| // Copyright 2021 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "printing/backend/cups_connection_pool.h" |
| |
| #include <cups/cups.h> |
| |
| #include "base/check.h" |
| #include "base/containers/queue.h" |
| #include "base/logging.h" |
| #include "base/sequence_checker.h" |
| #include "printing/backend/cups_connection.h" |
| #include "printing/backend/cups_deleters.h" |
| #include "printing/backend/cups_helper.h" |
| |
| namespace printing { |
| |
| namespace { |
| |
| // There needs to be a separate connection for each thread which will connect to |
| // the CUPS server. While the CUPS library is thread safe, a connection cannot |
| // be shared between threads. |
| // Issuing multiple print jobs concurrently to a single destination can be |
| // be queued up for submission across the same connection (they would be |
| // serialized at the device anyway). There is benefit to supporting concurrent |
| // printing to different destinations. It is unlikely that this would be done |
| // to more than a handful of destinations at once. Pick a somewhat arbitrary |
| // low number for the number of allowed connections, knowing that one of these |
| // connections will be needed by PrintBackend. If a user were to swamp printing |
| // with jobs to more unique destinations at the same time then the PrintBackend |
| // service could be made to throttle those and queue them up awaiting a |
| // connection to use. |
| constexpr int kNumCupsConnections = 5; |
| |
| CupsConnectionPool* g_cups_connection_pool_singleton = nullptr; |
| |
| } // namespace |
| |
| CupsConnectionPool::CupsConnectionPool() = default; |
| |
| CupsConnectionPool::~CupsConnectionPool() = default; |
| |
| // static |
| void CupsConnectionPool::Create() { |
| DCHECK(!g_cups_connection_pool_singleton); |
| |
| // The pool is only used for connections to default server over the default |
| // IPP port. These connections are never closed; they are reused until the |
| // the process terminates. |
| const int port = ippPort(); |
| const char* server = cupsServer(); |
| g_cups_connection_pool_singleton = new CupsConnectionPool(); |
| VLOG(1) << "Creating CUPS connection pool seeded with " << kNumCupsConnections |
| << " connections"; |
| for (auto i = 0; i < kNumCupsConnections; ++i) { |
| ScopedHttpPtr connection(httpConnect2( |
| server, port, /*addrlist=*/nullptr, AF_UNSPEC, HTTP_ENCRYPTION_NEVER, |
| /*blocking*/ 0, kCupsTimeoutMs, /*cancel=*/nullptr)); |
| if (!connection) { |
| LOG(ERROR) << "Unable to create CUPS connection: " |
| << cupsLastErrorString(); |
| break; |
| } |
| g_cups_connection_pool_singleton->AddConnection(std::move(connection)); |
| } |
| } |
| |
| // static |
| CupsConnectionPool* CupsConnectionPool::GetInstance() { |
| return g_cups_connection_pool_singleton; |
| } |
| |
| ScopedHttpPtr CupsConnectionPool::TakeConnection() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (connections_.empty()) |
| return nullptr; |
| |
| ScopedHttpPtr connection = std::move(connections_.front()); |
| connections_.pop(); |
| return connection; |
| } |
| |
| void CupsConnectionPool::AddConnection(ScopedHttpPtr connection) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| DCHECK(connection); |
| connections_.emplace(std::move(connection)); |
| } |
| |
| } // namespace printing |