XRootD
Loading...
Searching...
No Matches
XrdClHttp::HandlerQueue Class Reference

#include <XrdClHttpUtil.hh>

Collaboration diagram for XrdClHttp::HandlerQueue:

Public Member Functions

 HandlerQueue (unsigned max_pending_ops)
std::shared_ptr< CurlOperation > Consume (std::chrono::steady_clock::duration)
void Expire ()
CURL * GetHandle ()
int PollFD () const
void Produce (std::shared_ptr< CurlOperation > handler)
void RecycleHandle (CURL *)
void ReleaseHandles ()
void Shutdown ()
std::shared_ptr< CurlOperation > TryConsume ()

Static Public Member Functions

static unsigned GetDefaultMaxPendingOps ()
static std::string GetMonitoringJson ()

Detailed Description

HandlerQueue is a deque of curl operations that need to be performed. The object is thread safe and can be waited on via poll().

The fact that it's poll'able is necessary because the multi-curl driver thread is based on polling FD's

Definition at line 196 of file XrdClHttpUtil.hh.

Constructor & Destructor Documentation

◆ HandlerQueue()

HandlerQueue::HandlerQueue ( unsigned max_pending_ops)

Definition at line 697 of file XrdClHttpUtil.cc.

697 :
698 m_max_pending_ops(max_pending_ops)
699{
700 int filedes[2];
701 auto result = pipe(filedes);
702 if (result == -1) {
703 throw std::system_error(errno, std::generic_category(),
704 "failed to create HTTP worker pipe");
705 }
706 if (fcntl(filedes[0], F_SETFL, O_NONBLOCK | O_CLOEXEC) == -1 || fcntl(filedes[1], F_SETFL, O_NONBLOCK | O_CLOEXEC) == -1) {
707 const int error = errno;
708 close(filedes[0]);
709 close(filedes[1]);
710 throw std::system_error(error, std::generic_category(),
711 "failed to configure HTTP worker pipe");
712 }
713 m_read_fd = filedes[0];
714 m_write_fd = filedes[1];
715};
#define close(a)
Definition XrdPosix.hh:48

References close.

Member Function Documentation

◆ Consume()

std::shared_ptr< CurlOperation > HandlerQueue::Consume ( std::chrono::steady_clock::duration dur)

Definition at line 939 of file XrdClHttpUtil.cc.

940{
941 std::unique_lock<std::mutex> lk(m_mutex);
942 m_consumer_cv.wait_for(lk, dur, [&]{return m_ops.size() > 0 || m_shutdown;});
943 if (m_shutdown || m_ops.empty()) {
944 return {};
945 }
946
947 std::shared_ptr<CurlOperation> result = m_ops.front();
948 m_ops.pop_front();
949
950 char ready[1];
951 while (true) {
952 auto result = read(m_read_fd, ready, 1);
953 if (result == -1) {
954 if (errno == EINTR) {
955 continue;
956 } else if (errno == EAGAIN || errno == EWOULDBLOCK) {
957 // This should never happen, but if it does, just continue
958 // as if we successfully read the byte.
959 break;
960 }
961 throw std::system_error(errno, std::generic_category(),
962 "failed to consume HTTP worker notification");
963 }
964 break;
965 }
966
967 lk.unlock();
968 m_producer_cv.notify_one();
969 m_ops_consumed.fetch_add(1, std::memory_order_relaxed);
970
971 return result;
972}
#define read(a, b, c)
Definition XrdPosix.hh:86

References read.

◆ Expire()

void HandlerQueue::Expire ( )

Definition at line 837 of file XrdClHttpUtil.cc.

838{
839 std::unique_lock<std::mutex> lk(m_mutex);
840 auto now = std::chrono::steady_clock::now();
841
842 // Iterate through the paused transfers, checking if they are done.
843 for (auto &op : m_ops) {
844 if (!op->IsPaused()) continue;
845
846 if (op->TransferStalled(0, now)) {
847 op->ContinueHandle();
848 }
849 }
850
851 std::vector<decltype(m_ops)::value_type> expired_ops;
852 unsigned expired_count = 0;
853 auto it = std::remove_if(m_ops.begin(), m_ops.end(),
854 [&](const std::shared_ptr<CurlOperation> &handler) {
855 auto expired = handler->GetOperationExpiry() < now;
856 if (expired) {
857 expired_ops.push_back(handler);
858 expired_count++;
859 }
860 return expired;
861 });
862 m_ops.erase(it, m_ops.end());
863
864 // The contents of our pipe and the in-memory queue are now off by expired_count.
865 // Read exactly that many bytes from the pipe and throw them away.
866 char throwaway[64];
867 unsigned bytes_to_read = expired_count;
868 while (bytes_to_read > 0) {
869 size_t chunk = std::min<size_t>(sizeof(throwaway), bytes_to_read);
870 ssize_t n = read(m_read_fd, throwaway, chunk);
871 if (n > 0) {
872 bytes_to_read -= n;
873 } else if (n == -1) {
874 if (errno == EINTR) {
875 continue;
876 } else {
877 // EWOULDBLOCK is a possibility if there's a synchronization error;
878 // for now, just continue on as if we were successful in reading out
879 // the missing bytes
880 break;
881 }
882 } else {
883 break;
884 }
885 }
886
887 // Note: the failure handler may trigger new operations submitted to the queue
888 // (which requires the lock to be held) such as a prefetch operation that gets split
889 // into multiple sub-operations.
890 //
891 // Thus, we must unlock the mutex protecting the queue and avoid touching the shared state of
892 // m_ops.
893 lk.unlock();
894 for (auto &handler : expired_ops) {
895 if (handler) handler->Fail(XrdCl::errOperationExpired, 0, "Operation expired while in queue");
896 }
897}
const uint16_t errOperationExpired

◆ GetDefaultMaxPendingOps()

unsigned XrdClHttp::HandlerQueue::GetDefaultMaxPendingOps ( )
inlinestatic

Definition at line 223 of file XrdClHttpUtil.hh.

223{return m_default_max_pending_ops;}

◆ GetHandle()

CURL * HandlerQueue::GetHandle ( )

Definition at line 821 of file XrdClHttpUtil.cc.

821 {
822 if (m_handles.size()) {
823 auto result = m_handles.back();
824 m_handles.pop_back();
825 return result;
826 }
827
828 return ::GetHandle(EnableCurlHeaderDump());
829}

◆ GetMonitoringJson()

std::string HandlerQueue::GetMonitoringJson ( )
static

Definition at line 975 of file XrdClHttpUtil.cc.

976{
977 auto consumed = m_ops_consumed.load(std::memory_order_relaxed);
978 auto produced = m_ops_produced.load(std::memory_order_relaxed);
979 return "{"
980 "\"produced\":" + std::to_string(produced) + ","
981 "\"consumed\":" + std::to_string(consumed) + ","
982 "\"pending\":" + std::to_string(produced - consumed) + ","
983 "\"rejected\":" + std::to_string(m_ops_rejected.load(std::memory_order_relaxed)) +
984 "}";
985}

◆ PollFD()

int XrdClHttp::HandlerQueue::PollFD ( ) const
inline

Definition at line 205 of file XrdClHttpUtil.hh.

205{return m_read_fd;}

◆ Produce()

void HandlerQueue::Produce ( std::shared_ptr< CurlOperation > handler)

Definition at line 900 of file XrdClHttpUtil.cc.

901{
902 auto handler_expiry = handler->GetOperationExpiry();
903 std::unique_lock<std::mutex> lk{m_mutex};
904 m_producer_cv.wait_until(lk,
905 handler_expiry,
906 [&]{return m_ops.size() < m_max_pending_ops;}
907 );
908 if (std::chrono::steady_clock::now() > handler_expiry) {
909 lk.unlock();
910 handler->Fail(XrdCl::errOperationExpired, 0, "Operation expired while waiting for worker");
911 m_ops_rejected.fetch_add(1, std::memory_order_relaxed);
912 return;
913 }
914
915 m_ops.push_back(handler);
916 char ready[] = "1";
917 while (true) {
918 auto result = write(m_write_fd, ready, 1);
919 if (result == -1) {
920 if (errno == EINTR) {
921 continue;
922 } else if (errno == EAGAIN || errno == EWOULDBLOCK) {
923 // This should never happen, but if it does, just continue
924 // as if we successfully wrote the notification to the pipe.
925 break;
926 }
927 throw std::system_error(errno, std::generic_category(),
928 "failed to notify HTTP worker");
929 }
930 break;
931 }
932
933 lk.unlock();
934 m_consumer_cv.notify_one();
935 m_ops_produced.fetch_add(1, std::memory_order_relaxed);
936}
#define write(a, b, c)
Definition XrdPosix.hh:121

References XrdCl::errOperationExpired, and write.

Referenced by XrdClHttp::stat_size().

Here is the caller graph for this function:

◆ RecycleHandle()

void HandlerQueue::RecycleHandle ( CURL * curl)

Definition at line 832 of file XrdClHttpUtil.cc.

832 {
833 m_handles.push_back(curl);
834}

◆ ReleaseHandles()

void HandlerQueue::ReleaseHandles ( )

Definition at line 1032 of file XrdClHttpUtil.cc.

1033{
1034 for (auto handle : m_handles) {
1035 curl_easy_cleanup(handle);
1036 }
1037 m_handles.clear();
1038}

◆ Shutdown()

void HandlerQueue::Shutdown ( )

Definition at line 1024 of file XrdClHttpUtil.cc.

1025{
1026 std::unique_lock lock(m_mutex);
1027 m_shutdown = true;
1028 m_consumer_cv.notify_all();
1029}

◆ TryConsume()

std::shared_ptr< CurlOperation > HandlerQueue::TryConsume ( )

Definition at line 988 of file XrdClHttpUtil.cc.

989{
990 std::unique_lock<std::mutex> lk(m_mutex);
991 if (m_ops.size() == 0) {
992 std::shared_ptr<CurlOperation> result;
993 return result;
994 }
995
996 std::shared_ptr<CurlOperation> result = m_ops.front();
997 m_ops.pop_front();
998
999 char ready[1];
1000 while (true) {
1001 auto result = read(m_read_fd, ready, 1);
1002 if (result == -1) {
1003 if (errno == EINTR) {
1004 continue;
1005 } else if (errno == EAGAIN || errno == EWOULDBLOCK) {
1006 // This should never happen, but if it does, just continue
1007 // as if we successfully read the byte.
1008 break;
1009 }
1010 throw std::system_error(errno, std::generic_category(),
1011 "failed to consume HTTP worker notification");
1012 }
1013 break;
1014 }
1015
1016 lk.unlock();
1017 m_producer_cv.notify_one();
1018 m_ops_consumed.fetch_add(1, std::memory_order_relaxed);
1019
1020 return result;
1021}

References read.


The documentation for this class was generated from the following files: