/* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License.
*/
/* XXX Unfortunately, there are still many unsigned ints in use here, so we * XXX cannot allow more than UINT_MAX. Since some of the ints are exposed in * XXX public interfaces, a simple search and replace is not enough. * XXX It should be possible to extend that so that the total cache size can * XXX be APR_SIZE_MAX and only the object size needs to be smaller than * XXX UINT_MAX.
*/ #define SHMCB_MAX_SIZE (UINT_MAX<APR_SIZE_MAX ? UINT_MAX : APR_SIZE_MAX)
/* * Header structure - the start of the shared-mem segment
*/ typedefstruct { /* Stats for cache operations */ unsignedlong stat_stores; unsignedlong stat_replaced; unsignedlong stat_expiries; unsignedlong stat_scrolled; unsignedlong stat_retrieves_hit; unsignedlong stat_retrieves_miss; unsignedlong stat_removes_hit; unsignedlong stat_removes_miss; /* Number of subcaches */ unsignedint subcache_num; /* How many indexes each subcache's queue has */ unsignedint index_num; /* How large each subcache is, including the queue and data */ unsignedint subcache_size; /* How far into each subcache the data area is (optimisation) */ unsignedint subcache_data_offset; /* How large the data area in each subcache is (optimisation) */ unsignedint subcache_data_size;
} SHMCBHeader;
/* * Subcache structure - the start of each subcache, followed by * indexes then data
*/ typedefstruct { /* The start position and length of the cyclic buffer of indexes */ unsignedint idx_pos, idx_used; /* Same for the data area */ unsignedint data_pos, data_used;
} SHMCBSubcache;
/* * Index structure - each subcache has an array of these
*/ typedefstruct { /* absolute time this entry expires */
apr_time_t expires; /* location within the subcache's data area */ unsignedint data_pos; /* size (most logic ignores this, we keep it only to minimise memcpy) */ unsignedint data_used; /* length of the used data which contains the id */ unsignedint id_len; /* Used to mark explicitly-removed socache entries */ unsignedchar removed;
} SHMCBIndex;
/* The SHM data segment is of fixed size and stores data as follows. * * [ SHMCBHeader | Subcaches ] * * The SHMCBHeader header structure stores metadata concerning the * cache and the contained subcaches. * * Subcaches is a hash table of header->subcache_num SHMCBSubcache * structures. The hash table is indexed by SHMCB_MASK(id). Each * SHMCBSubcache structure has a fixed size (header->subcache_size), * which is determined at creation time, and looks like the following: * * [ SHMCBSubcache | Indexes | Data ] * * Each subcache is prefixed by the SHMCBSubcache structure. * * The subcache's "Data" segment is a single cyclic data buffer, of * total size header->subcache_data_size; data inside is referenced * using byte offsets. The offset marking the beginning of the cyclic * buffer is subcache->data_pos; the buffer's length is * subcache->data_used. * * "Indexes" is an array of header->index_num SHMCBIndex structures, * which is used as a cyclic queue; subcache->idx_pos gives the array * index of the first in use, subcache->idx_used gives the number in * use. Both ->idx_* values have a range of [0, header->index_num) * * Each in-use SHMCBIndex structure represents a single cached object. * The ID and data segment are stored consecutively in the subcache's * cyclic data buffer. The "Data" segment can thus be seen to * look like this, for example * * offset: [ 0 1 2 3 4 5 6 ... * contents:[ ID1 Data1 ID2 Data2 ID3 ... * * where the corresponding indices would look like: * * idx1 = { data_pos = 0, data_used = 3, id_len = 1, ...} * idx2 = { data_pos = 3, data_used = 3, id_len = 1, ...} * ...
*/
/* This macro takes a pointer to the header and a zero-based index and returns
* a pointer to the corresponding subcache. */ #define SHMCB_SUBCACHE(pHeader, num) \
(SHMCBSubcache *)(((unsignedchar *)(pHeader)) + \
ALIGNED_HEADER_SIZE + \
(num) * ((pHeader)->subcache_size))
/* This macro takes a pointer to the header and an id and returns a
* pointer to the corresponding subcache. */ #define SHMCB_MASK(pHeader, id) \
SHMCB_SUBCACHE((pHeader), *(id) & ((pHeader)->subcache_num - 1))
/* This macro takes the same params as the last, generating two outputs for use
* in ap_log_error(...). */ #define SHMCB_MASK_DBG(pHeader, id) \
*(id), (*(id) & ((pHeader)->subcache_num - 1))
/* This macro takes a pointer to a subcache and a zero-based index and returns
* a pointer to the corresponding SHMCBIndex. */ #define SHMCB_INDEX(pSubcache, num) \
(SHMCBIndex *)(((unsignedchar *)pSubcache) + \
ALIGNED_SUBCACHE_SIZE + \
(num) * ALIGNED_INDEX_SIZE)
/* This macro takes a pointer to the header and a subcache and returns a
* pointer to the corresponding data area. */ #define SHMCB_DATA(pHeader, pSubcache) \
((unsignedchar *)(pSubcache) + (pHeader)->subcache_data_offset)
/* * Cyclic functions - assists in "wrap-around"/modulo logic
*/
/* A "normal-to-cyclic" memcpy. */ staticvoid shmcb_cyclic_ntoc_memcpy(unsignedint buf_size, unsignedchar *data, unsignedint dest_offset, constunsignedchar *src, unsignedint src_len)
{ if (dest_offset + src_len < buf_size) /* It be copied all in one go */
memcpy(data + dest_offset, src, src_len); else { /* Copy the two splits */
memcpy(data + dest_offset, src, buf_size - dest_offset);
memcpy(data, src + buf_size - dest_offset,
src_len + dest_offset - buf_size);
}
}
/* A "cyclic-to-normal" memcpy. */ staticvoid shmcb_cyclic_cton_memcpy(unsignedint buf_size, unsignedchar *dest, constunsignedchar *data, unsignedint src_offset, unsignedint src_len)
{ if (src_offset + src_len < buf_size) /* It be copied all in one go */
memcpy(dest, data + src_offset, src_len); else { /* Copy the two splits */
memcpy(dest, data + src_offset, buf_size - src_offset);
memcpy(dest + buf_size - src_offset, data,
src_len + src_offset - buf_size);
}
}
/* A memcmp against a cyclic data buffer. Compares SRC of length * SRC_LEN against the contents of cyclic buffer DATA (which is of
* size BUF_SIZE), starting at offset DEST_OFFSET. Got that? Good. */ staticint shmcb_cyclic_memcmp(unsignedint buf_size, unsignedchar *data, unsignedint dest_offset, constunsignedchar *src, unsignedint src_len)
{ if (dest_offset + src_len < buf_size) /* It be compared all in one go */ return memcmp(data + dest_offset, src, src_len); else { /* Compare the two splits */ int diff;
/* Use anonymous shm by default, fall back on name-based. */
rv = apr_shm_create(&ctx->shm, ctx->shm_size, NULL, p); if (APR_STATUS_IS_ENOTIMPL(rv)) { /* If anon shm isn't supported, fail if no named file was * configured successfully; the ap_server_root_relative call
* above will return NULL for invalid paths. */ if (ctx->data_file == NULL) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00818) "Could not use anonymous shm for '%s' cache", namespace);
ctx->shm = NULL; return APR_EINVAL;
}
/* For a name-based segment, remove it first in case of a
* previous unclean shutdown. */
apr_shm_remove(ctx->data_file, p);
/* Sanity check the input */ if (total_len > header->subcache_data_size) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00844) "inserting socache entry larger (%d) than subcache data area (%d)",
total_len, header->subcache_data_size); return -1;
}
/* First reclaim space from removed and expired records. */
shmcb_subcache_expire(s, header, subcache, apr_time_now());
/* Loop until there is enough space to insert * XXX: This should first compress out-of-order expiries and * removed records, and then force-remove oldest-first
*/ if (header->subcache_data_size - subcache->data_used < total_len
|| subcache->idx_used == header->index_num) {
/* HERE WE ASSUME THAT THE NEW ENTRY SHOULD GO ON THE END! I'M NOT * CHECKING WHETHER IT SHOULD BE GENUINELY "INSERTED" SOMEWHERE. * * We aught to fix that. httpd (never mind third party modules) * does not promise to perform any processing in date order * (c.f. FAQ "My log entries are not in date order!")
*/ /* Insert the id */
id_offset = SHMCB_CYCLIC_INCREMENT(subcache->data_pos, subcache->data_used,
header->subcache_data_size);
shmcb_cyclic_ntoc_memcpy(header->subcache_data_size,
SHMCB_DATA(header, subcache), id_offset,
id, id_len);
subcache->data_used += id_len; /* Insert the data */
data_offset = SHMCB_CYCLIC_INCREMENT(subcache->data_pos, subcache->data_used,
header->subcache_data_size);
shmcb_cyclic_ntoc_memcpy(header->subcache_data_size,
SHMCB_DATA(header, subcache), data_offset,
data, data_len);
subcache->data_used += data_len; /* Insert the index */
new_idx = SHMCB_CYCLIC_INCREMENT(subcache->idx_pos, subcache->idx_used,
header->index_num);
idx = SHMCB_INDEX(subcache, new_idx);
idx->expires = expiry;
idx->data_pos = id_offset;
idx->data_used = total_len;
idx->id_len = id_len;
idx->removed = 0;
subcache->idx_used++;
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00847) "insert happened at idx=%d, data=(%u:%u)", new_idx,
id_offset, data_offset);
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00848) "finished insert, subcache: idx_pos/idx_used=%d/%d, " "data_pos/data_used=%d/%d",
subcache->idx_pos, subcache->idx_used,
subcache->data_pos, subcache->data_used); return 0;
}
while (loop < subcache->idx_used) {
SHMCBIndex *idx = SHMCB_INDEX(subcache, pos);
/* Only consider 'idx' if the id matches, and the "removed" * flag isn't set, and the record is not expired. * Check the data length too to avoid a buffer overflow * in case of corruption, which should be impossible,
* but it's cheap to be safe. */ if (!idx->removed
&& idx->id_len == idlen
&& (idx->data_used - idx->id_len) <= *destlen
&& shmcb_cyclic_memcmp(header->subcache_data_size,
SHMCB_DATA(header, subcache),
idx->data_pos, id, idx->id_len) == 0) {
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00849) "match at idx=%d, data=%d", pos, idx->data_pos); if (idx->expires > now) { unsignedint data_offset;
/* Find the offset of the data segment, after the id */
data_offset = SHMCB_CYCLIC_INCREMENT(idx->data_pos,
idx->id_len,
header->subcache_data_size);
*destlen = idx->data_used - idx->id_len;
/* Copy out the data */
shmcb_cyclic_cton_memcpy(header->subcache_data_size,
dest, SHMCB_DATA(header, subcache),
data_offset, *destlen);
/* Only consider 'idx' if the id matches, and the "removed"
* flag isn't set. */ if (!idx->removed && idx->id_len == idlen
&& shmcb_cyclic_memcmp(header->subcache_data_size,
SHMCB_DATA(header, subcache),
idx->data_pos, id, idx->id_len) == 0) {
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00852) "possible match at idx=%d, data=%d", pos, idx->data_pos);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.