Esempio n. 1
0
bool
io_service::async_write(_sock_t fd, const std::vector<char>& buffer, std::size_t write_size, const write_callback_t& callback) {
  std::lock_guard<std::recursive_mutex> lock(m_fds_mutex);

  __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service is requested async_write for fd #" + std::to_string(fd));

  auto reg_fd_it = m_fds.find(fd);
  if (reg_fd_it == m_fds.end()) {
    __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service does not track fd #" + std::to_string(fd));
    return false;
  }

  auto& reg_fd  = reg_fd_it->second;
  bool expected = false;
  if (!reg_fd.async_write.compare_exchange_strong(expected, true)) {
    __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service already doing async_write for fd #" + std::to_string(fd));
    return false;
  }

  reg_fd.write_buffer   = buffer;
  reg_fd.write_size     = write_size;
  reg_fd.write_callback = callback;

  notify_poll();

  return true;
}
		void
		redis_connection::connect(const std::string &host, std::size_t port,
		                          const disconnection_handler_t &client_disconnection_handler,
		                          const reply_callback_t &client_reply_callback,
		                          std::uint32_t timeout_ms) {
			try {
				__CPP_REDIS_LOG(debug, "cpp_redis::network::redis_connection attempts to connect");

/**
 * connect client
 */
				m_client->connect(host, (uint32_t) port, timeout_ms);
				m_client->set_on_disconnection_handler(std::bind(&redis_connection::tcp_client_disconnection_handler, this));

/**
 * start to read asynchronously
 */
				tcp_client_iface::read_request request = {__CPP_REDIS_READ_SIZE,
				                                          std::bind(&redis_connection::tcp_client_receive_handler, this,
				                                                    std::placeholders::_1)};
				m_client->async_read(request);

				__CPP_REDIS_LOG(debug, "cpp_redis::network::redis_connection connected");
			}
			catch (const std::exception &e) {
				__CPP_REDIS_LOG(error, std::string("cpp_redis::network::redis_connection ") + e.what());
				throw redis_error(e.what());
			}

			m_reply_callback = client_reply_callback;
			m_disconnection_handler = client_disconnection_handler;
		}
Esempio n. 3
0
void
io_service::write_fd(int fd) {
  std::unique_lock<std::recursive_mutex> lock(m_fds_mutex);

  __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service non-blocking write available for fd #" + std::to_string(fd));

  auto fd_it = m_fds.find(fd);
  if (fd_it == m_fds.end()) {
    __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service does not track fd #" + std::to_string(fd));
    return;
  }

  __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service writing data for fd #" + std::to_string(fd));
  int nb_bytes_written      = static_cast<int>(send(fd_it->first, fd_it->second.write_buffer.data(), fd_it->second.write_size, 0));
  fd_it->second.async_write = false;

  if (nb_bytes_written <= 0) {
    __CPP_REDIS_LOG(error, "cpp_redis::network::io_service write error for fd #" + std::to_string(fd));
    fd_it->second.disconnection_handler(*this);
    m_fds.erase(fd_it);
  }
  else {
    __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service calling write callback for fd #" + std::to_string(fd));

    fd_it->second.callback_running = true;
    lock.unlock();
    fd_it->second.write_callback(nb_bytes_written);
    fd_it->second.callback_running = false;
    fd_it->second.callback_notification.notify_all();
  }
}
Esempio n. 4
0
io_service::~io_service(void) {
  m_should_stop = true;
  notify_poll();

  __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service waiting for worker completion");
  m_worker.join();
  __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service finished to wait for worker completion");

  if (m_notif_pipe_fds[0] != -1)
    close(m_notif_pipe_fds[0]);
  if (m_notif_pipe_fds[1] != -1)
    close(m_notif_pipe_fds[1]);

  __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service destroyed");
}
		void
		redis_connection::call_disconnection_handler() {
			if (m_disconnection_handler) {
				__CPP_REDIS_LOG(debug, "cpp_redis::network::redis_connection calls disconnection handler");
				m_disconnection_handler(*this);
			}
		}
		redis_connection &
		redis_connection::send(const std::vector<std::string> &redis_cmd) {
			std::lock_guard<std::mutex> lock(m_buffer_mutex);

			m_buffer += build_command(redis_cmd);
			__CPP_REDIS_LOG(debug, "cpp_redis::network::redis_connection stored new command in the send buffer");

			return *this;
		}
Esempio n. 7
0
io_service::io_service(std::size_t nb_workers)
: network::io_service(nb_workers)
, m_should_stop(false)
, m_notif_pipe_fds{1, 1} {
  if (pipe(m_notif_pipe_fds) == -1) {
    __CPP_REDIS_LOG(error, "cpp_redis::network::io_service could not create pipe");
    throw cpp_redis::redis_error("Could not init cpp_redis::io_service, pipe() failure");
  }

  int flags = fcntl(m_notif_pipe_fds[1], F_GETFL, 0);
  if (flags == -1 || fcntl(m_notif_pipe_fds[1], F_SETFL, flags | O_NONBLOCK) == -1) {
    __CPP_REDIS_LOG(error, "cpp_redis::network::io_service could not configure pipe");
    throw cpp_redis::redis_error("Could not init cpp_redis::io_service, fcntl() failure");
  }

  m_worker = std::thread(&io_service::process_io, this);

  __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service created");
}
Esempio n. 8
0
void
io_service::process_io(void) {
  struct pollfd fds[_CPP_REDIS_MAX_NB_FDS];

  __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service starts poll loop in worker thread");

  while (!m_should_stop) {
    std::size_t nfds = init_sets(fds);

    if (poll(fds, static_cast<nfds_t>(nfds), -1) > 0) {
      __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service woke up by poll");
      process_sets(fds, nfds);
    }
    else {
      __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service woke up by poll, but nothing to process");
    }
  }

  __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service ends poll loop in worker thread");
}
		void
		redis_connection::disconnect(bool wait_for_removal) {
			__CPP_REDIS_LOG(debug, "cpp_redis::network::redis_connection attempts to disconnect");

/**
 * close connection
 */
			m_client->disconnect(wait_for_removal);

/**
 * clear buffer
 */
			m_buffer.clear();
/**
 * clear builder
 */
			m_builder.reset();

			__CPP_REDIS_LOG(debug, "cpp_redis::network::redis_connection disconnected");
		}
Esempio n. 10
0
void
io_service::untrack(_sock_t fd) {
  std::unique_lock<std::recursive_mutex> lock(m_fds_mutex);

  __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service requests to untrack fd #" + std::to_string(fd));

  auto fd_it = m_fds.find(fd);
  if (fd_it == m_fds.end()) {
    __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service does not track fd #" + std::to_string(fd));
    return;
  }

  if (fd_it->second.callback_running) {
    __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service waits for callbacks to complete before untracking fd #" + std::to_string(fd));
    fd_it->second.callback_notification.wait(lock, [=] { return !fd_it->second.callback_running; });
  }

  m_fds.erase(fd_it);

  __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service now untracks fd #" + std::to_string(fd));
}
		void
		redis_connection::tcp_client_receive_handler(const tcp_client_iface::read_result &result) {
			if (!result.success) { return; }

			try {
				__CPP_REDIS_LOG(debug, "cpp_redis::network::redis_connection receives packet, attempts to build reply");
				m_builder << std::string(result.buffer.begin(), result.buffer.end());
			}
			catch (const redis_error &) {
				__CPP_REDIS_LOG(error,
				                "cpp_redis::network::redis_connection could not build reply (invalid format), disconnecting");
				call_disconnection_handler();
				return;
			}

			while (m_builder.reply_available()) {
				__CPP_REDIS_LOG(debug, "cpp_redis::network::redis_connection reply fully built");

				auto reply = m_builder.get_front();
				m_builder.pop_front();

				if (m_reply_callback) {
					__CPP_REDIS_LOG(debug, "cpp_redis::network::redis_connection executes reply callback");
					m_reply_callback(*this, reply);
				}
			}

			try {
				tcp_client_iface::read_request request = {__CPP_REDIS_READ_SIZE,
				                                          std::bind(&redis_connection::tcp_client_receive_handler, this,
				                                                    std::placeholders::_1)};
				m_client->async_read(request);
			}
			catch (const std::exception &) {
/**
 * Client disconnected in the meantime
 */
			}
		}
Esempio n. 12
0
void
io_service::track(_sock_t fd, const disconnection_handler_t& handler) {
  std::lock_guard<std::recursive_mutex> lock(m_fds_mutex);

  auto& info                 = m_fds[fd];
  info.async_read            = false;
  info.async_write           = false;
  info.disconnection_handler = handler;

  __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service now tracks fd #" + std::to_string(fd));

  notify_poll();
}
Esempio n. 13
0
void
io_service::read_fd(int fd) {
  std::unique_lock<std::recursive_mutex> lock(m_fds_mutex);

  __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service non-blocking read available for fd #" + std::to_string(fd));

  auto fd_it = m_fds.find(fd);
  if (fd_it == m_fds.end()) {
    __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service does not track fd #" + std::to_string(fd));
    return;
  }

  auto& buffer             = *fd_it->second.read_buffer;
  int original_buffer_size = static_cast<int>(buffer.size());
  buffer.resize(original_buffer_size + fd_it->second.read_size);

  __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service reading data for fd #" + std::to_string(fd));
  int nb_bytes_read        = static_cast<int>(recv(fd_it->first, buffer.data() + original_buffer_size, fd_it->second.read_size, 0));
  fd_it->second.async_read = false;

  if (nb_bytes_read <= 0) {
    __CPP_REDIS_LOG(error, "cpp_redis::network::io_service read error for fd #" + std::to_string(fd));
    buffer.resize(original_buffer_size);
    fd_it->second.disconnection_handler(*this);
    m_fds.erase(fd_it);
  }
  else {
    buffer.resize(original_buffer_size + nb_bytes_read);

    __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service calling read callback for fd #" + std::to_string(fd));

    fd_it->second.callback_running = true;
    lock.unlock();
    fd_it->second.read_callback(nb_bytes_read);
    fd_it->second.callback_running = false;
    fd_it->second.callback_notification.notify_all();
  }
}
		/**
		 * commit pipelined transaction
		 */
		redis_connection &
		redis_connection::commit() {
			std::lock_guard<std::mutex> lock(m_buffer_mutex);

			/**
			 * ensure buffer is cleared
			 */
			__CPP_REDIS_LOG(debug, "cpp_redis::network::redis_connection attempts to send pipelined commands");
			std::string buffer = std::move(m_buffer);

			try {
				tcp_client_iface::write_request request = {std::vector<char>{buffer.begin(), buffer.end()}, nullptr};
				m_client->async_write(request);
			}
			catch (const std::exception &e) {
				__CPP_REDIS_LOG(error, std::string("cpp_redis::network::redis_connection ") + e.what());
				throw redis_error(e.what());
			}

			__CPP_REDIS_LOG(debug, "cpp_redis::network::redis_connection sent pipelined commands");

			return *this;
		}
		void
		redis_connection::tcp_client_disconnection_handler() {
			__CPP_REDIS_LOG(debug, "cpp_redis::network::redis_connection has been disconnected");
/**
 * clear buffer
 */
			m_buffer.clear();
/**
 * clear builder
 */
			m_builder.reset();
/**
 * call disconnection handler
 */
			call_disconnection_handler();
		}
Esempio n. 16
0
void
io_service::notify_poll(void) {
  __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service notifies poll to wake up");
  (void) write(m_notif_pipe_fds[1], "a", 1);
}
		redis_connection::redis_connection(const std::shared_ptr<tcp_client_iface> &client)
				: m_client(client), m_reply_callback(nullptr), m_disconnection_handler(nullptr) {
			__CPP_REDIS_LOG(debug, "cpp_redis::network::redis_connection created");
		}
		redis_connection::~redis_connection() {
			m_client->disconnect(true);
			__CPP_REDIS_LOG(debug, "cpp_redis::network::redis_connection destroyed");
		}