passenger 5.0.0.beta2 → 5.0.0.beta3

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of passenger might be problematic. Click here for more details.

Files changed (73) hide show
  1. checksums.yaml +8 -8
  2. checksums.yaml.gz.asc +7 -7
  3. data.tar.gz.asc +7 -7
  4. data/CHANGELOG +30 -0
  5. data/CONTRIBUTORS +2 -0
  6. data/Gemfile.lock +1 -1
  7. data/bin/passenger-status +13 -15
  8. data/build/cxx_tests.rb +14 -1
  9. data/build/preprocessor.rb +4 -2
  10. data/debian.template/control.template +2 -2
  11. data/doc/Security of user switching support.txt +2 -2
  12. data/doc/Users guide Apache.idmap.txt +6 -4
  13. data/doc/Users guide Apache.txt +20 -1
  14. data/doc/Users guide Nginx.idmap.txt +5 -3
  15. data/doc/Users guide Nginx.txt +22 -2
  16. data/ext/apache2/Configuration.cpp +6 -0
  17. data/ext/apache2/Configuration.hpp +4 -1
  18. data/ext/apache2/Hooks.cpp +1 -0
  19. data/ext/common/Constants.h +4 -2
  20. data/ext/common/Constants.h.erb +1 -1
  21. data/ext/common/DataStructures/LString.h +10 -0
  22. data/ext/common/ServerKit/Channel.h +1 -1
  23. data/ext/common/ServerKit/Context.h +2 -21
  24. data/ext/common/ServerKit/CookieUtils.h +246 -0
  25. data/ext/common/ServerKit/FdSourceChannel.h +10 -0
  26. data/ext/common/ServerKit/FileBufferedChannel.h +173 -17
  27. data/ext/common/ServerKit/FileBufferedFdSinkChannel.h +33 -1
  28. data/ext/common/ServerKit/HeaderTable.h +3 -1
  29. data/ext/common/ServerKit/HttpServer.h +36 -8
  30. data/ext/common/ServerKit/Server.h +1 -0
  31. data/ext/common/Utils.cpp +2 -1
  32. data/ext/common/Utils/DateParsing.h +15 -2
  33. data/ext/common/Utils/JsonUtils.h +39 -1
  34. data/ext/common/agents/HelperAgent/Main.cpp +4 -2
  35. data/ext/common/agents/HelperAgent/OptionParser.h +14 -2
  36. data/ext/common/agents/HelperAgent/RequestHandler.h +22 -8
  37. data/ext/common/agents/HelperAgent/RequestHandler/ForwardResponse.cpp +92 -11
  38. data/ext/common/agents/HelperAgent/RequestHandler/Hooks.cpp +3 -1
  39. data/ext/common/agents/HelperAgent/RequestHandler/InitRequest.cpp +9 -5
  40. data/ext/common/agents/HelperAgent/RequestHandler/Request.h +1 -0
  41. data/ext/common/agents/HelperAgent/RequestHandler/SendRequest.cpp +27 -13
  42. data/ext/common/agents/HelperAgent/ResponseCache.h +91 -34
  43. data/ext/common/agents/LoggingAgent/AdminServer.h +21 -1
  44. data/ext/nginx/CacheLocationConfig.c +20 -0
  45. data/ext/nginx/Configuration.c +130 -24
  46. data/ext/nginx/Configuration.h +2 -1
  47. data/ext/nginx/ConfigurationCommands.c +10 -0
  48. data/ext/nginx/ConfigurationFields.h +2 -0
  49. data/ext/nginx/ContentHandler.c +1 -6
  50. data/ext/nginx/CreateLocationConfig.c +5 -0
  51. data/ext/nginx/MergeLocationConfig.c +6 -0
  52. data/ext/nginx/StaticContentHandler.c +3 -9
  53. data/ext/nginx/ngx_http_passenger_module.c +2 -1
  54. data/ext/ruby/extconf.rb +5 -4
  55. data/lib/phusion_passenger.rb +2 -2
  56. data/lib/phusion_passenger/constants.rb +2 -1
  57. data/lib/phusion_passenger/nginx/config_options.rb +5 -1
  58. data/lib/phusion_passenger/rack/thread_handler_extension.rb +3 -1
  59. data/lib/phusion_passenger/ruby_core_enhancements.rb +3 -4
  60. data/lib/phusion_passenger/standalone/start_command.rb +5 -1
  61. data/lib/phusion_passenger/standalone/start_command/builtin_engine.rb +10 -3
  62. data/resources/templates/standalone/config.erb +2 -1
  63. data/test/cxx/DateParsingTest.cpp +75 -0
  64. data/test/cxx/ResponseCacheTest.cpp +322 -0
  65. data/test/cxx/ServerKit/CookieUtilsTest.cpp +274 -0
  66. data/test/cxx/ServerKit/HttpServerTest.cpp +77 -0
  67. data/test/stub/rails3.0/Gemfile.lock +2 -2
  68. data/test/stub/rails3.1/Gemfile.lock +2 -2
  69. data/test/stub/rails3.2/Gemfile.lock +2 -2
  70. data/test/stub/rails4.0/Gemfile.lock +2 -2
  71. data/test/stub/rails4.1/Gemfile.lock +2 -2
  72. metadata +6 -2
  73. metadata.gz.asc +7 -7
@@ -1224,6 +1224,7 @@ public:
1224
1224
  .set ("default_ruby", serverConfig.defaultRuby)
1225
1225
  .setInt ("max_pool_size", serverConfig.maxPoolSize)
1226
1226
  .setInt ("pool_idle_time", serverConfig.poolIdleTime)
1227
+ .setInt ("response_buffer_high_watermark", serverConfig.responseBufferHighWatermark)
1227
1228
  .setInt ("stat_throttle_rate", serverConfig.statThrottleRate)
1228
1229
  .set ("analytics_log_user", serverConfig.analyticsLogUser)
1229
1230
  .set ("analytics_log_group", serverConfig.analyticsLogGroup)
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * Phusion Passenger - https://www.phusionpassenger.com/
3
- * Copyright (c) 2010-2013 Phusion
3
+ * Copyright (c) 2010-2015 Phusion
4
4
  *
5
5
  * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
6
6
  *
@@ -78,6 +78,8 @@
78
78
 
79
79
  #define DEFAULT_PYTHON "python"
80
80
 
81
+ #define DEFAULT_RESPONSE_BUFFER_HIGH_WATERMARK 134217728
82
+
81
83
  #define DEFAULT_RUBY "ruby"
82
84
 
83
85
  #define DEFAULT_SPAWN_METHOD "smart"
@@ -110,7 +112,7 @@
110
112
 
111
113
  #define NGINX_DOC_URL "https://www.phusionpassenger.com/documentation/Users%20guide%20Nginx.html"
112
114
 
113
- #define PASSENGER_VERSION "5.0.0.beta2"
115
+ #define PASSENGER_VERSION "5.0.0.beta3"
114
116
 
115
117
  #define POOL_HELPER_THREAD_STACK_SIZE 262144
116
118
 
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * Phusion Passenger - https://www.phusionpassenger.com/
3
- * Copyright (c) 2010-2013 Phusion
3
+ * Copyright (c) 2010-2015 Phusion
4
4
  *
5
5
  * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
6
6
  *
@@ -117,6 +117,16 @@ psg_lstr_append_part(LString *str, LString::Part *part) {
117
117
  part->next = NULL;
118
118
  }
119
119
 
120
+ inline void
121
+ psg_lstr_append_part_from_another_lstr(LString *str, psg_pool_t *pool, const LString::Part *part) {
122
+ LString::Part *copy = (LString::Part *) psg_palloc(pool, sizeof(LString::Part));
123
+ *copy = *part;
124
+ if (part->mbuf_block != NULL) {
125
+ mbuf_block_ref(part->mbuf_block);
126
+ }
127
+ psg_lstr_append_part(str, copy);
128
+ }
129
+
120
130
  inline void
121
131
  psg_lstr_append(LString *str, psg_pool_t *pool, const MemoryKit::mbuf &buffer,
122
132
  const char *data, unsigned int size)
@@ -133,7 +133,7 @@ using namespace boost;
133
133
  *
134
134
  * ...process buffer....
135
135
  *
136
- * return Channel::Result(bytesProcessed, acceptFurtherData);
136
+ * return Channel::Result(bytesProcessed, !acceptFurtherData);
137
137
  * } else if (errcode == 0) {
138
138
  * // EOF reached. Result doesn't matter in this case.
139
139
  * return Channel::Result(0, false);
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * Phusion Passenger - https://www.phusionpassenger.com/
3
- * Copyright (c) 2014 Phusion
3
+ * Copyright (c) 2014-2015 Phusion
4
4
  *
5
5
  * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
6
6
  *
@@ -33,6 +33,7 @@
33
33
  #include <Constants.h>
34
34
  #include <Utils/StrIntUtils.h>
35
35
  #include <Utils/json.h>
36
+ #include <Utils/JsonUtils.h>
36
37
 
37
38
  namespace Passenger {
38
39
  namespace ServerKit {
@@ -61,26 +62,6 @@ private:
61
62
  MemoryKit::mbuf_pool_init(&mbuf_pool);
62
63
  }
63
64
 
64
- string formatFloat(double val) const {
65
- char buf[64];
66
- int size = snprintf(buf, sizeof(buf), "%.1f", val);
67
- return string(buf, size);
68
- }
69
-
70
- Json::Value
71
- byteSizeToJson(size_t size) const {
72
- Json::Value doc;
73
- doc["bytes"] = (Json::UInt64) size;
74
- if (size < 1024) {
75
- doc["human_readable"] = toString(size) + " bytes";
76
- } else if (size < 1024 * 1024) {
77
- doc["human_readable"] = formatFloat(size / 1024.0) + " KB";
78
- } else {
79
- doc["human_readable"] = formatFloat(size / 1024.0 / 1024.0) + " MB";
80
- }
81
- return doc;
82
- }
83
-
84
65
  public:
85
66
  SafeLibevPtr libev;
86
67
  struct MemoryKit::mbuf_pool mbuf_pool;
@@ -0,0 +1,246 @@
1
+ /*
2
+ * Phusion Passenger - https://www.phusionpassenger.com/
3
+ * Copyright (c) 2014 Phusion
4
+ *
5
+ * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
6
+ *
7
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ * of this software and associated documentation files (the "Software"), to deal
9
+ * in the Software without restriction, including without limitation the rights
10
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ * copies of the Software, and to permit persons to whom the Software is
12
+ * furnished to do so, subject to the following conditions:
13
+ *
14
+ * The above copyright notice and this permission notice shall be included in
15
+ * all copies or substantial portions of the Software.
16
+ *
17
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ * THE SOFTWARE.
24
+ */
25
+ #ifndef _PASSENGER_SERVER_KIT_COOKIE_UTILS_H_
26
+ #define _PASSENGER_SERVER_KIT_COOKIE_UTILS_H_
27
+
28
+ #include <cstring>
29
+ #include <cassert>
30
+ #include <MemoryKit/palloc.h>
31
+ #include <DataStructures/LString.h>
32
+
33
+ namespace Passenger {
34
+ namespace ServerKit {
35
+
36
+
37
+ inline bool findCookieNameValueSeparator(const LString::Part *part, size_t index,
38
+ const LString::Part **separatorPart, size_t *separatorIndex);
39
+ inline bool findCookieEnd(const LString::Part *separatorPart, size_t separatorIndex,
40
+ const LString::Part **endPart, size_t *endIndex);
41
+ inline bool matchCookieName(psg_pool_t *pool, const LString::Part *part, size_t index,
42
+ const LString::Part *separatorPart, size_t separatorIndex,
43
+ const LString *name);
44
+ inline LString *extractCookieValue(psg_pool_t *pool,
45
+ const LString::Part *separatorPart, size_t separatorIndex,
46
+ const LString::Part *endPart, size_t endIndex);
47
+
48
+
49
+ /**
50
+ * Given the value of an HTTP cookie header, returns the value of the cookie
51
+ * of the given name, or NULL if not found.
52
+ */
53
+ inline LString *
54
+ findCookie(psg_pool_t *pool, const LString *cookieHeaderValue, const LString *name) {
55
+ const LString::Part *part = cookieHeaderValue->start;
56
+ const LString::Part *separatorPart, *endPart;
57
+ size_t index = 0, separatorIndex, endIndex;
58
+ bool done = part == NULL;
59
+ LString *result = NULL;
60
+
61
+ while (!done) {
62
+ if (findCookieNameValueSeparator(part, index, &separatorPart, &separatorIndex)) {
63
+ if (!findCookieEnd(separatorPart, separatorIndex, &endPart, &endIndex)) {
64
+ done = true;
65
+ } else if (matchCookieName(pool, part, index, separatorPart, separatorIndex, name)) {
66
+ result = extractCookieValue(pool, separatorPart, separatorIndex, endPart, endIndex);
67
+ done = true;
68
+ } else {
69
+ part = endPart;
70
+ index = endIndex;
71
+ done = endIndex >= endPart->size;
72
+ }
73
+ } else {
74
+ done = true;
75
+ }
76
+ }
77
+
78
+ return result;
79
+ }
80
+
81
+ inline bool
82
+ findCookieNameValueSeparator(const LString::Part *part, size_t index,
83
+ const LString::Part **separatorPart, size_t *separatorIndex)
84
+ {
85
+ const char *pos;
86
+ bool result = false;
87
+ bool done = part == NULL;
88
+
89
+ while (!done) {
90
+ pos = (const char *) memchr(part->data + index, '=', part->size - index);
91
+ if (pos == NULL) {
92
+ part = part->next;
93
+ index = 0;
94
+ done = part == NULL;
95
+ } else {
96
+ *separatorPart = part;
97
+ *separatorIndex = pos - part->data;
98
+ result = true;
99
+ done = true;
100
+ }
101
+ }
102
+
103
+ return result;
104
+ }
105
+
106
+ inline bool
107
+ findCookieEnd(const LString::Part *separatorPart, size_t separatorIndex,
108
+ const LString::Part **endPart, size_t *endIndex)
109
+ {
110
+ const LString::Part *part = separatorPart;
111
+ size_t index = separatorIndex;
112
+ const char *pos;
113
+ bool result = false;
114
+ bool done = part == NULL;
115
+
116
+ while (!done) {
117
+ pos = (const char *) memchr(part->data + index, ';', part->size - index);
118
+ if (pos == NULL) {
119
+ if (part->next == NULL) {
120
+ // Semicolon not found in entire LString. Return end-of-LString
121
+ // as cookie end.
122
+ *endPart = part;
123
+ *endIndex = part->size;
124
+ result = true;
125
+ done = true;
126
+ } else {
127
+ part = part->next;
128
+ index = 0;
129
+ done = part == NULL;
130
+ }
131
+ } else {
132
+ // Semicolon found.
133
+ *endPart = part;
134
+ *endIndex = pos - part->data;
135
+ result = true;
136
+ done = true;
137
+ }
138
+ }
139
+
140
+ return result;
141
+ }
142
+
143
+ inline void
144
+ _matchCookieName_skipWhitespace(LString *str) {
145
+ LString::Part *part = str->start;
146
+ size_t pos = 0;
147
+ bool done = false;
148
+
149
+ while (!done) {
150
+ while (part->data[pos] == ' ' || part->data[pos] == ';') {
151
+ pos++;
152
+ }
153
+
154
+ if (pos == part->size) {
155
+ str->start = part->next;
156
+ str->size -= part->size;
157
+ part = part->next;
158
+ if (part == NULL) {
159
+ assert(str->size == 0);
160
+ done = true;
161
+ str->end = NULL;
162
+ }
163
+ } else {
164
+ part->data += pos;
165
+ part->size -= pos;
166
+ str->size -= pos;
167
+ done = true;
168
+ }
169
+ }
170
+ }
171
+
172
+ inline bool
173
+ matchCookieName(psg_pool_t *pool, const LString::Part *part, size_t index,
174
+ const LString::Part *separatorPart, size_t separatorIndex,
175
+ const LString *name)
176
+ {
177
+ LString *str = (LString *) psg_palloc(pool, sizeof(LString));
178
+ psg_lstr_init(str);
179
+
180
+ if (part == separatorPart) {
181
+ assert(index < separatorIndex);
182
+ psg_lstr_append(str, pool,
183
+ part->data + index,
184
+ separatorIndex - index);
185
+ } else {
186
+ psg_lstr_append(str, pool,
187
+ part->data + index,
188
+ part->size - index);
189
+
190
+ part = part->next;
191
+ while (part != separatorPart) {
192
+ psg_lstr_append(str, pool, part->data, part->size);
193
+ part = part->next;
194
+ }
195
+
196
+ if (separatorIndex != 0) {
197
+ psg_lstr_append(str, pool, separatorPart->data, separatorIndex);
198
+ }
199
+ }
200
+
201
+ _matchCookieName_skipWhitespace(str);
202
+
203
+ bool result = psg_lstr_cmp(str, name);
204
+ psg_lstr_deinit(str);
205
+ return result;
206
+ }
207
+
208
+ inline LString *
209
+ extractCookieValue(psg_pool_t *pool,
210
+ const LString::Part *separatorPart, size_t separatorIndex,
211
+ const LString::Part *endPart, size_t endIndex)
212
+ {
213
+ LString *str = (LString *) psg_palloc(pool, sizeof(LString));
214
+ psg_lstr_init(str);
215
+
216
+ if (separatorPart == endPart) {
217
+ assert(separatorIndex < endIndex);
218
+ psg_lstr_append(str, pool,
219
+ separatorPart->data + separatorIndex + 1,
220
+ endIndex - separatorIndex - 1);
221
+ } else {
222
+ if (separatorIndex < separatorPart->size - 1) {
223
+ psg_lstr_append(str, pool,
224
+ separatorPart->data + separatorIndex + 1,
225
+ separatorPart->size - separatorIndex - 1);
226
+ }
227
+
228
+ const LString::Part *part = separatorPart->next;
229
+ while (part != endPart) {
230
+ psg_lstr_append(str, pool, part->data, part->size);
231
+ part = part->next;
232
+ }
233
+
234
+ if (endIndex != 0) {
235
+ psg_lstr_append(str, pool, endPart->data, endIndex);
236
+ }
237
+ }
238
+
239
+ return str;
240
+ }
241
+
242
+
243
+ } // namespace ServerKit
244
+ } // namespace Passenger
245
+
246
+ #endif /* _PASSENGER_SERVER_KIT_COOKIE_UTILS_H_ */
@@ -207,6 +207,16 @@ public:
207
207
  return watcher.fd;
208
208
  }
209
209
 
210
+ OXT_FORCE_INLINE
211
+ State getState() const {
212
+ return Channel::getState();
213
+ }
214
+
215
+ OXT_FORCE_INLINE
216
+ bool isStarted() const {
217
+ return Channel::isStarted();
218
+ }
219
+
210
220
  OXT_FORCE_INLINE
211
221
  void setDataCallback(DataCallback callback) {
212
222
  Channel::dataCallback = callback;
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * Phusion Passenger - https://www.phusionpassenger.com/
3
- * Copyright (c) 2014 Phusion
3
+ * Copyright (c) 2014-2015 Phusion
4
4
  *
5
5
  * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
6
6
  *
@@ -43,6 +43,8 @@
43
43
  #include <ServerKit/Context.h>
44
44
  #include <ServerKit/Errors.h>
45
45
  #include <ServerKit/Channel.h>
46
+ #include <Utils/json.h>
47
+ #include <Utils/JsonUtils.h>
46
48
 
47
49
  namespace Passenger {
48
50
  namespace ServerKit {
@@ -187,8 +189,10 @@ public:
187
189
  typedef Channel::DataCallback DataCallback;
188
190
  typedef void (*Callback)(FileBufferedChannel *channel);
189
191
 
190
- // `buffered` is 25-bit. This is 2^25-1, or 32 MB.
191
- static const unsigned int MAX_MEMORY_BUFFERING = 33554431;
192
+ // 2^32-1 bytes.
193
+ static const unsigned int MAX_MEMORY_BUFFERING = 4294967295u;
194
+ // `nbuffers` is 27-bit. This is 2^27-1.
195
+ static const unsigned int MAX_BUFFERS = 134217727;
192
196
 
193
197
 
194
198
  private:
@@ -197,6 +201,13 @@ private:
197
201
  SafeLibevPtr libev;
198
202
  eio_req *req;
199
203
  boost::atomic<bool> canceled;
204
+ /**
205
+ * Synchronizes access to `req`. Because all I/O callbacks call
206
+ * `eioFinished()`, this mutex blocks callbacks until the main
207
+ * thread is done assigning `req`.
208
+ * See https://github.com/phusion/passenger/issues/1326
209
+ */
210
+ boost::mutex syncher;
200
211
 
201
212
  eio_ssize_t result;
202
213
  int errcode;
@@ -213,6 +224,7 @@ private:
213
224
  virtual ~IOContext() { }
214
225
 
215
226
  void cancel() {
227
+ boost::lock_guard<boost::mutex> l(syncher);
216
228
  if (req != NULL) {
217
229
  eio_cancel(req);
218
230
  }
@@ -225,6 +237,7 @@ private:
225
237
  }
226
238
 
227
239
  void eioFinished() {
240
+ boost::lock_guard<boost::mutex> l(syncher);
228
241
  result = req->result;
229
242
  errcode = req->errorno;
230
243
  req = NULL;
@@ -337,10 +350,10 @@ private:
337
350
  };
338
351
 
339
352
  FileBufferedChannelConfig *config;
340
- Mode mode;
341
- ReaderState readerState;
353
+ Mode mode: 2;
354
+ ReaderState readerState: 3;
342
355
  /** Number of buffers in `firstBuffer` + `moreBuffers`. */
343
- boost::uint16_t nbuffers;
356
+ unsigned int nbuffers: 27;
344
357
 
345
358
  /**
346
359
  * If an error is encountered, its details are stored here.
@@ -390,6 +403,7 @@ private:
390
403
 
391
404
  void pushBuffer(const MemoryKit::mbuf &buffer) {
392
405
  assert(bytesBuffered + buffer.size() <= MAX_MEMORY_BUFFERING);
406
+ assert(nbuffers < MAX_BUFFERS);
393
407
  if (nbuffers == 0) {
394
408
  firstBuffer = buffer;
395
409
  } else {
@@ -606,7 +620,6 @@ private:
606
620
  void channelHasBecomeIdle() {
607
621
  FBC_DEBUG("Reader: underlying channel has become idle");
608
622
  verifyInvariants();
609
- //readerState = RS_INACTIVE;
610
623
  readNext();
611
624
  }
612
625
 
@@ -631,9 +644,9 @@ private:
631
644
  };
632
645
 
633
646
  void readNextChunkFromFile() {
647
+ assert(inFileMode->written > 0);
634
648
  size_t size = std::min<size_t>(inFileMode->written,
635
- ctx->mbuf_pool.mbuf_block_chunk_size -
636
- ctx->mbuf_pool.mbuf_block_offset);
649
+ mbuf_pool_data_size(&ctx->mbuf_pool));
637
650
  FBC_DEBUG("Reader: reading next chunk from file");
638
651
  verifyInvariants();
639
652
  ReadContext *readContext = new ReadContext(this);
@@ -641,16 +654,34 @@ private:
641
654
  readContext->inFileMode = inFileMode;
642
655
  readerState = RS_READING_FROM_FILE;
643
656
  inFileMode->readRequest = readContext;
657
+ boost::unique_lock<boost::mutex> l(readContext->syncher);
644
658
  readContext->req = eio_read(inFileMode->fd, readContext->buffer.start,
645
659
  size, inFileMode->readOffset, 0, _nextChunkDoneReading, readContext);
660
+ l.unlock();
646
661
  verifyInvariants();
647
662
  }
648
663
 
664
+ // Since a ReadContext contains an mbuf, we may only destroy it
665
+ // in the event loop thread.
666
+ static void destroyReadContext(ReadContext *readContext) {
667
+ if (readContext->libev->onEventLoopThread()) {
668
+ destroyReadContext_onEventLoopThread(readContext);
669
+ } else {
670
+ readContext->libev->runLater(boost::bind(
671
+ destroyReadContext_onEventLoopThread,
672
+ readContext));
673
+ }
674
+ }
675
+
676
+ static void destroyReadContext_onEventLoopThread(ReadContext *readContext) {
677
+ delete readContext;
678
+ }
679
+
649
680
  static int _nextChunkDoneReading(eio_req *req) {
650
681
  ReadContext *readContext = (ReadContext *) req->data;
651
682
  readContext->eioFinished();
652
683
  if (readContext->isCanceled()) {
653
- delete readContext;
684
+ destroyReadContext(readContext);
654
685
  return 0;
655
686
  }
656
687
 
@@ -666,7 +697,7 @@ private:
666
697
 
667
698
  static void _nextChunkDoneReading_onEventLoopThread(ReadContext *readContext) {
668
699
  if (readContext->isCanceled()) {
669
- delete readContext;
700
+ destroyReadContext(readContext);
670
701
  return;
671
702
  }
672
703
 
@@ -683,7 +714,7 @@ private:
683
714
  int fd = readContext->result;
684
715
  int errcode = readContext->errcode;
685
716
  MemoryKit::mbuf buffer(boost::move(readContext->buffer));
686
- delete readContext;
717
+ destroyReadContext(readContext);
687
718
  inFileMode->readRequest = NULL;
688
719
 
689
720
  if (fd != -1) {
@@ -802,6 +833,7 @@ private:
802
833
  inFileMode->writerState = WS_CREATING_FILE;
803
834
  inFileMode->writerRequest = fcContext;
804
835
 
836
+ boost::lock_guard<boost::mutex> l(fcContext->syncher);
805
837
  if (config->delayInFileModeSwitching == 0) {
806
838
  FBC_DEBUG("Writer: creating file " << fcContext->path);
807
839
  fcContext->req = eio_open(fcContext->path.c_str(),
@@ -845,6 +877,7 @@ private:
845
877
  }
846
878
 
847
879
  void bufferFileDoneDelaying(FileCreationContext *fcContext) {
880
+ boost::lock_guard<boost::mutex> l(fcContext->syncher);
848
881
  FBC_DEBUG("Writer: done delaying in-file mode switching. "
849
882
  "Creating file: " << fcContext->path);
850
883
  fcContext->req = eio_open(fcContext->path.c_str(),
@@ -981,19 +1014,37 @@ private:
981
1014
 
982
1015
  inFileMode->writerState = WS_MOVING;
983
1016
  inFileMode->writerRequest = moveContext;
1017
+ boost::unique_lock<boost::mutex> l(moveContext->syncher);
984
1018
  moveContext->req = eio_write(inFileMode->fd,
985
1019
  moveContext->buffer.start,
986
1020
  moveContext->buffer.size(),
987
1021
  inFileMode->readOffset + inFileMode->written,
988
1022
  0, _bufferWrittenToFile, moveContext);
1023
+ l.unlock();
989
1024
  verifyInvariants();
990
1025
  }
991
1026
 
1027
+ // Since a MoveContext contains an mbuf, we may only destroy it
1028
+ // in the event loop thread.
1029
+ static void destroyMoveContext(MoveContext *moveContext) {
1030
+ if (moveContext->libev->onEventLoopThread()) {
1031
+ destroyMoveContext_onEventLoopThread(moveContext);
1032
+ } else {
1033
+ moveContext->libev->runLater(boost::bind(
1034
+ destroyMoveContext_onEventLoopThread,
1035
+ moveContext));
1036
+ }
1037
+ }
1038
+
1039
+ static void destroyMoveContext_onEventLoopThread(MoveContext *moveContext) {
1040
+ delete moveContext;
1041
+ }
1042
+
992
1043
  static int _bufferWrittenToFile(eio_req *req) {
993
1044
  MoveContext *moveContext = static_cast<MoveContext *>(req->data);
994
1045
  moveContext->eioFinished();
995
1046
  if (moveContext->isCanceled()) {
996
- delete moveContext;
1047
+ destroyMoveContext(moveContext);
997
1048
  return 0;
998
1049
  }
999
1050
 
@@ -1009,7 +1060,7 @@ private:
1009
1060
 
1010
1061
  static void _bufferWrittenToFile_onEventLoopThread(MoveContext *moveContext) {
1011
1062
  if (moveContext->isCanceled()) {
1012
- delete moveContext;
1063
+ destroyMoveContext(moveContext);
1013
1064
  return;
1014
1065
  }
1015
1066
 
@@ -1040,27 +1091,29 @@ private:
1040
1091
  if (generation != this->generation || mode >= ERROR) {
1041
1092
  // buffersFlushedCallback deinitialized this object, or callback
1042
1093
  // called a method that encountered an error.
1043
- delete moveContext;
1094
+ destroyMoveContext(moveContext);
1044
1095
  return;
1045
1096
  }
1046
1097
 
1047
1098
  inFileMode->writerRequest = NULL;
1048
- delete moveContext;
1099
+ destroyMoveContext(moveContext);
1049
1100
  moveNextBufferToFile();
1050
1101
  } else {
1051
1102
  FBC_DEBUG("Writer: move incomplete, proceeding " <<
1052
1103
  "with writing rest of buffer");
1104
+ boost::unique_lock<boost::mutex> l(moveContext->syncher);
1053
1105
  moveContext->req = eio_write(inFileMode->fd,
1054
1106
  moveContext->buffer.start + moveContext->written,
1055
1107
  moveContext->buffer.size() - moveContext->written,
1056
1108
  inFileMode->readOffset + inFileMode->written,
1057
1109
  0, _bufferWrittenToFile, moveContext);
1110
+ l.unlock();
1058
1111
  verifyInvariants();
1059
1112
  }
1060
1113
  } else {
1061
1114
  FBC_DEBUG("Writer: file write failed");
1062
1115
  int errcode = moveContext->errcode;
1063
- delete moveContext;
1116
+ destroyMoveContext(moveContext);
1064
1117
  inFileMode->writerRequest = NULL;
1065
1118
  inFileMode->writerState = WS_TERMINATED;
1066
1119
  setError(errcode, __FILE__, __LINE__);
@@ -1141,6 +1194,42 @@ private:
1141
1194
  inFileMode->writerState = WS_INACTIVE;
1142
1195
  }
1143
1196
 
1197
+ const char *getReaderStateString() const {
1198
+ switch (readerState) {
1199
+ case RS_INACTIVE:
1200
+ return "RS_INACTIVE";
1201
+ case RS_FEEDING:
1202
+ return "RS_FEEDING";
1203
+ case RS_FEEDING_EOF:
1204
+ return "RS_FEEDING_EOF";
1205
+ case RS_WAITING_FOR_CHANNEL_IDLE:
1206
+ return "RS_WAITING_FOR_CHANNEL_IDLE";
1207
+ case RS_READING_FROM_FILE:
1208
+ return "RS_READING_FROM_FILE";
1209
+ case RS_TERMINATED:
1210
+ return "RS_TERMINATED";
1211
+ default:
1212
+ P_BUG("Unknown readerState");
1213
+ return NULL;
1214
+ }
1215
+ }
1216
+
1217
+ const char *getWriterStateString() const {
1218
+ switch (inFileMode->writerState) {
1219
+ case WS_INACTIVE:
1220
+ return "WS_INACTIVE";
1221
+ case WS_CREATING_FILE:
1222
+ return "WS_CREATING_FILE";
1223
+ case WS_MOVING:
1224
+ return "WS_MOVING";
1225
+ case WS_TERMINATED:
1226
+ return "WS_TERMINATED";
1227
+ default:
1228
+ P_BUG("Unknown writerState");
1229
+ return NULL;
1230
+ }
1231
+ }
1232
+
1144
1233
  void verifyInvariants() const {
1145
1234
  #ifndef NDEBUG
1146
1235
  if (mode >= ERROR) {
@@ -1353,10 +1442,32 @@ public:
1353
1442
  return inFileMode->writerState;
1354
1443
  }
1355
1444
 
1445
+ /**
1446
+ * Returns the number of bytes buffered in memory.
1447
+ */
1356
1448
  unsigned int getBytesBuffered() const {
1357
1449
  return bytesBuffered;
1358
1450
  }
1359
1451
 
1452
+ /**
1453
+ * Returns the number of bytes that are buffered on disk
1454
+ * and have not yet been read.
1455
+ */
1456
+ boost::uint64_t getBytesBufferedOnDisk() const {
1457
+ if (mode == IN_FILE_MODE && inFileMode->written >= 0) {
1458
+ return inFileMode->written;
1459
+ } else {
1460
+ return 0;
1461
+ }
1462
+ }
1463
+
1464
+ /**
1465
+ * Returns the total bytes buffered, both in-memory and on disk.
1466
+ */
1467
+ boost::uint64_t getTotalBytesBuffered() const {
1468
+ return bytesBuffered + getBytesBufferedOnDisk();
1469
+ }
1470
+
1360
1471
  bool ended() const {
1361
1472
  return (hasBuffers() && peekLastBuffer().empty())
1362
1473
  || mode >= ERROR || Channel::ended();
@@ -1370,14 +1481,27 @@ public:
1370
1481
  return bytesBuffered >= config->threshold;
1371
1482
  }
1372
1483
 
1484
+ OXT_FORCE_INLINE
1373
1485
  void setDataCallback(DataCallback callback) {
1374
1486
  Channel::dataCallback = callback;
1375
1487
  }
1376
1488
 
1489
+ OXT_FORCE_INLINE
1490
+ Callback getBuffersFlushedCallback() const {
1491
+ return buffersFlushedCallback;
1492
+ }
1493
+
1494
+ OXT_FORCE_INLINE
1377
1495
  void setBuffersFlushedCallback(Callback callback) {
1378
1496
  buffersFlushedCallback = callback;
1379
1497
  }
1380
1498
 
1499
+ OXT_FORCE_INLINE
1500
+ Callback getDataFlushedCallback() const {
1501
+ return dataFlushedCallback;
1502
+ }
1503
+
1504
+ OXT_FORCE_INLINE
1381
1505
  void setDataFlushedCallback(Callback callback) {
1382
1506
  dataFlushedCallback = callback;
1383
1507
  }
@@ -1387,9 +1511,41 @@ public:
1387
1511
  return Channel::hooks;
1388
1512
  }
1389
1513
 
1514
+ OXT_FORCE_INLINE
1390
1515
  void setHooks(Hooks *hooks) {
1391
1516
  Channel::hooks = hooks;
1392
1517
  }
1518
+
1519
+ Json::Value inspectAsJson() const {
1520
+ Json::Value doc;
1521
+
1522
+ switch (mode) {
1523
+ case IN_MEMORY_MODE:
1524
+ doc["mode"] = "IN_MEMORY_MODE";
1525
+ break;
1526
+ case IN_FILE_MODE:
1527
+ doc["mode"] = "IN_FILE_MODE";
1528
+ doc["writer_state"] = getWriterStateString();
1529
+ doc["read_offset"] = byteSizeToJson(inFileMode->readOffset);
1530
+ doc["written"] = signedByteSizeToJson(inFileMode->written);
1531
+ break;
1532
+ case ERROR:
1533
+ doc["mode"] = "ERROR";
1534
+ break;
1535
+ case ERROR_WAITING:
1536
+ doc["mode"] = "ERROR_WAITING";
1537
+ break;
1538
+ default:
1539
+ break;
1540
+ }
1541
+
1542
+ doc["reader_state"] = getReaderStateString();
1543
+ doc["nbuffers"] = nbuffers;
1544
+ doc["bytes_buffered"] = byteSizeToJson(getBytesBuffered());
1545
+ doc["callback_in_progress"] = !acceptingInput();
1546
+
1547
+ return doc;
1548
+ }
1393
1549
  };
1394
1550
 
1395
1551