1 /* ====================================================================
2 * The Apache Software License, Version 1.1
4 * Copyright (c) 2001-2002 The Apache Software Foundation. All rights
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
19 * 3. The end-user documentation included with the redistribution,
20 * if any, must include the following acknowledgment:
21 * "This product includes software developed by the
22 * Apache Software Foundation (http://www.apache.org/)."
23 * Alternately, this acknowledgment may appear in the software itself,
24 * if and wherever such third-party acknowledgments normally appear.
26 * 4. The names "Apache" and "Apache Software Foundation" must
27 * not be used to endorse or promote products derived from this
28 * software without prior written permission. For written
29 * permission, please contact apache@apache.org.
31 * 5. Products derived from this software may not be called "Apache",
32 * nor may "Apache" appear in their name, without prior written
33 * permission of the Apache Software Foundation.
35 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47 * ====================================================================
49 * This software consists of voluntary contributions made by many
50 * individuals on behalf of the Apache Software Foundation. For more
51 * information on the Apache Software Foundation, please see
52 * <http://www.apache.org/>.
56 #include "util_time.h"
58 /* Cache for exploded values of recent timestamps
61 struct exploded_time_cache_element {
64 apr_int64_t t_validate; /* please see comments in cached_explode() */
67 /* the "+ 1" is for the current second: */
68 #define TIME_CACHE_SIZE (AP_TIME_RECENT_THRESHOLD + 1)
70 static struct exploded_time_cache_element exploded_cache_localtime[TIME_CACHE_SIZE];
71 static struct exploded_time_cache_element exploded_cache_gmt[TIME_CACHE_SIZE];
74 static apr_status_t cached_explode(apr_time_exp_t *xt, apr_time_t t,
75 struct exploded_time_cache_element *cache,
78 apr_int64_t seconds = t / APR_USEC_PER_SEC;
79 struct exploded_time_cache_element *cache_element =
80 &(cache[seconds % TIME_CACHE_SIZE]);
81 struct exploded_time_cache_element cache_element_snapshot;
83 /* The cache is implemented as a ring buffer. Each second,
84 * it uses a different element in the buffer. The timestamp
85 * in the element indicates whether the element contains the
86 * exploded time for the current second (vs the time
87 * 'now - AP_TIME_RECENT_THRESHOLD' seconds ago). If the
88 * cached value is for the current time, we use it. Otherwise,
89 * we compute the apr_time_exp_t and store it in this
90 * cache element. Note that the timestamp in the cache
91 * element is updated only after the exploded time. Thus
92 * if two threads hit this cache element simultaneously
93 * at the start of a new second, they'll both explode the
94 * time and store it. I.e., the writers will collide, but
95 * they'll be writing the same value.
97 if (cache_element->t >= seconds) {
98 /* There is an intentional race condition in this design:
99 * in a multithreaded app, one thread might be reading
100 * from this cache_element to resolve a timestamp from
101 * TIME_CACHE_SIZE seconds ago at the same time that
102 * another thread is copying the exploded form of the
103 * current time into the same cache_element. (I.e., the
104 * first thread might hit this element of the ring buffer
105 * just as the element is being recycled.) This can
106 * also happen at the start of a new second, if a
107 * reader accesses the cache_element after a writer
108 * has updated cache_element.t but before the writer
109 * has finished updating the whole cache_element.
111 * Rather than trying to prevent this race condition
112 * with locks, we allow it to happen and then detect
113 * and correct it. The detection works like this:
114 * Step 1: Take a "snapshot" of the cache element by
115 * copying it into a temporary buffer.
116 * Step 2: Check whether the snapshot contains consistent
117 * data: the timestamps at the start and end of
118 * the cache_element should both match the 'seconds'
119 * value that we computed from the input time.
120 * If these three don't match, then the snapshot
121 * shows the cache_element in the middle of an
122 * update, and its contents are invalid.
123 * Step 3: If the snapshot is valid, use it. Otherwise,
124 * just give up on the cache and explode the
127 memcpy(&cache_element_snapshot, cache_element,
128 sizeof(struct exploded_time_cache_element));
129 if ((seconds != cache_element_snapshot.t) ||
130 (seconds != cache_element_snapshot.t_validate)) {
131 /* Invalid snapshot */
133 return apr_time_exp_gmt(xt, t);
136 return apr_explode_localtime(xt, t);
141 memcpy(xt, &(cache_element_snapshot.xt),
142 sizeof(apr_time_exp_t));
148 r = apr_time_exp_gmt(xt, t);
151 r = apr_explode_localtime(xt, t);
153 if (!APR_STATUS_IS_SUCCESS(r)) {
156 cache_element->t = seconds;
157 memcpy(&(cache_element->xt), xt, sizeof(apr_time_exp_t));
158 cache_element->t_validate = seconds;
160 xt->tm_usec = (int)(t % APR_USEC_PER_SEC);
165 AP_DECLARE(apr_status_t) ap_explode_recent_localtime(apr_time_exp_t * tm,
168 return cached_explode(tm, t, exploded_cache_localtime, 0);
171 AP_DECLARE(apr_status_t) ap_explode_recent_gmt(apr_time_exp_t * tm,
174 return cached_explode(tm, t, exploded_cache_gmt, 1);
177 AP_DECLARE(apr_status_t) ap_recent_ctime(char *date_str, apr_time_t t)
179 /* ### This code is a clone of apr_ctime(), except that it
180 * uses ap_explode_recent_localtime() instead of apr_explode_localtime().
186 /* example: "Wed Jun 30 21:49:08 1993" */
187 /* 123456789012345678901234 */
189 ap_explode_recent_localtime(&xt, t);
190 s = &apr_day_snames[xt.tm_wday][0];
195 s = &apr_month_snames[xt.tm_mon][0];
200 *date_str++ = xt.tm_mday / 10 + '0';
201 *date_str++ = xt.tm_mday % 10 + '0';
203 *date_str++ = xt.tm_hour / 10 + '0';
204 *date_str++ = xt.tm_hour % 10 + '0';
206 *date_str++ = xt.tm_min / 10 + '0';
207 *date_str++ = xt.tm_min % 10 + '0';
209 *date_str++ = xt.tm_sec / 10 + '0';
210 *date_str++ = xt.tm_sec % 10 + '0';
212 real_year = 1900 + xt.tm_year;
213 *date_str++ = real_year / 1000 + '0';
214 *date_str++ = real_year % 1000 / 100 + '0';
215 *date_str++ = real_year % 100 / 10 + '0';
216 *date_str++ = real_year % 10 + '0';