xref: /unit/src/nxt_sendbuf.c (revision 42)
10Sigor@sysoev.ru 
20Sigor@sysoev.ru /*
30Sigor@sysoev.ru  * Copyright (C) Igor Sysoev
40Sigor@sysoev.ru  * Copyright (C) NGINX, Inc.
50Sigor@sysoev.ru  */
60Sigor@sysoev.ru 
70Sigor@sysoev.ru #include <nxt_main.h>
80Sigor@sysoev.ru 
90Sigor@sysoev.ru 
100Sigor@sysoev.ru static nxt_bool_t nxt_sendbuf_copy(nxt_buf_mem_t *bm, nxt_buf_t *b,
110Sigor@sysoev.ru     size_t *copied);
120Sigor@sysoev.ru 
130Sigor@sysoev.ru 
140Sigor@sysoev.ru nxt_uint_t
1513Sigor@sysoev.ru nxt_sendbuf_mem_coalesce0(nxt_task_t *task, nxt_sendbuf_t *sb,
1613Sigor@sysoev.ru     struct iovec *iov, nxt_uint_t niov_max)
1713Sigor@sysoev.ru {
1813Sigor@sysoev.ru     u_char      *last;
1913Sigor@sysoev.ru     size_t      size, total;
2013Sigor@sysoev.ru     nxt_buf_t   *b;
2113Sigor@sysoev.ru     nxt_uint_t  n;
2213Sigor@sysoev.ru 
2313Sigor@sysoev.ru     total = sb->size;
2413Sigor@sysoev.ru     last = NULL;
2513Sigor@sysoev.ru     n = (nxt_uint_t) -1;
2613Sigor@sysoev.ru 
2713Sigor@sysoev.ru     for (b = sb->buf; b != NULL && total < sb->limit; b = b->next) {
2813Sigor@sysoev.ru 
2913Sigor@sysoev.ru         nxt_prefetch(b->next);
3013Sigor@sysoev.ru 
3113Sigor@sysoev.ru         if (nxt_buf_is_file(b)) {
3213Sigor@sysoev.ru             break;
3313Sigor@sysoev.ru         }
3413Sigor@sysoev.ru 
3513Sigor@sysoev.ru         if (nxt_buf_is_mem(b)) {
3613Sigor@sysoev.ru 
3713Sigor@sysoev.ru             size = b->mem.free - b->mem.pos;
3813Sigor@sysoev.ru 
3913Sigor@sysoev.ru             if (size != 0) {
4013Sigor@sysoev.ru 
4113Sigor@sysoev.ru                 if (total + size > sb->limit) {
4213Sigor@sysoev.ru                     size = sb->limit - total;
4313Sigor@sysoev.ru 
4413Sigor@sysoev.ru                     if (size == 0) {
4513Sigor@sysoev.ru                         break;
4613Sigor@sysoev.ru                     }
4713Sigor@sysoev.ru                 }
4813Sigor@sysoev.ru 
4913Sigor@sysoev.ru                 if (b->mem.pos != last) {
5013Sigor@sysoev.ru 
5113Sigor@sysoev.ru                     if (++n >= niov_max) {
5213Sigor@sysoev.ru                         goto done;
5313Sigor@sysoev.ru                     }
5413Sigor@sysoev.ru 
5513Sigor@sysoev.ru                     iov[n].iov_base =  b->mem.pos;
5613Sigor@sysoev.ru                     iov[n].iov_len = size;
5713Sigor@sysoev.ru 
5813Sigor@sysoev.ru                 } else {
5913Sigor@sysoev.ru                     iov[n].iov_len += size;
6013Sigor@sysoev.ru                 }
6113Sigor@sysoev.ru 
6213Sigor@sysoev.ru                 nxt_debug(task, "sendbuf: %ui, %p, %uz",
6313Sigor@sysoev.ru                           n, iov[n].iov_base, iov[n].iov_len);
6413Sigor@sysoev.ru 
6513Sigor@sysoev.ru                 total += size;
6613Sigor@sysoev.ru                 last = b->mem.pos + size;
6713Sigor@sysoev.ru             }
6813Sigor@sysoev.ru 
6913Sigor@sysoev.ru         } else {
7013Sigor@sysoev.ru             sb->sync = 1;
7113Sigor@sysoev.ru             sb->last |= nxt_buf_is_last(b);
7213Sigor@sysoev.ru         }
7313Sigor@sysoev.ru     }
7413Sigor@sysoev.ru 
7513Sigor@sysoev.ru     n++;
7613Sigor@sysoev.ru 
7713Sigor@sysoev.ru done:
7813Sigor@sysoev.ru 
7913Sigor@sysoev.ru     sb->buf = b;
8013Sigor@sysoev.ru 
8113Sigor@sysoev.ru     return n;
8213Sigor@sysoev.ru }
8313Sigor@sysoev.ru 
8413Sigor@sysoev.ru 
8513Sigor@sysoev.ru nxt_uint_t
861Sigor@sysoev.ru nxt_sendbuf_mem_coalesce(nxt_task_t *task, nxt_sendbuf_coalesce_t *sb)
870Sigor@sysoev.ru {
880Sigor@sysoev.ru     u_char      *last;
890Sigor@sysoev.ru     size_t      size, total;
900Sigor@sysoev.ru     nxt_buf_t   *b;
910Sigor@sysoev.ru     nxt_uint_t  n;
920Sigor@sysoev.ru 
930Sigor@sysoev.ru     total = sb->size;
940Sigor@sysoev.ru     last = NULL;
950Sigor@sysoev.ru     n = (nxt_uint_t) -1;
960Sigor@sysoev.ru 
970Sigor@sysoev.ru     for (b = sb->buf; b != NULL && total < sb->limit; b = b->next) {
980Sigor@sysoev.ru 
990Sigor@sysoev.ru         nxt_prefetch(b->next);
1000Sigor@sysoev.ru 
1010Sigor@sysoev.ru         if (nxt_buf_is_file(b)) {
1020Sigor@sysoev.ru             break;
1030Sigor@sysoev.ru         }
1040Sigor@sysoev.ru 
1050Sigor@sysoev.ru         if (nxt_buf_is_mem(b)) {
1060Sigor@sysoev.ru 
1070Sigor@sysoev.ru             size = b->mem.free - b->mem.pos;
1080Sigor@sysoev.ru 
1090Sigor@sysoev.ru             if (size != 0) {
1100Sigor@sysoev.ru 
1110Sigor@sysoev.ru                 if (total + size > sb->limit) {
1120Sigor@sysoev.ru                     size = sb->limit - total;
1130Sigor@sysoev.ru 
1140Sigor@sysoev.ru                     if (size == 0) {
1150Sigor@sysoev.ru                         break;
1160Sigor@sysoev.ru                     }
1170Sigor@sysoev.ru                 }
1180Sigor@sysoev.ru 
1190Sigor@sysoev.ru                 if (b->mem.pos != last) {
1200Sigor@sysoev.ru 
1210Sigor@sysoev.ru                     if (++n >= sb->nmax) {
1220Sigor@sysoev.ru                         goto done;
1230Sigor@sysoev.ru                     }
1240Sigor@sysoev.ru 
12514Sigor@sysoev.ru                     sb->iobuf[n].iov_base =  b->mem.pos;
12614Sigor@sysoev.ru                     sb->iobuf[n].iov_len = size;
1270Sigor@sysoev.ru 
1280Sigor@sysoev.ru                 } else {
12914Sigor@sysoev.ru                     sb->iobuf[n].iov_len += size;
1300Sigor@sysoev.ru                 }
1310Sigor@sysoev.ru 
13214Sigor@sysoev.ru                 nxt_debug(task, "sendbuf: %ui, %p, %uz",
13314Sigor@sysoev.ru                           n, sb->iobuf[n].iov_base, sb->iobuf[n].iov_len);
1340Sigor@sysoev.ru 
1350Sigor@sysoev.ru                 total += size;
1360Sigor@sysoev.ru                 last = b->mem.pos + size;
1370Sigor@sysoev.ru             }
1380Sigor@sysoev.ru 
1390Sigor@sysoev.ru         } else {
1400Sigor@sysoev.ru             sb->sync = 1;
1410Sigor@sysoev.ru             sb->last |= nxt_buf_is_last(b);
1420Sigor@sysoev.ru         }
1430Sigor@sysoev.ru     }
1440Sigor@sysoev.ru 
1450Sigor@sysoev.ru     n++;
1460Sigor@sysoev.ru 
1470Sigor@sysoev.ru done:
1480Sigor@sysoev.ru 
1490Sigor@sysoev.ru     sb->buf = b;
1500Sigor@sysoev.ru     sb->size = total;
151*42Smax.romanov@nginx.com     sb->niov = n;
1520Sigor@sysoev.ru 
1530Sigor@sysoev.ru     return n;
1540Sigor@sysoev.ru }
1550Sigor@sysoev.ru 
1560Sigor@sysoev.ru 
1570Sigor@sysoev.ru size_t
1580Sigor@sysoev.ru nxt_sendbuf_file_coalesce(nxt_sendbuf_coalesce_t *sb)
1590Sigor@sysoev.ru {
1600Sigor@sysoev.ru     size_t     file_start, total;
1610Sigor@sysoev.ru     nxt_fd_t   fd;
1620Sigor@sysoev.ru     nxt_off_t  size, last;
1630Sigor@sysoev.ru     nxt_buf_t  *b;
1640Sigor@sysoev.ru 
1650Sigor@sysoev.ru     b = sb->buf;
1660Sigor@sysoev.ru     fd = b->file->fd;
1670Sigor@sysoev.ru 
1680Sigor@sysoev.ru     total = sb->size;
1690Sigor@sysoev.ru 
1700Sigor@sysoev.ru     for ( ;; ) {
1710Sigor@sysoev.ru 
1720Sigor@sysoev.ru         nxt_prefetch(b->next);
1730Sigor@sysoev.ru 
1740Sigor@sysoev.ru         size = b->file_end - b->file_pos;
1750Sigor@sysoev.ru 
1760Sigor@sysoev.ru         if (total + size >= sb->limit) {
1770Sigor@sysoev.ru             total = sb->limit;
1780Sigor@sysoev.ru             break;
1790Sigor@sysoev.ru         }
1800Sigor@sysoev.ru 
1810Sigor@sysoev.ru         total += size;
1820Sigor@sysoev.ru         last = b->file_pos + size;
1830Sigor@sysoev.ru 
1840Sigor@sysoev.ru         b = b->next;
1850Sigor@sysoev.ru 
1860Sigor@sysoev.ru         if (b == NULL || !nxt_buf_is_file(b)) {
1870Sigor@sysoev.ru             break;
1880Sigor@sysoev.ru         }
1890Sigor@sysoev.ru 
1900Sigor@sysoev.ru         if (b->file_pos != last || b->file->fd != fd) {
1910Sigor@sysoev.ru             break;
1920Sigor@sysoev.ru         }
1930Sigor@sysoev.ru     }
1940Sigor@sysoev.ru 
1950Sigor@sysoev.ru     sb->buf = b;
1960Sigor@sysoev.ru 
1970Sigor@sysoev.ru     file_start = sb->size;
1980Sigor@sysoev.ru     sb->size = total;
1990Sigor@sysoev.ru 
2000Sigor@sysoev.ru     return total - file_start;
2010Sigor@sysoev.ru }
2020Sigor@sysoev.ru 
2030Sigor@sysoev.ru 
2040Sigor@sysoev.ru ssize_t
2050Sigor@sysoev.ru nxt_sendbuf_copy_coalesce(nxt_event_conn_t *c, nxt_buf_mem_t *bm,
2060Sigor@sysoev.ru     nxt_buf_t *b, size_t limit)
2070Sigor@sysoev.ru {
2080Sigor@sysoev.ru     size_t      size, bsize, copied;
2090Sigor@sysoev.ru     ssize_t     n;
2100Sigor@sysoev.ru     nxt_bool_t  flush;
2110Sigor@sysoev.ru 
2120Sigor@sysoev.ru     size = nxt_buf_mem_used_size(&b->mem);
2130Sigor@sysoev.ru     bsize = nxt_buf_mem_size(bm);
2140Sigor@sysoev.ru 
2150Sigor@sysoev.ru     if (bsize != 0) {
2160Sigor@sysoev.ru 
2170Sigor@sysoev.ru         if (size > bsize && bm->pos == bm->free) {
2180Sigor@sysoev.ru             /*
2190Sigor@sysoev.ru              * A data buffer size is larger than the internal
2200Sigor@sysoev.ru              * buffer size and the internal buffer is empty.
2210Sigor@sysoev.ru              */
2220Sigor@sysoev.ru             goto no_buffer;
2230Sigor@sysoev.ru         }
2240Sigor@sysoev.ru 
2250Sigor@sysoev.ru         if (bm->pos == NULL) {
2260Sigor@sysoev.ru             bm->pos = nxt_malloc(bsize);
2270Sigor@sysoev.ru             if (nxt_slow_path(bm->pos == NULL)) {
2280Sigor@sysoev.ru                 return NXT_ERROR;
2290Sigor@sysoev.ru             }
2300Sigor@sysoev.ru 
2310Sigor@sysoev.ru             bm->start = bm->pos;
2320Sigor@sysoev.ru             bm->free = bm->pos;
2330Sigor@sysoev.ru             bm->end += (uintptr_t) bm->pos;
2340Sigor@sysoev.ru         }
2350Sigor@sysoev.ru 
2360Sigor@sysoev.ru         copied = 0;
2370Sigor@sysoev.ru 
2380Sigor@sysoev.ru         flush = nxt_sendbuf_copy(bm, b, &copied);
2390Sigor@sysoev.ru 
2400Sigor@sysoev.ru         nxt_log_debug(c->socket.log, "sendbuf copy:%uz fl:%b", copied, flush);
2410Sigor@sysoev.ru 
2420Sigor@sysoev.ru         if (flush == 0) {
2430Sigor@sysoev.ru             return copied;
2440Sigor@sysoev.ru         }
2450Sigor@sysoev.ru 
2460Sigor@sysoev.ru         size = nxt_buf_mem_used_size(bm);
2470Sigor@sysoev.ru 
2480Sigor@sysoev.ru         if (size == 0 && nxt_buf_is_sync(b)) {
2490Sigor@sysoev.ru             goto done;
2500Sigor@sysoev.ru         }
2510Sigor@sysoev.ru 
2520Sigor@sysoev.ru         n = c->io->send(c, bm->pos, nxt_min(size, limit));
2530Sigor@sysoev.ru 
2540Sigor@sysoev.ru         nxt_log_debug(c->socket.log, "sendbuf sent:%z", n);
2550Sigor@sysoev.ru 
2560Sigor@sysoev.ru         if (n > 0) {
2570Sigor@sysoev.ru             bm->pos += n;
2580Sigor@sysoev.ru 
2590Sigor@sysoev.ru             if (bm->pos == bm->free) {
2600Sigor@sysoev.ru                 bm->pos = bm->start;
2610Sigor@sysoev.ru                 bm->free = bm->start;
2620Sigor@sysoev.ru             }
2630Sigor@sysoev.ru 
2640Sigor@sysoev.ru             n = 0;
2650Sigor@sysoev.ru         }
2660Sigor@sysoev.ru 
2670Sigor@sysoev.ru         return (copied != 0) ? (ssize_t) copied : n;
2680Sigor@sysoev.ru     }
2690Sigor@sysoev.ru 
2700Sigor@sysoev.ru     /* No internal buffering. */
2710Sigor@sysoev.ru 
2720Sigor@sysoev.ru     if (size == 0 && nxt_buf_is_sync(b)) {
2730Sigor@sysoev.ru         goto done;
2740Sigor@sysoev.ru     }
2750Sigor@sysoev.ru 
2760Sigor@sysoev.ru no_buffer:
2770Sigor@sysoev.ru 
2780Sigor@sysoev.ru     return c->io->send(c, b->mem.pos, nxt_min(size, limit));
2790Sigor@sysoev.ru 
2800Sigor@sysoev.ru done:
2810Sigor@sysoev.ru 
2820Sigor@sysoev.ru     nxt_log_debug(c->socket.log, "sendbuf done");
2830Sigor@sysoev.ru 
2840Sigor@sysoev.ru     return 0;
2850Sigor@sysoev.ru }
2860Sigor@sysoev.ru 
2870Sigor@sysoev.ru 
2880Sigor@sysoev.ru static nxt_bool_t
2890Sigor@sysoev.ru nxt_sendbuf_copy(nxt_buf_mem_t *bm, nxt_buf_t *b, size_t *copied)
2900Sigor@sysoev.ru {
2910Sigor@sysoev.ru     size_t      size, bsize;
2920Sigor@sysoev.ru     nxt_bool_t  flush;
2930Sigor@sysoev.ru 
2940Sigor@sysoev.ru     flush = 0;
2950Sigor@sysoev.ru 
2960Sigor@sysoev.ru     do {
2970Sigor@sysoev.ru         nxt_prefetch(b->next);
2980Sigor@sysoev.ru 
2990Sigor@sysoev.ru         if (nxt_buf_is_mem(b)) {
3000Sigor@sysoev.ru             bsize = bm->end - bm->free;
3010Sigor@sysoev.ru             size = b->mem.free - b->mem.pos;
3020Sigor@sysoev.ru             size = nxt_min(size, bsize);
3030Sigor@sysoev.ru 
3040Sigor@sysoev.ru             nxt_memcpy(bm->free, b->mem.pos, size);
3050Sigor@sysoev.ru 
3060Sigor@sysoev.ru             *copied += size;
3070Sigor@sysoev.ru             bm->free += size;
3080Sigor@sysoev.ru 
3090Sigor@sysoev.ru             if (bm->free == bm->end) {
3100Sigor@sysoev.ru                 return 1;
3110Sigor@sysoev.ru             }
3120Sigor@sysoev.ru         }
3130Sigor@sysoev.ru 
3140Sigor@sysoev.ru         flush |= nxt_buf_is_flush(b) || nxt_buf_is_last(b);
3150Sigor@sysoev.ru 
3160Sigor@sysoev.ru         b = b->next;
3170Sigor@sysoev.ru 
3180Sigor@sysoev.ru     } while (b != NULL);
3190Sigor@sysoev.ru 
3200Sigor@sysoev.ru     return flush;
3210Sigor@sysoev.ru }
3220Sigor@sysoev.ru 
3230Sigor@sysoev.ru 
3240Sigor@sysoev.ru nxt_buf_t *
3250Sigor@sysoev.ru nxt_sendbuf_update(nxt_buf_t *b, size_t sent)
3260Sigor@sysoev.ru {
3270Sigor@sysoev.ru     size_t  size;
3280Sigor@sysoev.ru 
3290Sigor@sysoev.ru     while (b != NULL) {
3300Sigor@sysoev.ru 
3310Sigor@sysoev.ru         nxt_prefetch(b->next);
3320Sigor@sysoev.ru 
3330Sigor@sysoev.ru         if (!nxt_buf_is_sync(b)) {
3340Sigor@sysoev.ru 
3350Sigor@sysoev.ru             size = nxt_buf_used_size(b);
3360Sigor@sysoev.ru 
3370Sigor@sysoev.ru             if (size != 0) {
3380Sigor@sysoev.ru 
3390Sigor@sysoev.ru                 if (sent == 0) {
3400Sigor@sysoev.ru                     break;
3410Sigor@sysoev.ru                 }
3420Sigor@sysoev.ru 
3430Sigor@sysoev.ru                 if (sent < size) {
3440Sigor@sysoev.ru 
3450Sigor@sysoev.ru                     if (nxt_buf_is_mem(b)) {
3460Sigor@sysoev.ru                         b->mem.pos += sent;
3470Sigor@sysoev.ru                     }
3480Sigor@sysoev.ru 
3490Sigor@sysoev.ru                     if (nxt_buf_is_file(b)) {
3500Sigor@sysoev.ru                         b->file_pos += sent;
3510Sigor@sysoev.ru                     }
3520Sigor@sysoev.ru 
3530Sigor@sysoev.ru                     break;
3540Sigor@sysoev.ru                 }
3550Sigor@sysoev.ru 
3560Sigor@sysoev.ru                 /* b->mem.free is NULL in file-only buffer. */
3570Sigor@sysoev.ru                 b->mem.pos = b->mem.free;
3580Sigor@sysoev.ru 
3590Sigor@sysoev.ru                 if (nxt_buf_is_file(b)) {
3600Sigor@sysoev.ru                     b->file_pos = b->file_end;
3610Sigor@sysoev.ru                 }
3620Sigor@sysoev.ru 
3630Sigor@sysoev.ru                 sent -= size;
3640Sigor@sysoev.ru             }
3650Sigor@sysoev.ru         }
3660Sigor@sysoev.ru 
3670Sigor@sysoev.ru         b = b->next;
3680Sigor@sysoev.ru     }
3690Sigor@sysoev.ru 
3700Sigor@sysoev.ru     return b;
3710Sigor@sysoev.ru }
3720Sigor@sysoev.ru 
3730Sigor@sysoev.ru 
3740Sigor@sysoev.ru nxt_buf_t *
3751Sigor@sysoev.ru nxt_sendbuf_completion(nxt_task_t *task, nxt_work_queue_t *wq, nxt_buf_t *b,
3760Sigor@sysoev.ru     size_t sent)
3770Sigor@sysoev.ru {
3780Sigor@sysoev.ru     size_t  size;
3790Sigor@sysoev.ru 
3800Sigor@sysoev.ru     while (b != NULL) {
3810Sigor@sysoev.ru 
3820Sigor@sysoev.ru         nxt_prefetch(b->next);
3830Sigor@sysoev.ru 
3840Sigor@sysoev.ru         if (!nxt_buf_is_sync(b)) {
3850Sigor@sysoev.ru 
3860Sigor@sysoev.ru             size = nxt_buf_used_size(b);
3870Sigor@sysoev.ru 
3880Sigor@sysoev.ru             if (size != 0) {
3890Sigor@sysoev.ru 
3900Sigor@sysoev.ru                 if (sent == 0) {
3910Sigor@sysoev.ru                     break;
3920Sigor@sysoev.ru                 }
3930Sigor@sysoev.ru 
394*42Smax.romanov@nginx.com                 if (nxt_buf_is_port_mmap(b)) {
395*42Smax.romanov@nginx.com                     /*
396*42Smax.romanov@nginx.com                      * buffer has been sent to other side which is now
397*42Smax.romanov@nginx.com                      * responsible for shared memory bucket release
398*42Smax.romanov@nginx.com                      */
399*42Smax.romanov@nginx.com                     b->is_port_mmap_sent = 1;
400*42Smax.romanov@nginx.com                 }
401*42Smax.romanov@nginx.com 
4020Sigor@sysoev.ru                 if (sent < size) {
4030Sigor@sysoev.ru 
4040Sigor@sysoev.ru                     if (nxt_buf_is_mem(b)) {
4050Sigor@sysoev.ru                         b->mem.pos += sent;
4060Sigor@sysoev.ru                     }
4070Sigor@sysoev.ru 
4080Sigor@sysoev.ru                     if (nxt_buf_is_file(b)) {
4090Sigor@sysoev.ru                         b->file_pos += sent;
4100Sigor@sysoev.ru                     }
4110Sigor@sysoev.ru 
4120Sigor@sysoev.ru                     break;
4130Sigor@sysoev.ru                 }
4140Sigor@sysoev.ru 
4150Sigor@sysoev.ru                 /* b->mem.free is NULL in file-only buffer. */
4160Sigor@sysoev.ru                 b->mem.pos = b->mem.free;
4170Sigor@sysoev.ru 
4180Sigor@sysoev.ru                 if (nxt_buf_is_file(b)) {
4190Sigor@sysoev.ru                     b->file_pos = b->file_end;
4200Sigor@sysoev.ru                 }
4210Sigor@sysoev.ru 
4220Sigor@sysoev.ru                 sent -= size;
4230Sigor@sysoev.ru             }
4240Sigor@sysoev.ru         }
4250Sigor@sysoev.ru 
4264Sigor@sysoev.ru         nxt_work_queue_add(wq, b->completion_handler, task, b, b->parent);
4270Sigor@sysoev.ru 
4280Sigor@sysoev.ru         b = b->next;
4290Sigor@sysoev.ru     }
4300Sigor@sysoev.ru 
4310Sigor@sysoev.ru     return b;
4320Sigor@sysoev.ru }
433