View | Details | Raw Unified | Return to bug 39380
Collapse All | Expand All

(-)mod_disk_cache.c.loadstore (-13 / +271 lines)
Lines 65-70 Link Here
65
                               apr_file_t *file);
65
                               apr_file_t *file);
66
66
67
/*
67
/*
68
 * Modified file bucket implementation to be able to deliver files
69
 * while caching.
70
 */
71
72
/* Derived from apr_buckets_file.c */
73
74
#define BUCKET_IS_DISKCACHE(e)        ((e)->type == &bucket_type_diskcache)
75
APU_DECLARE_DATA const apr_bucket_type_t bucket_type_diskcache;
76
77
static void diskcache_bucket_destroy(void *data)
78
{
79
    diskcache_bucket_data *f = data;
80
81
    if (apr_bucket_shared_destroy(f)) {
82
        /* no need to close files here; it will get
83
         * done automatically when the pool gets cleaned up */
84
        apr_bucket_free(f);
85
    }
86
}
87
88
89
/* The idea here is to convert diskcache buckets to regular file buckets
90
   as data becomes available */
91
/* FIXME: Maybe we should care about the block argument, right now we're
92
          always blocking */
93
static apr_status_t diskcache_bucket_read(apr_bucket *e, const char **str,
94
                                          apr_size_t *len, 
95
                                          apr_read_type_e block)
96
{
97
    diskcache_bucket_data *a = e->data;
98
    apr_file_t *f = a->fd;
99
    apr_bucket *b = NULL;
100
    char *buf;
101
    apr_status_t rv;
102
    apr_finfo_t finfo;
103
    apr_size_t filelength = e->length; /* bytes remaining in file past offset */
104
    apr_off_t fileoffset = e->start;
105
    apr_off_t fileend;
106
    apr_size_t available;
107
#if APR_HAS_THREADS && !APR_HAS_XTHREAD_FILES
108
    apr_int32_t flags;
109
#endif
110
111
#if APR_HAS_THREADS && !APR_HAS_XTHREAD_FILES
112
    if ((flags = apr_file_flags_get(f)) & APR_XTHREAD) {
113
        /* this file descriptor is shared across multiple threads and
114
         * this OS doesn't support that natively, so as a workaround
115
         * we must reopen the file into a->readpool */
116
        const char *fname;
117
        apr_file_name_get(&fname, f);
118
119
        rv = apr_file_open(&f, fname, (flags & ~APR_XTHREAD), 0, a->readpool);
120
        if (rv != APR_SUCCESS)
121
            return rv;
122
123
        a->fd = f;
124
    }
125
#endif
126
127
    /* in case we die prematurely */
128
    *str = NULL;
129
    *len = 0;
130
131
    while(1) {
132
        /* Figure out how big the file is right now, sit here until
133
           it's grown enough or we get bored */
134
        fileend = 0;
135
        rv = apr_file_seek(f, APR_END, &fileend);
136
        if(rv != APR_SUCCESS) {
137
            return rv;
138
        }
139
140
        if(fileend >= fileoffset + MIN(filelength, CACHE_BUF_SIZE)) {
141
            break;
142
        }
143
144
        rv = apr_file_info_get(&finfo, APR_FINFO_MTIME, f);
145
        if(rv != APR_SUCCESS ||
146
                finfo.mtime < (apr_time_now() - a->updtimeout) ) 
147
        {
148
            return APR_EGENERAL;
149
        }
150
        apr_sleep(CACHE_LOOP_SLEEP);
151
    }
152
153
    /* Convert this bucket to a zero-length heap bucket so we won't be called
154
       again */
155
    buf = apr_bucket_alloc(0, e->list);
156
    apr_bucket_heap_make(e, buf, 0, apr_bucket_free);
157
158
    /* Wrap as much as possible into a regular file bucket */
159
    available = MIN(filelength, fileend-fileoffset);
160
    b = apr_bucket_file_create(f, fileoffset, available, a->readpool, e->list);
161
    APR_BUCKET_INSERT_AFTER(e, b);
162
163
    /* Put any remains in yet another bucket */
164
    if(available < filelength) {
165
        e=b;
166
        /* for efficiency, we can just build a new apr_bucket struct
167
         * to wrap around the existing bucket */
168
        b = apr_bucket_alloc(sizeof(*b), e->list);
169
        b->start  = fileoffset + available;
170
        b->length = filelength - available;
171
        b->data   = a;
172
        b->type   = &bucket_type_diskcache;
173
        b->free   = apr_bucket_free;
174
        b->list   = e->list;
175
        APR_BUCKET_INSERT_AFTER(e, b);
176
    }
177
    else {
178
        diskcache_bucket_destroy(a);
179
    }
180
181
    *str = buf;
182
    return APR_SUCCESS;
183
}
184
185
static apr_bucket * diskcache_bucket_make(apr_bucket *b,
186
                                                apr_file_t *fd,
187
                                                apr_off_t offset,
188
                                                apr_size_t len, 
189
                                                apr_interval_time_t timeout,
190
                                                apr_pool_t *p)
191
{
192
    diskcache_bucket_data *f;
193
194
    f = apr_bucket_alloc(sizeof(*f), b->list);
195
    f->fd = fd;
196
    f->readpool = p;
197
    f->updtimeout = timeout;
198
199
    b = apr_bucket_shared_make(b, f, offset, len);
200
    b->type = &bucket_type_diskcache;
201
202
    return b;
203
}
204
205
static apr_bucket * diskcache_bucket_create(apr_file_t *fd,
206
                                                  apr_off_t offset,
207
                                                  apr_size_t len, 
208
                                                  apr_interval_time_t timeout,
209
                                                  apr_pool_t *p,
210
                                                  apr_bucket_alloc_t *list)
211
{
212
    apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
213
214
    APR_BUCKET_INIT(b);
215
    b->free = apr_bucket_free;
216
    b->list = list;
217
    return diskcache_bucket_make(b, fd, offset, len, timeout, p);
218
}
219
220
221
/* FIXME: This is probably only correct for the first case, that seems
222
   to be the one that occurs all the time... */
223
static apr_status_t diskcache_bucket_setaside(apr_bucket *data, 
224
                                              apr_pool_t *reqpool)
225
{
226
    diskcache_bucket_data *a = data->data;
227
    apr_file_t *fd = NULL;
228
    apr_file_t *f = a->fd;
229
    apr_pool_t *curpool = apr_file_pool_get(f);
230
231
    if (apr_pool_is_ancestor(curpool, reqpool)) {
232
        return APR_SUCCESS;
233
    }
234
235
    if (!apr_pool_is_ancestor(a->readpool, reqpool)) {
236
        /* FIXME: Figure out what needs to be done here */
237
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
238
                "disk_cache: diskcache_bucket_setaside: FIXME1");
239
        a->readpool = reqpool;
240
    }
241
242
    /* FIXME: Figure out what needs to be done here */
243
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
244
            "disk_cache: diskcache_bucket_setaside: FIXME2");
245
246
    apr_file_setaside(&fd, f, reqpool);
247
    a->fd = fd;
248
    return APR_SUCCESS;
249
}
250
251
APU_DECLARE_DATA const apr_bucket_type_t bucket_type_diskcache = {
252
    "DISKCACHE", 5, APR_BUCKET_DATA,
253
    diskcache_bucket_destroy,
254
    diskcache_bucket_read,
255
    diskcache_bucket_setaside,
256
    apr_bucket_shared_split,
257
    apr_bucket_shared_copy
258
};
259
260
/* From apr_brigade.c */
261
262
/* A "safe" maximum bucket size, 1Gb */
263
#define MAX_BUCKET_SIZE (0x40000000)
264
265
static apr_bucket * diskcache_brigade_insert(apr_bucket_brigade *bb,
266
                                                   apr_file_t *f, apr_off_t
267
                                                   start, apr_off_t length,
268
                                                   apr_interval_time_t timeout,
269
                                                   apr_pool_t *p)
270
{
271
    apr_bucket *e;
272
273
    if (length < MAX_BUCKET_SIZE) {
274
        e = diskcache_bucket_create(f, start, (apr_size_t)length, timeout, p, 
275
                bb->bucket_alloc);
276
    }
277
    else {
278
        /* Several buckets are needed. */        
279
        e = diskcache_bucket_create(f, start, MAX_BUCKET_SIZE, timeout, p, 
280
                bb->bucket_alloc);
281
282
        while (length > MAX_BUCKET_SIZE) {
283
            apr_bucket *ce;
284
            apr_bucket_copy(e, &ce);
285
            APR_BRIGADE_INSERT_TAIL(bb, ce);
286
            e->start += MAX_BUCKET_SIZE;
287
            length -= MAX_BUCKET_SIZE;
288
        }
289
        e->length = (apr_size_t)length; /* Resize just the last bucket */
290
    }
291
292
    APR_BRIGADE_INSERT_TAIL(bb, e);
293
    return e;
294
}
295
296
/* --------------------------------------------------------------- */
297
298
/*
68
 * Local static functions
299
 * Local static functions
69
 */
300
 */
70
301
Lines 542-548 Link Here
542
                return CACHE_EDECLINED;
773
                return CACHE_EDECLINED;
543
            }
774
            }
544
        }
775
        }
545
        if(dobj->file_size == dobj->initial_size) {
776
        if(dobj->file_size > 0) {
546
            break;
777
            break;
547
        }
778
        }
548
        apr_sleep(CACHE_LOOP_SLEEP);
779
        apr_sleep(CACHE_LOOP_SLEEP);
Lines 989-995 Link Here
989
    apr_bucket *e;
1220
    apr_bucket *e;
990
    disk_cache_object_t *dobj = (disk_cache_object_t*) h->cache_obj->vobj;
1221
    disk_cache_object_t *dobj = (disk_cache_object_t*) h->cache_obj->vobj;
991
1222
992
    apr_brigade_insert_file(bb, dobj->fd, 0, dobj->file_size, p);
1223
    /* Insert as much as possible as regular file (ie. sendfile():able) */
1224
    if(dobj->file_size > 0) {
1225
        if(apr_brigade_insert_file(bb, dobj->fd, 0, 
1226
                                   dobj->file_size, p) == NULL) 
1227
        {
1228
            return APR_ENOMEM;
1229
        }
1230
    }
1231
1232
    /* Insert any remainder as read-while-caching bucket */
1233
    if(dobj->file_size < dobj->initial_size) {
1234
        if(diskcache_brigade_insert(bb, dobj->fd, dobj->file_size, 
1235
                                    dobj->initial_size - dobj->file_size,
1236
                                    dobj->updtimeout, p
1237
                    ) == NULL) 
1238
        {
1239
            return APR_ENOMEM;
1240
        }
1241
    }
993
1242
994
    e = apr_bucket_eos_create(bb->bucket_alloc);
1243
    e = apr_bucket_eos_create(bb->bucket_alloc);
995
    APR_BRIGADE_INSERT_TAIL(bb, e);
1244
    APR_BRIGADE_INSERT_TAIL(bb, e);
Lines 1042-1048 Link Here
1042
        rv = apr_file_open(fd, filename, flags, 
1291
        rv = apr_file_open(fd, filename, flags, 
1043
                APR_FPROT_UREAD | APR_FPROT_UWRITE, r->pool);
1292
                APR_FPROT_UREAD | APR_FPROT_UWRITE, r->pool);
1044
1293
1045
        /* FIXME: Debug */
1046
        ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, r->server,
1294
        ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, r->server,
1047
                     "disk_cache: open_new_file: Opening %s", filename);
1295
                     "disk_cache: open_new_file: Opening %s", filename);
1048
1296
Lines 1471-1476 Link Here
1471
    disk_cache_conf *conf = ap_get_module_config(r->server->module_config,
1719
    disk_cache_conf *conf = ap_get_module_config(r->server->module_config,
1472
                                                 &disk_cache_module);
1720
                                                 &disk_cache_module);
1473
1721
1722
    dobj->store_body_called++;
1723
1474
    if(r->no_cache) {
1724
    if(r->no_cache) {
1475
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1725
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1476
                     "disk_cache: store_body called for URL %s even though"
1726
                     "disk_cache: store_body called for URL %s even though"
Lines 1484-1506 Link Here
1484
        return APR_SUCCESS;
1734
        return APR_SUCCESS;
1485
    }
1735
    }
1486
1736
1487
    if(dobj->skipstore) {
1737
    if(!dobj->skipstore && dobj->fd == NULL) {
1488
        /* Someone else beat us to storing this object */
1489
        /* FIXME: Read-while-caching here */
1490
        return APR_SUCCESS;
1491
    }
1492
1493
    if(!dobj->fd) {
1494
        rv = open_new_file(r, dobj->datafile, &(dobj->fd), conf);
1738
        rv = open_new_file(r, dobj->datafile, &(dobj->fd), conf);
1495
        if(rv == CACHE_EEXIST) {
1739
        if(rv == CACHE_EEXIST) {
1496
            /* Someone else beat us to storing this */
1740
            /* Someone else beat us to storing this */
1497
            /* FIXME: Read-while-caching here later on */
1741
            dobj->skipstore = TRUE;
1498
            return APR_SUCCESS;
1499
        }
1742
        }
1500
        else if(rv != APR_SUCCESS) {
1743
        else if(rv != APR_SUCCESS) {
1501
            return rv;
1744
            return rv;
1502
        }
1745
        }
1503
        dobj->file_size = 0;
1746
        else {
1747
            dobj->file_size = 0;
1748
        }
1749
    }
1750
1751
    if(dobj->skipstore) {
1752
        /* Someone else beat us to storing this object */
1753
        if(dobj->store_body_called == 1 &&
1754
                dobj->initial_size > 0 &&
1755
                APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb)) )
1756
        {   
1757
            /* Yay, we can replace the body with the cached instance */
1758
            return replace_brigade_with_cache(h, r, bb);
1759
        }
1760
1761
        return APR_SUCCESS;
1504
    }
1762
    }
1505
1763
1506
    /* Check if this is a complete single sequential file, eligable for
1764
    /* Check if this is a complete single sequential file, eligable for
(-)mod_disk_cache.h.loadstore (-1 / +16 lines)
Lines 84-90 Link Here
84
84
85
    apr_interval_time_t updtimeout; /* Cache update timeout */
85
    apr_interval_time_t updtimeout; /* Cache update timeout */
86
86
87
    int skipstore;           /* Set if we should skip storing stuff */
87
    int skipstore;              /* Set if we should skip storing stuff */
88
    int store_body_called;      /* Number of times store_body() has executed */
88
} disk_cache_object_t;
89
} disk_cache_object_t;
89
90
90
91
Lines 114-117 Link Here
114
#define CACHE_EEXIST (APR_OS_START_USERERR+3)
115
#define CACHE_EEXIST (APR_OS_START_USERERR+3)
115
116
116
117
118
typedef struct diskcache_bucket_data diskcache_bucket_data;
119
struct diskcache_bucket_data {
120
    /* Number of buckets using this memory */
121
    apr_bucket_refcount  refcount;
122
    apr_file_t  *fd;
123
    /* The pool into which any needed structures should
124
     *  be created while reading from this file bucket */
125
    apr_pool_t *readpool;
126
    /* Cache update timeout */
127
    apr_interval_time_t updtimeout;
128
129
};
130
131
117
#endif /*MOD_DISK_CACHE_H*/
132
#endif /*MOD_DISK_CACHE_H*/

Return to bug 39380