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< CurlOperationConsume (std::chrono::steady_clock::duration)
void Expire ()
CURLGetHandle ()
int PollFD () const
void Produce (std::shared_ptr< CurlOperation > handler)
void RecycleHandle (CURL *)
void ReleaseHandles ()
void Shutdown ()
std::shared_ptr< CurlOperationTryConsume ()

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 167 of file XrdClHttpUtil.hh.

Constructor & Destructor Documentation

◆ HandlerQueue()

HandlerQueue::HandlerQueue ( unsigned max_pending_ops)

Definition at line 559 of file XrdClHttpUtil.cc.

559 :
560 m_max_pending_ops(max_pending_ops)
561{
562 int filedes[2];
563 auto result = pipe(filedes);
564 if (result == -1) {
565 throw std::runtime_error(strerror(errno));
566 }
567 if (fcntl(filedes[0], F_SETFL, O_NONBLOCK | O_CLOEXEC) == -1 || fcntl(filedes[1], F_SETFL, O_NONBLOCK | O_CLOEXEC) == -1) {
568 close(filedes[0]);
569 close(filedes[1]);
570 throw std::runtime_error(strerror(errno));
571 }
572 m_read_fd = filedes[0];
573 m_write_fd = filedes[1];
574};
#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 792 of file XrdClHttpUtil.cc.

793{
794 std::unique_lock<std::mutex> lk(m_mutex);
795 m_consumer_cv.wait_for(lk, dur, [&]{return m_ops.size() > 0 || m_shutdown;});
796 if (m_shutdown || m_ops.empty()) {
797 return {};
798 }
799
800 std::shared_ptr<CurlOperation> result = m_ops.front();
801 m_ops.pop_front();
802
803 char ready[1];
804 while (true) {
805 auto result = read(m_read_fd, ready, 1);
806 if (result == -1) {
807 if (errno == EINTR) {
808 continue;
809 } else if (errno == EAGAIN || errno == EWOULDBLOCK) {
810 // This should never happen, but if it does, just continue
811 // as if we successfully read the byte.
812 break;
813 }
814 throw std::runtime_error(strerror(errno));
815 }
816 break;
817 }
818
819 lk.unlock();
820 m_producer_cv.notify_one();
821 m_ops_consumed.fetch_add(1, std::memory_order_relaxed);
822
823 return result;
824}
#define read(a, b, c)
Definition XrdPosix.hh:86

References read.

◆ Expire()

void HandlerQueue::Expire ( )

Definition at line 691 of file XrdClHttpUtil.cc.

692{
693 std::unique_lock<std::mutex> lk(m_mutex);
694 auto now = std::chrono::steady_clock::now();
695
696 // Iterate through the paused transfers, checking if they are done.
697 for (auto &op : m_ops) {
698 if (!op->IsPaused()) continue;
699
700 if (op->TransferStalled(0, now)) {
701 op->ContinueHandle();
702 }
703 }
704
705 std::vector<decltype(m_ops)::value_type> expired_ops;
706 unsigned expired_count = 0;
707 auto it = std::remove_if(m_ops.begin(), m_ops.end(),
708 [&](const std::shared_ptr<CurlOperation> &handler) {
709 auto expired = handler->GetOperationExpiry() < now;
710 if (expired) {
711 expired_ops.push_back(handler);
712 expired_count++;
713 }
714 return expired;
715 });
716 m_ops.erase(it, m_ops.end());
717
718 // The contents of our pipe and the in-memory queue are now off by expired_count.
719 // Read exactly that many bytes from the pipe and throw them away.
720 char throwaway[64];
721 unsigned bytes_to_read = expired_count;
722 while (bytes_to_read > 0) {
723 size_t chunk = std::min<size_t>(sizeof(throwaway), bytes_to_read);
724 ssize_t n = read(m_read_fd, throwaway, chunk);
725 if (n > 0) {
726 bytes_to_read -= n;
727 } else if (n == -1) {
728 if (errno == EINTR) {
729 continue;
730 } else {
731 // EWOULDBLOCK is a possibility if there's a synchronization error;
732 // for now, just continue on as if we were successful in reading out
733 // the missing bytes
734 break;
735 }
736 } else {
737 break;
738 }
739 }
740
741 // Note: the failure handler may trigger new operations submitted to the queue
742 // (which requires the lock to be held) such as a prefetch operation that gets split
743 // into multiple sub-operations.
744 //
745 // Thus, we must unlock the mutex protecting the queue and avoid touching the shared state of
746 // m_ops.
747 lk.unlock();
748 for (auto &handler : expired_ops) {
749 if (handler) handler->Fail(XrdCl::errOperationExpired, 0, "Operation expired while in queue");
750 }
751}
const uint16_t errOperationExpired

◆ GetDefaultMaxPendingOps()

unsigned XrdClHttp::HandlerQueue::GetDefaultMaxPendingOps ( )
inlinestatic

Definition at line 194 of file XrdClHttpUtil.hh.

194{return m_default_max_pending_ops;}

◆ GetHandle()

CURL * HandlerQueue::GetHandle ( )

Definition at line 675 of file XrdClHttpUtil.cc.

675 {
676 if (m_handles.size()) {
677 auto result = m_handles.back();
678 m_handles.pop_back();
679 return result;
680 }
681
682 return ::GetHandle(EnableCurlHeaderDump());
683}

◆ GetMonitoringJson()

std::string HandlerQueue::GetMonitoringJson ( )
static

Definition at line 827 of file XrdClHttpUtil.cc.

828{
829 auto consumed = m_ops_consumed.load(std::memory_order_relaxed);
830 auto produced = m_ops_produced.load(std::memory_order_relaxed);
831 return "{"
832 "\"produced\":" + std::to_string(produced) + ","
833 "\"consumed\":" + std::to_string(consumed) + ","
834 "\"pending\":" + std::to_string(produced - consumed) + ","
835 "\"rejected\":" + std::to_string(m_ops_rejected.load(std::memory_order_relaxed)) +
836 "}";
837}

◆ PollFD()

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

Definition at line 176 of file XrdClHttpUtil.hh.

176{return m_read_fd;}

◆ Produce()

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

Definition at line 754 of file XrdClHttpUtil.cc.

755{
756 auto handler_expiry = handler->GetOperationExpiry();
757 std::unique_lock<std::mutex> lk{m_mutex};
758 m_producer_cv.wait_until(lk,
759 handler_expiry,
760 [&]{return m_ops.size() < m_max_pending_ops;}
761 );
762 if (std::chrono::steady_clock::now() > handler_expiry) {
763 lk.unlock();
764 handler->Fail(XrdCl::errOperationExpired, 0, "Operation expired while waiting for worker");
765 m_ops_rejected.fetch_add(1, std::memory_order_relaxed);
766 return;
767 }
768
769 m_ops.push_back(handler);
770 char ready[] = "1";
771 while (true) {
772 auto result = write(m_write_fd, ready, 1);
773 if (result == -1) {
774 if (errno == EINTR) {
775 continue;
776 } else if (errno == EAGAIN || errno == EWOULDBLOCK) {
777 // This should never happen, but if it does, just continue
778 // as if we successfully wrote the notification to the pipe.
779 break;
780 }
781 throw std::runtime_error(strerror(errno));
782 }
783 break;
784 }
785
786 lk.unlock();
787 m_consumer_cv.notify_one();
788 m_ops_produced.fetch_add(1, std::memory_order_relaxed);
789}
#define write(a, b, c)
Definition XrdPosix.hh:121

References XrdCl::errOperationExpired, and write.

◆ RecycleHandle()

void HandlerQueue::RecycleHandle ( CURL * curl)

Definition at line 686 of file XrdClHttpUtil.cc.

686 {
687 m_handles.push_back(curl);
688}

◆ ReleaseHandles()

void HandlerQueue::ReleaseHandles ( )

Definition at line 883 of file XrdClHttpUtil.cc.

884{
885 for (auto handle : m_handles) {
886 curl_easy_cleanup(handle);
887 }
888 m_handles.clear();
889}

◆ Shutdown()

void HandlerQueue::Shutdown ( )

Definition at line 875 of file XrdClHttpUtil.cc.

876{
877 std::unique_lock lock(m_mutex);
878 m_shutdown = true;
879 m_consumer_cv.notify_all();
880}

◆ TryConsume()

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

Definition at line 840 of file XrdClHttpUtil.cc.

841{
842 std::unique_lock<std::mutex> lk(m_mutex);
843 if (m_ops.size() == 0) {
844 std::shared_ptr<CurlOperation> result;
845 return result;
846 }
847
848 std::shared_ptr<CurlOperation> result = m_ops.front();
849 m_ops.pop_front();
850
851 char ready[1];
852 while (true) {
853 auto result = read(m_read_fd, ready, 1);
854 if (result == -1) {
855 if (errno == EINTR) {
856 continue;
857 } else if (errno == EAGAIN || errno == EWOULDBLOCK) {
858 // This should never happen, but if it does, just continue
859 // as if we successfully read the byte.
860 break;
861 }
862 throw std::runtime_error(strerror(errno));
863 }
864 break;
865 }
866
867 lk.unlock();
868 m_producer_cv.notify_one();
869 m_ops_consumed.fetch_add(1, std::memory_order_relaxed);
870
871 return result;
872}

References read.


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