Project

General

Profile

Feature #1795 ยป gthread-freebsd-sendfile-r2294.diff

peto, 2008-10-10 05:00

View differences:

src/base.h (.../trunk/lighttpd-1.5.0) (revision 546)
563 563
	NETWORK_BACKEND_POSIX_AIO,
564 564
	NETWORK_BACKEND_GTHREAD_AIO,
565 565
	NETWORK_BACKEND_GTHREAD_SENDFILE,
566
	NETWORK_BACKEND_GTHREAD_FREEBSD_SENDFILE,
566 567

  
567 568
	NETWORK_BACKEND_FREEBSD_SENDFILE,
568 569
	NETWORK_BACKEND_SOLARIS_SENDFILEV,
src/network.c (.../trunk/lighttpd-1.5.0) (revision 546)
103 103
	},
104 104

  
105 105
	{
106
		NETWORK_BACKEND_GTHREAD_FREEBSD_SENDFILE,
107
		"gthread-freebsd-sendfile",
108
		NULL,
109
#if defined USE_WRITE && defined USE_GTHREAD_AIO && defined USE_GTHREAD_FREEBSD_SENDFILE
110
		BACKEND_HANDLERS(read, gthreadwritev)
111
#else
112
		NULL, NULL
113
#endif
114
	},
115

  
116
	{
106 117
		NETWORK_BACKEND_WRITEV,
107 118
		"writev",
108 119
		NULL,
src/network_backends.h (.../trunk/lighttpd-1.5.0) (revision 546)
28 28
LI_API NETWORK_BACKEND_WRITE(posixaio);
29 29
LI_API NETWORK_BACKEND_WRITE(gthreadaio);
30 30
LI_API NETWORK_BACKEND_WRITE(gthreadsendfile);
31
LI_API NETWORK_BACKEND_WRITE(gthreadwritev);
31 32
LI_API NETWORK_BACKEND_WRITE(freebsdsendfile);
32 33
LI_API NETWORK_BACKEND_WRITE(solarissendfilev);
33 34

  
src/Makefile.am (.../trunk/lighttpd-1.5.0) (revision 546)
66 66
      network_linux_aio.c \
67 67
      network_posix_aio.c \
68 68
      network_gthread_aio.c network_gthread_sendfile.c \
69
      network_gthread_freebsd_sendfile.c \
69 70
      http_resp.c http_resp_parser.c \
70 71
      http_req.c http_req_parser.c \
71 72
      http_req_range.c http_req_range_parser.c timing.c
src/settings.h (.../trunk/lighttpd-1.5.0) (revision 546)
72 72
# include <sys/uio.h>
73 73
#endif
74 74

  
75
#if defined(USE_FREEBSD_SENDFILE) && defined(USE_GTHREAD)
76
#  define USE_GTHREAD_FREEBSD_SENDFILE
77
#endif
78

  
79

  
75 80
#if defined HAVE_SYS_SENDFILE_H && defined HAVE_SENDFILEV && defined HAVE_WRITEV && defined(__sun)
76 81
# define USE_SOLARIS_SENDFILEV
77 82
# include <sys/sendfile.h>
src/server.c (.../trunk/lighttpd-1.5.0) (revision 546)
103 103
gpointer stat_cache_thread(gpointer );
104 104
gpointer network_gthread_aio_read_thread(gpointer );
105 105
gpointer network_gthread_sendfile_read_thread(gpointer );
106
gpointer network_gthread_freebsd_sendfile_read_thread(gpointer );
106 107
gpointer linux_aio_read_thread(gpointer );
107 108
#endif
108 109

  
......
1689 1709
		need_joblist_queue_thread = 1;
1690 1710
		break;
1691 1711
#endif
1712
#ifdef USE_GTHREAD_FREEBSD_SENDFILE
1713
	case NETWORK_BACKEND_GTHREAD_FREEBSD_SENDFILE:
1714
		aio_write_threads = calloc(srv->srvconf.max_read_threads, sizeof(*aio_write_threads));
1715
		for (i = 0; i < srv->srvconf.max_read_threads; i++) {
1716
			aio_write_threads[i] = g_thread_create_full(network_gthread_freebsd_sendfile_read_thread, srv, LI_THREAD_STACK_SIZE, 1, TRUE, G_THREAD_PRIORITY_NORMAL, &gerr);
1717
			if (gerr) {
1718
				ERROR("g_thread_create failed: %s", gerr->message);
1719

  
1720
				return -1;
1721
			}
1722
		}
1723
		need_joblist_queue_thread = 1;
1724
		break;
1725
#endif
1692 1726
#ifdef USE_POSIX_AIO
1693 1727
	case NETWORK_BACKEND_POSIX_AIO:
1694 1728
		srv->posix_aio_iocbs = calloc(srv->srvconf.max_read_threads, sizeof(*srv->posix_aio_iocbs));
src/network_gthread_freebsd_sendfile.c (.../trunk/lighttpd-1.5.0) (revision 546)
1
/*
2
 * make sure _GNU_SOURCE is defined
3
 */
4
#include "settings.h"
5
#include "network_backends.h"
6
#if defined(USE_GTHREAD_FREEBSD_SENDFILE)
7
#include <sys/types.h>
8
#include <sys/stat.h>
9
#ifdef HAVE_SYS_TIME_H
10
#include <sys/time.h>
11
#endif
12

  
13
#include <errno.h>
14
#include <fcntl.h>
15
#include <string.h>
16
#include <stdlib.h>
17
#include <fcntl.h>
18
#include <assert.h>
19

  
20
#include "network.h"
21
#include "fdevent.h"
22
#include "log.h"
23
#include "stat_cache.h"
24
#include "joblist.h"
25
#include "timing.h"
26

  
27
#include "sys-files.h"
28
#include "sys-socket.h"
29

  
30
typedef struct {
31
	chunk *c;
32

  
33
	void *con;
34

  
35
	int sock_fd;
36
} write_job;
37

  
38
static write_job *write_job_init() {
39
	write_job *wj = calloc(1, sizeof(*wj));
40

  
41
	return wj;
42
}
43

  
44
static void write_job_free(write_job *wj) {
45
	if (!wj) return;
46

  
47
	free(wj);
48
}
49

  
50
#define kByte * (1024)
51
#define MByte * (1024 kByte)
52

  
53
/**
54
 * log the time-stamps of the different stages
55
 */
56
static void timing_print(server *srv, connection *con) {
57
	if (!srv->srvconf.log_timing) return;
58

  
59
	TRACE("write-start: %ld.%06ld "
60
	      "read-queue-wait: %ld ms "
61
	      "read-time: %ld ms "
62
	      "write-time: %ld ms ",
63
	       con->timestamps[TIME_SEND_WRITE_START].tv_sec,
64
	       con->timestamps[TIME_SEND_WRITE_START].tv_usec,
65

  
66
	       TIME_DIFF(TIME_SEND_ASYNC_READ_START, TIME_SEND_ASYNC_READ_QUEUED),
67
	       TIME_DIFF(TIME_SEND_ASYNC_READ_END, TIME_SEND_ASYNC_READ_START),
68
	       TIME_DIFF(TIME_SEND_WRITE_END, TIME_SEND_ASYNC_READ_END_QUEUED)
69
       );
70
}
71

  
72
/**
73
 * a backend which calls sendfile() in a thread
74
 */
75

  
76
gpointer network_gthread_freebsd_sendfile_read_thread(gpointer _srv) {
77
        server *srv = (server *)_srv;
78

  
79
	GAsyncQueue * inq;
80
	GAsyncQueue * outq;
81

  
82
	g_async_queue_ref(srv->joblist_queue);
83
	g_async_queue_ref(srv->aio_write_queue);
84

  
85
	outq = srv->joblist_queue;
86
	inq = srv->aio_write_queue;
87

  
88
	/* */
89
	while (!srv->is_shutdown) {
90
		GTimeVal ts;
91
        	write_job *wj = NULL;
92

  
93
		/* wait one second as the poll() */
94
		g_get_current_time(&ts);
95
		g_time_val_add(&ts, 500 * 1000);
96

  
97
		if ((wj = g_async_queue_timed_pop(inq, &ts))) {
98
			/* let's see what we have to stat */
99
			ssize_t r;
100
			off_t offset;
101
			size_t toSend;
102
			chunk *c = wj->c;
103
			connection *con = wj->con;
104
			off_t max_toSend = 512 kByte; /** should be larger than the send buffer */
105

  
106
			offset = c->file.start + c->offset;
107

  
108
			toSend = c->file.length - c->offset > max_toSend ?
109
				max_toSend : c->file.length - c->offset;
110

  
111
			timing_log(srv, con, TIME_SEND_ASYNC_READ_START);
112

  
113
			r = 0;
114
			if (-1 == sendfile(c->file.fd, wj->sock_fd, offset, toSend, NULL, &r, 0)) {
115
				switch (errno) {
116
				case EAGAIN:
117
				case EINTR:
118
					c->async.ret_val = NETWORK_STATUS_WAIT_FOR_EVENT;
119
					break;
120
                                case ENOTCONN:
121
					c->async.ret_val = NETWORK_STATUS_CONNECTION_CLOSE;
122
					break;
123
				default:
124
					ERROR("sendfile(%s) failed: %s (%d)", 
125
						SAFE_BUF_STR(c->file.name),
126
						strerror(errno), errno);
127
					c->async.ret_val = NETWORK_STATUS_FATAL_ERROR;
128
					break;
129
				}
130
			} else if (r == 0) {
131
				c->async.ret_val = NETWORK_STATUS_CONNECTION_CLOSE;
132
			} else {
133
				c->async.written = r;
134
			}
135

  
136
			timing_log(srv, con, TIME_SEND_ASYNC_READ_END);
137
			timing_log(srv, con, TIME_SEND_ASYNC_READ_END_QUEUED);
138

  
139
			/* read async, write as usual */ 
140
			g_async_queue_push(outq, wj->con);
141

  
142
			write_job_free(wj);
143
		}
144
	}
145
	
146
	g_async_queue_unref(srv->aio_write_queue);
147
	g_async_queue_unref(srv->joblist_queue);
148

  
149
	return NULL;
150

  
151
}
152

  
153

  
154
NETWORK_BACKEND_WRITE(gthreadwritev) {
155
	chunk *c, *tc;
156
	size_t chunks_written = 0;
157

  
158
	for(c = cq->first; c; c = c->next, chunks_written++) {
159
		int chunk_finished = 0;
160
		network_status_t ret;
161

  
162
		switch(c->type) {
163
		case MEM_CHUNK:
164
			ret = network_write_chunkqueue_writev_mem(srv, con, sock, cq, c);
165

  
166
			/* check which chunks are finished now */
167
			for (tc = c; tc && chunk_is_done(tc); tc = tc->next) {
168
				/* skip the first c->next as that will be done by the c = c->next in the other for()-loop */
169
				if (chunk_finished) {
170
					c = c->next;
171
				} else {
172
					chunk_finished = 1;
173
				}
174
			}
175

  
176
			if (ret != NETWORK_STATUS_SUCCESS) {
177
				return ret;
178
			}
179

  
180
			break;
181
		case FILE_CHUNK: {
182
			/* we might be on our way back from the async request and have a status-code */
183
			if (c->async.ret_val != NETWORK_STATUS_UNSET) {
184
				ret = c->async.ret_val;
185

  
186
				c->async.ret_val = NETWORK_STATUS_UNSET;
187

  
188
				return ret;
189
			}
190

  
191
			/* open file if not already opened */
192
			if (-1 == c->file.fd) {
193
				if (-1 == (c->file.fd = open(c->file.name->ptr, O_RDONLY /* | O_DIRECT */ | (srv->srvconf.use_noatime ? O_NOATIME : 0)))) {
194
					ERROR("opening '%s' failed: %s", SAFE_BUF_STR(c->file.name), strerror(errno));
195

  
196
					return NETWORK_STATUS_FATAL_ERROR;
197
				}
198
#ifdef FD_CLOEXEC
199
				fcntl(c->file.fd, F_SETFD, FD_CLOEXEC);
200
#endif
201
#if defined(HAVE_POSIX_FADVISE) && defined(POSIX_FADV_SEQUENTIAL)
202
				/* tell the kernel that we want to stream the file */
203
				if (-1 == posix_fadvise(c->file.fd, c->file.start, c->file.length, POSIX_FADV_SEQUENTIAL)) {
204
					if (ENOSYS != errno) {
205
						ERROR("posix_fadvise(%s) failed: %s (%d)", c->file.name->ptr, strerror(errno), errno);
206
					}
207
				}
208
#endif
209
			}
210

  
211
			if (c->async.written > 0) {
212
				/* the backend has written something */
213

  
214
				c->offset += c->async.written; /* global offset in the file */
215
				cq->bytes_out += c->async.written;
216

  
217
				/* this block is sent, get a new one */
218
				timing_log(srv, con, TIME_SEND_WRITE_END);
219

  
220
				timing_print(srv, con);
221

  
222
				c->async.written = -1;
223
			}
224

  
225
			if (c->offset == c->file.length) {
226
				chunk_finished = 1;
227

  
228
				if (c->file.fd != -1) {
229
					close(c->file.fd);
230
					c->file.fd = -1;
231
				}
232
			} else {
233
				/* start this write */
234
				write_job *wj;
235

  
236
				timing_log(srv, con, TIME_SEND_WRITE_START);
237
				wj = write_job_init();
238
				wj->c = c;
239
				wj->con = con;
240
				wj->sock_fd = sock->fd;
241

  
242
				c->async.written = -1;
243
				c->async.ret_val = NETWORK_STATUS_UNSET;
244

  
245
				g_async_queue_push(srv->aio_write_queue, wj);
246

  
247
				timing_log(srv, con, TIME_SEND_ASYNC_READ_QUEUED);
248

  
249
				return NETWORK_STATUS_WAIT_FOR_AIO_EVENT;
250
			}
251

  
252
			break;
253
		}
254
		default:
255

  
256
			log_error_write(srv, __FILE__, __LINE__, "ds", c, "type not known");
257

  
258
			return NETWORK_STATUS_FATAL_ERROR;
259
		}
260

  
261
		if (!chunk_finished) {
262
			/* not finished yet */
263

  
264
			return NETWORK_STATUS_WAIT_FOR_EVENT;
265
		}
266
	}
267

  
268
	return NETWORK_STATUS_SUCCESS;
269
}
270

  
271
#endif
272

  
    (1-1/1)