ASF Bugzilla – Attachment 29805 Details for
Bug 54363
make time conversion caching functions thread-safe in util_time.c and mod_log_config.c
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
server/util_time.c (patched)
util_time.c (text/x-csrc), 10.54 KB, created by
Daniel Lescohier
on 2012-12-31 16:14:15 UTC
(
hide
)
Description:
server/util_time.c (patched)
Filename:
MIME Type:
Creator:
Daniel Lescohier
Created:
2012-12-31 16:14:15 UTC
Size:
10.54 KB
patch
obsolete
>/* 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. > */ > >#include "util_time.h" >#include <apr_atomic.h> > > >/* Number of characters needed to format the microsecond part of a timestamp. > * Microseconds have 6 digits plus one separator character makes 7. > * */ >#define AP_CTIME_USEC_LENGTH 7 > >/* Length of ISO 8601 date/time */ >#define AP_CTIME_COMPACT_LEN 20 > > >/* Cache for exploded values of recent timestamps > */ > >struct exploded_time_cache_element { > apr_time_exp_t xt; /* First for alignment of copies */ > apr_uint32_t key; >}; > >/* the "+ 1" is for the current second: */ >#define TIME_CACHE_SIZE (AP_TIME_RECENT_THRESHOLD + 1) > >/* Note that AP_TIME_RECENT_THRESHOLD is defined to > * be a power of two minus one in util_time.h, so that > * we can replace a modulo operation with a bitwise AND > * when hashing items into a cache of size > * AP_TIME_RECENT_THRESHOLD+1 > */ >#define TIME_CACHE_MASK (AP_TIME_RECENT_THRESHOLD) > >static struct exploded_time_cache_element exploded_cache_localtime[TIME_CACHE_SIZE]; >static struct exploded_time_cache_element exploded_cache_gmt[TIME_CACHE_SIZE]; > > >static apr_status_t cached_explode(apr_time_exp_t *xt, apr_time_t t, > struct exploded_time_cache_element *cache, > apr_status_t (*explode)(apr_time_exp_t *, apr_time_t)) >{ >#define SECONDS_MASK 0x7FFFFFFF > /* High bit is used to indicate invalid cache_element */ > const apr_uint32_t seconds = apr_time_sec(t) & SECONDS_MASK; > volatile struct exploded_time_cache_element * const cache_element = > &(cache[seconds & TIME_CACHE_MASK]); > /* The cache is implemented as a ring buffer. Each second, > * it uses a different element in the buffer. The timestamp > * in the element indicates whether the element contains the > * exploded time for the current second (vs the time > * 'now - AP_TIME_RECENT_THRESHOLD' seconds ago). If the > * cached value is for the current time, we copy the exploded time. > * After copying, we check the cache_element to see if it still has the > * same second. If so, the copy is valid, because we always set the key > * after copying the exploded time into the cache, and we're using > * memory barriers (implemented with Compare-And-Swap) > * that guarantee total memory ordering. > */ > const apr_uint32_t key = cache_element->key; > /* Above is done speculatively, no memory barrier used. > * It's doing the same thing as apr_atomic_read32, a read of > * memory marked volatile, but without doing the function call. */ > if (seconds == key && seconds != 0) { > /* seconds == 0 may mean cache is uninitialized, so don't use cache */ > *xt = cache_element->xt; > /* After copying xt, make sure cache_element was not marked invalid > * by another thread beginning an update, and that cache_element > * really contained data for our second. > * Requires memory barrier, so use CAS. */ > if (apr_atomic_cas32(&cache_element->key, seconds, seconds)==seconds) { > xt->tm_usec = (int)apr_time_usec(t); > return APR_SUCCESS; > } > } > /* Invalid cache element, so calculate the exploded time value. > This is a wait-free algorithm, and we purposely don't spin and > retry to get from the cache, we just continue and calculate it > and do useful work, instead of spinning. */ > do { > const apr_status_t r = explode(xt, t); > if (r != APR_SUCCESS) { > return r; > } > } while (0); > > /* Attempt to update the cache */ > > /* To prevent ABA problem, don't update the cache unless we have a > * newer time value (so that we never go from B->A). > * Handle cases where seconds overflows (e.g. year 2038), > * and cases where cache is uninitialized. > * Handle overflow, otherwise it will stop caching after overflow, > * until server process is restarted, which may be months later. > */ >#define OVERFLOW (((SECONDS_MASK)>>1) + 1) > if (key <= SECONDS_MASK /* another thread not updating cache_element */ > && seconds != 0 /* new key distinguishable from uninitialized */ > && ( > (seconds > key && seconds - key < OVERFLOW) || /* normal */ > (seconds < key && key - seconds > OVERFLOW) || /* overflow */ > (key == 0 && seconds < SECONDS_MASK - 0x100))) > /* cache is perhaps uninitialized, and not recent overflow */ > { > if (key == apr_atomic_cas32(&cache_element->key, ~seconds, key)) > { /* We won the race to update this cache_element. > * Above marks cache_element as invalid by using ~seconds, > * because we are starting an update: it's the start of a > * transaction. */ > cache_element->xt = *xt; > /* Finished copying, now update key with our key, > * ending the transaction. Need to use CAS for the > * memory barrier. > */ > apr_atomic_cas32(&cache_element->key, seconds, ~seconds); > } > } > return APR_SUCCESS; >} > > >AP_DECLARE(apr_status_t) ap_explode_recent_localtime(apr_time_exp_t * tm, > apr_time_t t) >{ > return cached_explode(tm, t, exploded_cache_localtime, &apr_time_exp_lt); >} > >AP_DECLARE(apr_status_t) ap_explode_recent_gmt(apr_time_exp_t * tm, > apr_time_t t) >{ > return cached_explode(tm, t, exploded_cache_gmt, &apr_time_exp_gmt); >} > >AP_DECLARE(apr_status_t) ap_recent_ctime(char *date_str, apr_time_t t) >{ > int len = APR_CTIME_LEN; > return ap_recent_ctime_ex(date_str, t, AP_CTIME_OPTION_NONE, &len); >} > >AP_DECLARE(apr_status_t) ap_recent_ctime_ex(char *date_str, apr_time_t t, > int option, int *len) >{ > /* ### This code is a clone of apr_ctime(), except that it > * uses ap_explode_recent_localtime() instead of apr_time_exp_lt(). > */ > apr_time_exp_t xt; > const char *s; > int real_year; > int needed; > > > /* Calculate the needed buffer length */ > if (option & AP_CTIME_OPTION_COMPACT) > needed = AP_CTIME_COMPACT_LEN; > else > needed = APR_CTIME_LEN; > > if (option & AP_CTIME_OPTION_USEC) { > needed += AP_CTIME_USEC_LENGTH; > } > > /* Check the provided buffer length */ > if (len && *len >= needed) { > *len = needed; > } > else { > if (len != NULL) { > *len = 0; > } > return APR_ENOMEM; > } > > /* example without options: "Wed Jun 30 21:49:08 1993" */ > /* 123456789012345678901234 */ > /* example for compact format: "1993-06-30 21:49:08" */ > /* 1234567890123456789 */ > > ap_explode_recent_localtime(&xt, t); > real_year = 1900 + xt.tm_year; > if (option & AP_CTIME_OPTION_COMPACT) { > int real_month = xt.tm_mon + 1; > *date_str++ = real_year / 1000 + '0'; > *date_str++ = real_year % 1000 / 100 + '0'; > *date_str++ = real_year % 100 / 10 + '0'; > *date_str++ = real_year % 10 + '0'; > *date_str++ = '-'; > *date_str++ = real_month / 10 + '0'; > *date_str++ = real_month % 10 + '0'; > *date_str++ = '-'; > } > else { > s = &apr_day_snames[xt.tm_wday][0]; > *date_str++ = *s++; > *date_str++ = *s++; > *date_str++ = *s++; > *date_str++ = ' '; > s = &apr_month_snames[xt.tm_mon][0]; > *date_str++ = *s++; > *date_str++ = *s++; > *date_str++ = *s++; > *date_str++ = ' '; > } > *date_str++ = xt.tm_mday / 10 + '0'; > *date_str++ = xt.tm_mday % 10 + '0'; > *date_str++ = ' '; > *date_str++ = xt.tm_hour / 10 + '0'; > *date_str++ = xt.tm_hour % 10 + '0'; > *date_str++ = ':'; > *date_str++ = xt.tm_min / 10 + '0'; > *date_str++ = xt.tm_min % 10 + '0'; > *date_str++ = ':'; > *date_str++ = xt.tm_sec / 10 + '0'; > *date_str++ = xt.tm_sec % 10 + '0'; > if (option & AP_CTIME_OPTION_USEC) { > int div; > int usec = (int)xt.tm_usec; > *date_str++ = '.'; > for (div=100000; div>0; div=div/10) { > *date_str++ = usec / div + '0'; > usec = usec % div; > } > } > if (!(option & AP_CTIME_OPTION_COMPACT)) { > *date_str++ = ' '; > *date_str++ = real_year / 1000 + '0'; > *date_str++ = real_year % 1000 / 100 + '0'; > *date_str++ = real_year % 100 / 10 + '0'; > *date_str++ = real_year % 10 + '0'; > } > *date_str++ = 0; > > return APR_SUCCESS; >} > >AP_DECLARE(apr_status_t) ap_recent_rfc822_date(char *date_str, apr_time_t t) >{ > /* ### This code is a clone of apr_rfc822_date(), except that it > * uses ap_explode_recent_gmt() instead of apr_time_exp_gmt(). > */ > apr_time_exp_t xt; > const char *s; > int real_year; > > ap_explode_recent_gmt(&xt, t); > > /* example: "Sat, 08 Jan 2000 18:31:41 GMT" */ > /* 12345678901234567890123456789 */ > > s = &apr_day_snames[xt.tm_wday][0]; > *date_str++ = *s++; > *date_str++ = *s++; > *date_str++ = *s++; > *date_str++ = ','; > *date_str++ = ' '; > *date_str++ = xt.tm_mday / 10 + '0'; > *date_str++ = xt.tm_mday % 10 + '0'; > *date_str++ = ' '; > s = &apr_month_snames[xt.tm_mon][0]; > *date_str++ = *s++; > *date_str++ = *s++; > *date_str++ = *s++; > *date_str++ = ' '; > real_year = 1900 + xt.tm_year; > /* This routine isn't y10k ready. */ > *date_str++ = real_year / 1000 + '0'; > *date_str++ = real_year % 1000 / 100 + '0'; > *date_str++ = real_year % 100 / 10 + '0'; > *date_str++ = real_year % 10 + '0'; > *date_str++ = ' '; > *date_str++ = xt.tm_hour / 10 + '0'; > *date_str++ = xt.tm_hour % 10 + '0'; > *date_str++ = ':'; > *date_str++ = xt.tm_min / 10 + '0'; > *date_str++ = xt.tm_min % 10 + '0'; > *date_str++ = ':'; > *date_str++ = xt.tm_sec / 10 + '0'; > *date_str++ = xt.tm_sec % 10 + '0'; > *date_str++ = ' '; > *date_str++ = 'G'; > *date_str++ = 'M'; > *date_str++ = 'T'; > *date_str++ = 0; > return APR_SUCCESS; >}
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Raw
Actions:
View
Attachments on
bug 54363
:
29803
|
29804
| 29805 |
29806