XRootD
XrdHttpTpcStream.hh
Go to the documentation of this file.
1 
11 
12 #include <memory>
13 #include <vector>
14 #include <string>
15 
16 #include <cstring>
17 
18 struct stat;
19 
20 class XrdSysError;
21 
22 namespace TPC {
23 class Stream {
24 public:
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  size_t AvailableBuffers() const {return m_avail_count;}
58 
59  void DumpBuffers() const;
60 
61  // Flush and finalize the stream. If all data has been sent to the underlying
62  // file handle, close() will be invoked on the file handle.
63  //
64  // Further write operations on this stream will result in an error.
65  // If any memory buffers remain, an error occurs.
66  //
67  // Returns true on success; false otherwise.
68  bool Finalize();
69 
70  std::string GetErrorMessage() const {return m_error_buf;}
71 
72 private:
73 
74  class Entry {
75  public:
76  Entry(size_t capacity) :
77  m_offset(-1),
78  m_capacity(capacity),
79  m_size(0)
80  {}
81 
82  bool Available() const {return m_offset == -1;}
83 
84  // Writes the contents of this buffer out to the stream, returning the
85  // number of bytes written (0 if the buffer is not eligible for a write
86  // yet) or SFS_ERROR. On success the buffer is emptied and becomes
87  // available again.
88  ssize_t Write(Stream &stream, bool force) {
89  if (Available() || !CanWrite(stream)) {return 0;}
90  // Only full buffer writes are accepted unless the stream forces a flush
91  // (i.e., we are at EOF) because the multistream code uses buffer occupancy
92  // to determine how many streams are currently in-flight. If we do an early
93  // write, then the buffer will be empty and the multistream code may decide
94  // to start another request (which we don't have the capacity to serve!).
95  if (!force && (m_size != m_capacity)) {
96  return 0;
97  }
98  ssize_t retval = stream.WriteImpl(m_offset, &m_buffer[0], m_size);
99  // Currently the only valid negative value is SFS_ERROR (-1); checking for
100  // all negative values to future-proof the code.
101  if ((retval < 0) || (static_cast<size_t>(retval) != m_size)) {
102  return -1;
103  }
104  m_offset = -1;
105  m_size = 0;
106  m_buffer.clear();
107  return retval;
108  }
109 
110  size_t Accept(off_t offset, const char *buf, size_t size) {
111  // Validate acceptance criteria.
112  if ((m_offset != -1) && (offset != m_offset + static_cast<ssize_t>(m_size))) {
113  return 0;
114  }
115  size_t to_accept = m_capacity - m_size;
116  if (to_accept == 0) {return 0;}
117  if (size > to_accept) {
118  size = to_accept;
119  }
120 
121  // Inflate the underlying buffer if needed.
122  ssize_t new_bytes_needed = (m_size + size) - m_buffer.size();
123  if (new_bytes_needed > 0) {
124  m_buffer.resize(m_capacity);
125  }
126 
127  // Finally, do the copy.
128  memcpy(&m_buffer[0] + m_size, buf, size);
129  m_size += size;
130  if (m_offset == -1) {
131  m_offset = offset;
132  }
133  return size;
134  }
135 
136  void ShrinkIfUnused() {
137  if (!Available()) {return;}
138  m_buffer.shrink_to_fit();
139  }
140 
141  off_t GetOffset() const {return m_offset;}
142  size_t GetCapacity() const {return m_capacity;}
143  size_t GetSize() const {return m_size;}
144 
145  private:
146 
147  Entry(const Entry&) = delete;
148 
149  bool CanWrite(Stream &stream) const {
150  return (m_size > 0) && (m_offset == stream.m_offset);
151  }
152 
153  off_t m_offset; // Offset within file that m_buffer[0] represents.
154  size_t m_capacity;
155  size_t m_size; // Number of bytes held in buffer.
156  std::vector<char> m_buffer;
157  };
158 
159  ssize_t WriteImpl(off_t offset, const char *buffer, size_t size);
160 
161  // Copies as much of [buffer, buffer+size) as possible into the buffers that
162  // are already holding data and can be extended contiguously. This is pure
163  // bookkeeping: it never touches the underlying filesystem.
164  //
165  // Returns the number of bytes consumed.
166  size_t AcceptIntoBuffers(off_t offset, const char *buffer, size_t size);
167 
168  // Writes out every buffer that is contiguous with m_offset, repeating until
169  // no further progress is made: flushing one buffer advances m_offset, which
170  // can in turn make another buffer writable. Only completely full buffers
171  // are written unless force is set (see Entry::Write).
172  //
173  // This is the only place where m_avail_count is computed.
174  //
175  // Returns the number of buffers written out, or SFS_ERROR.
176  ssize_t FlushBuffers(bool force);
177 
178  // Returns the first empty buffer, or nullptr if all of them hold data.
179  Entry *FirstAvailableBuffer();
180 
181  bool m_open_for_write;
182  size_t m_avail_count;
183  std::unique_ptr<XrdSfsFile> m_fh;
184  off_t m_offset;
185  std::vector<std::unique_ptr<Entry>> m_buffers;
186  XrdSysError &m_log;
187  std::string m_error_buf;
188 };
189 }
#define stat(a, b)
Definition: XrdPosix.hh:101
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
int Stat(struct stat *)