22#include < node_io_watcher.h>
33
44#include < node.h>
5+ #include < node_buffer.h>
56#include < v8.h>
67
8+
9+ #include < sys/uio.h> /* writev */
10+ #include < errno.h>
11+
712#include < assert.h>
813
914namespace node {
1015
1116using namespace v8 ;
1217
18+ static ev_prepare dumper;
19+ static Persistent<Object> dump_queue;
20+
1321Persistent<FunctionTemplate> IOWatcher::constructor_template;
1422Persistent<String> callback_symbol;
1523
24+ static Persistent<String> next_sym;
25+ static Persistent<String> data_sym;
26+ static Persistent<String> offset_sym;
27+ static Persistent<String> buckets_sym;
28+
1629
1730void IOWatcher::Initialize (Handle<Object> target) {
1831 HandleScope scope;
@@ -26,9 +39,23 @@ void IOWatcher::Initialize(Handle<Object> target) {
2639 NODE_SET_PROTOTYPE_METHOD (constructor_template, " stop" , IOWatcher::Stop);
2740 NODE_SET_PROTOTYPE_METHOD (constructor_template, " set" , IOWatcher::Set);
2841
29- target->Set (String::NewSymbol (" IOWatcher" ), constructor_template->GetFunction ());
42+ Local<Function> io_watcher = constructor_template->GetFunction ();
43+ target->Set (String::NewSymbol (" IOWatcher" ), io_watcher);
3044
3145 callback_symbol = NODE_PSYMBOL (" callback" );
46+
47+ next_sym = NODE_PSYMBOL (" next" );
48+ buckets_sym = NODE_PSYMBOL (" buckets" );
49+ offset_sym = NODE_PSYMBOL (" offset" );
50+ data_sym = NODE_PSYMBOL (" data" );
51+
52+
53+ ev_prepare_init (&dumper, IOWatcher::Dump);
54+ ev_prepare_start (EV_DEFAULT_UC_ &dumper);
55+ ev_unref (EV_DEFAULT_UC );
56+
57+ dump_queue = Persistent<Object>::New (Object::New ());
58+ io_watcher->Set (String::NewSymbol (" dumpQueue" ), dump_queue);
3259}
3360
3461
@@ -143,6 +170,260 @@ Handle<Value> IOWatcher::Set(const Arguments& args) {
143170 return Undefined ();
144171}
145172
173+ #define KB 1024
174+
175+ /*
176+ * A large javascript object structure is built up in net.js. The function
177+ * Dump is called at the end of each iteration, before select() is called,
178+ * to push all the data out to sockets.
179+ *
180+ * The structure looks like this:
181+ *
182+ * IOWatcher.dumpQueue -> W -> W -> W -> W
183+ * | | | |
184+ * o o o o
185+ * | | |
186+ * o o o
187+ * | |
188+ * o o
189+ * |
190+ * o
191+ *
192+ * Where the 'W' nodes are IOWatcher instances associated with a particular
193+ * socket. The 'o' nodes are little javascript objects with a 'data'
194+ * member. 'data' is either a string or buffer. E.G.
195+ * o = { data: "hello world" }
196+ *
197+ */
198+
199+ // To enable this debug output, do:
200+ // echo "CPPFLAGS += -DDUMP_DEBUG" >> config.mak
201+ // make clean all
202+ #ifdef DUMP_DEBUG
203+ #define DEBUG_PRINT (fmt,...) do { \
204+ fprintf (stderr, " %s:%d " , __FILE__, __LINE__); \
205+ fprintf (stderr, fmt " \n " , ##__VA_ARGS__); \
206+ } while (0 )
207+ #else
208+ #define DEBUG_PRINT (fmt,...)
209+ #endif
210+
211+
212+ void IOWatcher::Dump (EV_P_ ev_prepare *watcher, int revents) {
213+ assert (revents == EV_PREPARE );
214+ assert (watcher == &dumper);
215+
216+ HandleScope scope;
217+
218+
219+ #define IOV_SIZE 10000
220+ static struct iovec iov[IOV_SIZE ];
221+
222+ // Loop over all 'fd objects' in the dump queue. Each object stands for a
223+ // socket that has stuff to be written out.
224+ Local<Value> writer_node_v;
225+ Local<Object> writer_node;
226+ Local<Object> writer_node_last = Local<Object>::New (dump_queue);
227+
228+ for (writer_node_v = dump_queue->Get (next_sym);
229+ writer_node_v->IsObject ();
230+ writer_node_v = writer_node->Get (next_sym),
231+ writer_node_last = writer_node) {
232+
233+ writer_node = writer_node_v->ToObject ();
234+
235+ IOWatcher *io = ObjectWrap::Unwrap<IOWatcher>(writer_node);
236+ // stats (just for fun)
237+ io->dumps_ ++;
238+ io->last_dump_ = ev_now (EV_DEFAULT_UC );
239+
240+ DEBUG_PRINT (" Dumping %d" , io->watcher_ .fd );
241+
242+ // Number of items we've stored in iov
243+ int iovcnt = 0 ;
244+ // Number of bytes we've stored in iov
245+ size_t to_write = 0 ;
246+
247+
248+ // Offset is only so large as the first buffer of data
249+ // this occurs when a previous writev could not entirely flush
250+ // a bucket.
251+ size_t offset = 0 ;
252+ if (writer_node->Has (offset_sym)) {
253+ offset = writer_node->Get (offset_sym)->Uint32Value ();
254+ }
255+
256+ // Loop over all the buckets for this particular socket.
257+ Local<Value> bucket_v;
258+ Local<Object> bucket;
259+ bool first = true ;
260+ unsigned int bucket_index = 0 ;
261+ for (bucket_v = writer_node->Get (buckets_sym);
262+ bucket_v->IsObject () && to_write < 64 *KB && iovcnt < IOV_SIZE ;
263+ bucket_v = bucket->Get (next_sym), bucket_index++) {
264+ bucket = bucket_v->ToObject ();
265+
266+ Local<Value> data_v = bucket->Get (data_sym);
267+ // net.js will be setting this 'data' value. We can ensure that it is
268+ // never empty.
269+ assert (!data_v.IsEmpty ());
270+
271+ Local<Object> buf_object;
272+
273+ if (data_v->IsString ()) {
274+ // FIXME This is suboptimal - Buffer::New is slow.
275+ // Also insert v8::String::Pointers() hack here.
276+ Local<String> s = data_v->ToString ();
277+ buf_object = Local<Object>::New (Buffer::New (s));
278+ bucket->Set (data_sym, buf_object);
279+ } else if (Buffer::HasInstance (data_v)) {
280+ buf_object = data_v->ToObject ();
281+ } else {
282+ assert (0 );
283+ }
284+
285+ size_t l = Buffer::Length (buf_object);
286+
287+ if (first /* ugly */ ) {
288+ assert (offset < l);
289+ iov[iovcnt].iov_base = Buffer::Data (buf_object) + offset;
290+ iov[iovcnt].iov_len = l - offset;
291+ } else {
292+ iov[iovcnt].iov_base = Buffer::Data (buf_object);
293+ iov[iovcnt].iov_len = l;
294+ }
295+ to_write += iov[iovcnt].iov_len ;
296+ iovcnt++;
297+
298+ first = false ; // ugly
299+ }
300+
301+ ssize_t written = writev (io->watcher_ .fd , iov, iovcnt);
302+
303+ DEBUG_PRINT (" iovcnt: %d, to_write: %ld, written: %ld" , iovcnt, to_write, written);
304+
305+ if (written < 0 ) {
306+ switch (errno) {
307+ #if 0
308+ case EPIPE:
309+ // What do to do with EPIPE? Somehow we should be emitting an
310+ // error event on the socket object...
311+ break;
312+ #endif
313+
314+ case EAGAIN :
315+ DEBUG_PRINT (" EAGAIN" );
316+ io->Start ();
317+ continue ;
318+
319+ default :
320+ perror (" writev" );
321+ continue ;
322+ }
323+ }
324+
325+ // what about written == 0 ?
326+
327+ // Now drop the buckets that have been written.
328+ first = true ;
329+ bucket_index = 0 ;
330+
331+ for (bucket_v = writer_node->Get (buckets_sym);
332+ written > 0 && bucket_v->IsObject ();
333+ bucket_v = bucket->Get (next_sym), bucket_index++) {
334+ bucket = bucket_v->ToObject ();
335+ assert (written > 0 );
336+
337+ Local<Value> data_v = bucket->Get (data_sym);
338+ assert (!data_v.IsEmpty ());
339+
340+ // At the moment we're turning all string into buffers
341+ // so we assert that this is not a string. However, when the
342+ // "Pointer patch" lands, this assert will need to be removed.
343+ assert (!data_v->IsString ());
344+ // When the "Pointer patch" lands, we will need to be careful
345+ // to somehow store the length of strings that we're optimizing on
346+ // so that it need not be recalculated here. Note the "Pointer patch"
347+ // will only apply to ASCII strings - UTF8 ones will need to be
348+ // serialized onto a buffer.
349+ size_t bucket_len = Buffer::Length (data_v->ToObject ());
350+
351+ DEBUG_PRINT (" %ld bucket len: %ld" , bucket_index, bucket_len);
352+
353+ if (first) {
354+ // Only on the first bucket does the offset matter.
355+ if (offset + written < bucket_len) {
356+ // we have not written the entire first bucket
357+ DEBUG_PRINT (" %ld Only wrote part of the first buffer. "
358+ " setting watcher.offset = %ld" ,
359+ bucket_index,
360+ offset + written);
361+
362+ writer_node->Set (offset_sym,
363+ Integer::NewFromUnsigned (offset + written));
364+ break ;
365+ } else {
366+ DEBUG_PRINT (" %ld wrote the whole first bucket. discarding." ,
367+ bucket_index);
368+ // We have written the entire bucket, discard it.
369+ written -= bucket_len - offset;
370+ writer_node->Set (buckets_sym, bucket->Get (next_sym));
371+ }
372+ } else {
373+ // not first
374+
375+ if (static_cast <size_t >(written) < bucket_len) {
376+ // Didn't write the whole bucket.
377+ DEBUG_PRINT (" %ld Only wrote part of the buffer. "
378+ " setting watcher.offset = %ld" ,
379+ bucket_index,
380+ offset + written);
381+ writer_node->Set (offset_sym,
382+ Integer::NewFromUnsigned (written));
383+ break ;
384+ } else {
385+ // Wrote the whole bucket, drop it.
386+ DEBUG_PRINT (" %ld wrote the whole bucket. discarding." , bucket_index);
387+ written -= bucket_len - offset;
388+ written -= bucket_len;
389+ writer_node->Set (buckets_sym, bucket->Get (next_sym));
390+ }
391+ }
392+
393+ first = false ;
394+ }
395+
396+ /*
397+ * Finished dumping the buckets.
398+ * If our list of buckets is empty, we can emit 'drain' (somehow?) and
399+ * forget about this socket. Nothing needs to be done.
400+ *
401+ * Otherwise we need to prepare the io_watcher to wait for the interface
402+ * to become writable again.
403+ */
404+
405+ if (writer_node->Get (buckets_sym)->IsUndefined ()) {
406+ // Emptied the queue for this socket.
407+ // Don't wait for it to become writable.
408+ io->Stop ();
409+ DEBUG_PRINT (" Stop watcher %d" , io->watcher_ .fd );
410+
411+ // Drop the writer_node from the list.
412+ writer_node_last->Set (next_sym, writer_node->Get (next_sym));
413+
414+ // Emit drain event!
415+
416+ } else {
417+ io->Start ();
418+ DEBUG_PRINT (" Started watcher %d" , io->watcher_ .fd );
419+ // current->next = new_write_queue->next
420+
421+ // new_write_queue->next = current
422+ }
423+ }
424+ }
425+
426+
146427
147428
148429} // namespace node
0 commit comments