| // Copyright 2021 The Chromium Authors | 
 | // 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 |