XRootD
Loading...
Searching...
No Matches
XrdHttpTpcStream.hh
Go to the documentation of this file.
1
9
11
12#include <memory>
13#include <vector>
14#include <string>
15
16#include <cstring>
17
18struct stat;
19
20class XrdSysError;
21
22namespace TPC {
23class Stream {
24public:
25 Stream(std::unique_ptr<XrdSfsFile> fh, size_t max_blocks, size_t buffer_size, XrdSysError &log)
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 }
38
39 ~Stream();
40
41 int Stat(struct stat *);
42
43 int Read(off_t offset, char *buffer, size_t size);
44
45 // Writes a buffer of a given size to an offset.
46 // This will often keep the buffer in memory in to present the underlying
47 // filesystem with a single stream of data (required for HDFS); further,
48 // it will also buffer to align the writes on a 1MB boundary (required
49 // for some RADOS configurations). When force is set to true, it will
50 // skip the buffering and always write (this should only be done at the
51 // end of a stream!).
52 //
53 // Returns the number of bytes written; on error, returns -1 and sets
54 // the error code and error message for the stream
55 ssize_t Write(off_t offset, const char *buffer, size_t size, bool force);
56
57 // Force the data still held in the re-ordering buffers out to the underlying
58 // file handle, even if it results in unaligned or short writes. Typically
59 // only done while shutting down the transfer.
60 //
61 // The flush is deliberately issued at the current offset of the stream: the
62 // offset a given transfer state stopped at is not necessarily the offset the
63 // stream has been written up to. In the multistream case, all the states
64 // share this stream, and all but the one that happened to serve the last
65 // range end up before it -- flushing at their offset would be rejected as a
66 // write to a prior offset.
67 //
68 // Returns 0 on success; SFS_ERROR on failure.
69 ssize_t Flush() {return Write(m_offset, nullptr, 0, true);}
70
71 size_t AvailableBuffers() const {return m_avail_count;}
72
73 void DumpBuffers() const;
74
75 // Flush and finalize the stream. If all data has been sent to the underlying
76 // file handle, close() will be invoked on the file handle.
77 //
78 // Further write operations on this stream will result in an error.
79 // If any memory buffers remain, an error occurs.
80 //
81 // Returns true on success; false otherwise.
82 bool Finalize();
83
84 std::string GetErrorMessage() const {return m_error_buf;}
85
86private:
87
88 class Entry {
89 public:
90 Entry(size_t capacity) :
91 m_offset(-1),
92 m_capacity(capacity),
93 m_size(0)
94 {}
95
96 bool Available() const {return m_offset == -1;}
97
98 // Writes the contents of this buffer out to the stream, returning the
99 // number of bytes written (0 if the buffer is not eligible for a write
100 // yet) or SFS_ERROR. On success the buffer is emptied and becomes
101 // available again.
102 ssize_t Write(Stream &stream, bool force) {
103 if (Available() || !CanWrite(stream)) {return 0;}
104 // Only full buffer writes are accepted unless the stream forces a flush
105 // (i.e., we are at EOF) because the multistream code uses buffer occupancy
106 // to determine how many streams are currently in-flight. If we do an early
107 // write, then the buffer will be empty and the multistream code may decide
108 // to start another request (which we don't have the capacity to serve!).
109 if (!force && (m_size != m_capacity)) {
110 return 0;
111 }
112 ssize_t retval = stream.WriteImpl(m_offset, &m_buffer[0], m_size);
113 // Currently the only valid negative value is SFS_ERROR (-1); checking for
114 // all negative values to future-proof the code.
115 if ((retval < 0) || (static_cast<size_t>(retval) != m_size)) {
116 return -1;
117 }
118 m_offset = -1;
119 m_size = 0;
120 m_buffer.clear();
121 return retval;
122 }
123
124 size_t Accept(off_t offset, const char *buf, size_t size) {
125 // Validate acceptance criteria.
126 if ((m_offset != -1) && (offset != m_offset + static_cast<ssize_t>(m_size))) {
127 return 0;
128 }
129 size_t to_accept = m_capacity - m_size;
130 if (to_accept == 0) {return 0;}
131 if (size > to_accept) {
132 size = to_accept;
133 }
134
135 // Inflate the underlying buffer if needed.
136 ssize_t new_bytes_needed = (m_size + size) - m_buffer.size();
137 if (new_bytes_needed > 0) {
138 m_buffer.resize(m_capacity);
139 }
140
141 // Finally, do the copy.
142 memcpy(&m_buffer[0] + m_size, buf, size);
143 m_size += size;
144 if (m_offset == -1) {
145 m_offset = offset;
146 }
147 return size;
148 }
149
150 void ShrinkIfUnused() {
151 if (!Available()) {return;}
152 m_buffer.shrink_to_fit();
153 }
154
155 off_t GetOffset() const {return m_offset;}
156 size_t GetCapacity() const {return m_capacity;}
157 size_t GetSize() const {return m_size;}
158
159 private:
160
161 Entry(const Entry&) = delete;
162
163 bool CanWrite(Stream &stream) const {
164 return (m_size > 0) && (m_offset == stream.m_offset);
165 }
166
167 off_t m_offset; // Offset within file that m_buffer[0] represents.
168 size_t m_capacity;
169 size_t m_size; // Number of bytes held in buffer.
170 std::vector<char> m_buffer;
171 };
172
173 ssize_t WriteImpl(off_t offset, const char *buffer, size_t size);
174
175 // Copies as much of [buffer, buffer+size) as possible into the buffers that
176 // are already holding data and can be extended contiguously. This is pure
177 // bookkeeping: it never touches the underlying filesystem.
178 //
179 // Returns the number of bytes consumed.
180 size_t AcceptIntoBuffers(off_t offset, const char *buffer, size_t size);
181
182 // Writes out every buffer that is contiguous with m_offset, repeating until
183 // no further progress is made: flushing one buffer advances m_offset, which
184 // can in turn make another buffer writable. Only completely full buffers
185 // are written unless force is set (see Entry::Write).
186 //
187 // This is the only place where m_avail_count is computed.
188 //
189 // Returns the number of buffers written out, or SFS_ERROR.
190 ssize_t FlushBuffers(bool force);
191
192 // Returns the first empty buffer, or nullptr if all of them hold data.
193 Entry *FirstAvailableBuffer();
194
195 bool m_open_for_write;
196 size_t m_avail_count;
197 std::unique_ptr<XrdSfsFile> m_fh;
198 off_t m_offset;
199 std::vector<std::unique_ptr<Entry>> m_buffers;
200 XrdSysError &m_log;
201 std::string m_error_buf;
202};
203}
struct stat Stat
Definition XrdCks.cc:49
#define stat(a, b)
Definition XrdPosix.hh:105
int Read(off_t offset, char *buffer, size_t size)
ssize_t Write(off_t offset, const char *buffer, size_t size, bool force)
Stream(std::unique_ptr< XrdSfsFile > fh, size_t max_blocks, size_t buffer_size, XrdSysError &log)
void DumpBuffers() const
std::string GetErrorMessage() const
size_t AvailableBuffers() const