XRootD
Loading...
Searching...
No Matches
TPC::Stream Class Reference

#include <XrdHttpTpcStream.hh>

+ Collaboration diagram for TPC::Stream:

Public Member Functions

 Stream (std::unique_ptr< XrdSfsFile > fh, size_t max_blocks, size_t buffer_size, XrdSysError &log)
 
 ~Stream ()
 
size_t AvailableBuffers () const
 
void DumpBuffers () const
 
bool Finalize ()
 
std::string GetErrorMessage () const
 
int Read (off_t offset, char *buffer, size_t size)
 
int Stat (struct stat *)
 
ssize_t Write (off_t offset, const char *buffer, size_t size, bool force)
 

Detailed Description

Definition at line 23 of file XrdHttpTpcStream.hh.

Constructor & Destructor Documentation

◆ Stream()

TPC::Stream::Stream ( std::unique_ptr< XrdSfsFile > fh,
size_t max_blocks,
size_t buffer_size,
XrdSysError & log )
inline

Definition at line 25 of file XrdHttpTpcStream.hh.

26 : m_open_for_write(false),
27 m_avail_count(max_blocks),
28 m_fh(std::move(fh)),
29 m_offset(0),
30 m_log(log)
31 {
32 m_buffers.reserve(max_blocks);
33 for (size_t idx=0; idx < max_blocks; idx++) {
34 m_buffers.push_back(std::make_unique<Entry>(buffer_size));
35 }
36 m_open_for_write = true;
37 }

◆ ~Stream()

Stream::~Stream ( )

Definition at line 11 of file XrdHttpTpcStream.cc.

12{
13 m_fh->close();
14}

Member Function Documentation

◆ AvailableBuffers()

size_t TPC::Stream::AvailableBuffers ( ) const
inline

Definition at line 57 of file XrdHttpTpcStream.hh.

57{return m_avail_count;}

◆ DumpBuffers()

void Stream::DumpBuffers ( ) const

Definition at line 208 of file XrdHttpTpcStream.cc.

209{
210 m_log.Emsg("Stream::DumpBuffers", "Beginning dump of stream buffers.");
211 {
212 std::stringstream ss;
213 ss << "Stream offset: " << m_offset;
214 m_log.Emsg("Stream::DumpBuffers", ss.str().c_str());
215 }
216 size_t idx = 0;
217 for (const auto &entry : m_buffers) {
218 std::stringstream ss;
219 ss << "Buffer " << idx << ": Offset=" << entry->GetOffset() << ", Size="
220 << entry->GetSize() << ", Capacity=" << entry->GetCapacity();
221 m_log.Emsg("Stream::DumpBuffers", ss.str().c_str());
222 idx ++;
223 }
224 m_log.Emsg("Stream::DumpBuffers", "Finish dump of stream buffers.");
225}

Referenced by Write().

+ Here is the caller graph for this function:

◆ Finalize()

bool Stream::Finalize ( )

Definition at line 18 of file XrdHttpTpcStream.cc.

19{
20 // Do not close twice
21 if (!m_open_for_write) {
22 return false;
23 }
24 m_open_for_write = false;
25
26 // If there are outstanding buffers to reorder, finalization failed; the
27 // check has to happen before the buffers are released.
28 bool all_buffers_returned = m_avail_count == m_buffers.size();
29 m_buffers.clear();
30
31 if (m_fh->close() == SFS_ERROR) {
32 std::stringstream ss;
33 const char *msg = m_fh->error.getErrText();
34 if (!msg || (*msg == '\0')) {msg = "(no error message provided)";}
35 ss << "Failure when closing file handle: " << msg << " (code=" << m_fh->error.getErrInfo() << ")";
36 m_error_buf = ss.str();
37 return false;
38 }
39
40 return all_buffers_returned;
41}
#define SFS_ERROR

References SFS_ERROR.

◆ GetErrorMessage()

std::string TPC::Stream::GetErrorMessage ( ) const
inline

Definition at line 70 of file XrdHttpTpcStream.hh.

70{return m_error_buf;}

◆ Read()

int Stream::Read ( off_t offset,
char * buffer,
size_t size )

Definition at line 229 of file XrdHttpTpcStream.cc.

230{
231 return m_fh->read(offset, buf, size);
232}

◆ Stat()

int Stream::Stat ( struct stat * buf)

Definition at line 45 of file XrdHttpTpcStream.cc.

46{
47 return m_fh->stat(buf);
48}

References stat.

◆ Write()

ssize_t Stream::Write ( off_t offset,
const char * buffer,
size_t size,
bool force )

Definition at line 51 of file XrdHttpTpcStream.cc.

52{
53/*
54 * NOTE: these lines are useful for debuggin the state of the buffer
55 * management code; too expensive to compile in and have a runtime switch.
56 std::stringstream ss;
57 ss << "Offset=" << offset << ", Size=" << size << ", force=" << force;
58 m_log.Emsg("Stream::Write", ss.str().c_str());
59 DumpBuffers();
60*/
61 if (!m_open_for_write) {
62 if (!m_error_buf.size()) {m_error_buf = "Logic error: writing to a buffer not opened for write";}
63 return SFS_ERROR;
64 }
65 size_t bytes_accepted = 0;
66 ssize_t retval = size;
67 if (offset < m_offset) {
68 if (!m_error_buf.size()) {m_error_buf = "Logic error: writing to a prior offset";}
69 return SFS_ERROR;
70 }
71 // If this is write is appending to the stream and
72 // MB-aligned, then we write it to disk; otherwise, the
73 // data will be buffered.
74 if (offset == m_offset && (force || (size && !(size % (1024*1024))))) {
75 retval = WriteImpl(offset, buf, size);
76 bytes_accepted = retval;
77 // On failure, we don't care about flushing buffers from memory --
78 // the stream is now invalid.
79 if (retval < 0) {
80 return retval;
81 }
82 // If there are no in-use buffers, then we don't need to
83 // do any accounting.
84 if (m_avail_count == m_buffers.size()) {
85 return retval;
86 }
87 }
88 // Even if we already accepted the current data, always iterate through the
89 // buffers and try to write as much out to disk as possible.
90 //
91 // Accepting data can complete a buffer, and flushing a buffer advances
92 // m_offset, which can in turn let another buffer accept more data or become
93 // writable. Alternate between the two until neither makes progress. When
94 // size == 0 we force a flush even if things are not MB-aligned.
95 ssize_t buffers_flushed;
96 do {
97 bytes_accepted += AcceptIntoBuffers(offset + bytes_accepted,
98 buf + bytes_accepted,
99 size - bytes_accepted);
100 buffers_flushed = FlushBuffers(size == 0);
101 if (buffers_flushed == SFS_ERROR) {return SFS_ERROR;}
102 } while ((buffers_flushed > 0) && (bytes_accepted != size));
103
104 if (bytes_accepted != size && size) { // No place for this data in the buffers currently in use
105 Entry *avail_entry = FirstAvailableBuffer();
106 if (!avail_entry) { // No available buffers to allocate; logic error, should not happen.
107 DumpBuffers();
108 m_error_buf = "No empty buffers available to place unordered data.";
109 return SFS_ERROR;
110 }
111 if (avail_entry->Accept(offset + bytes_accepted, buf + bytes_accepted, size - bytes_accepted) != size - bytes_accepted) { // Empty buffer cannot accept?!?
112 m_error_buf = "Empty re-ordering buffer was unable to to accept data; internal logic error.";
113 return SFS_ERROR;
114 }
115 // The buffer we just filled may already be complete and contiguous with
116 // m_offset; flush it now instead of waiting for a later callback to
117 // notice, as every curl handle may be idle by then.
118 if (FlushBuffers(false) == SFS_ERROR) {return SFS_ERROR;}
119 }
120
121 // If we have low buffer occupancy, then release memory.
122 if ((m_buffers.size() > 2) && (m_avail_count * 2 > m_buffers.size())) {
123 for (auto &entry : m_buffers) {
124 entry->ShrinkIfUnused();
125 }
126 }
127
128 return retval;
129}
void DumpBuffers() const

References DumpBuffers(), and SFS_ERROR.

+ Here is the call graph for this function:

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