Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(14)

Side by Side Diff: source/common/unifiedcache.cpp

Issue 845603002: Update ICU to 54.1 step 1 (Closed) Base URL: https://chromium.googlesource.com/chromium/deps/icu.git@master
Patch Set: remove unusued directories Created 5 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « source/common/unifiedcache.h ('k') | source/common/uniset_props.cpp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 /*
2 ******************************************************************************
3 * Copyright (C) 2014, International Business Machines Corporation and
4 * others. All Rights Reserved.
5 ******************************************************************************
6 *
7 * File UNIFIEDCACHE.CPP
8 ******************************************************************************
9 */
10
11 #include "uhash.h"
12 #include "unifiedcache.h"
13 #include "umutex.h"
14 #include "mutex.h"
15 #include "uassert.h"
16 #include "ucln_cmn.h"
17
18 static icu::UnifiedCache *gCache = NULL;
19 static icu::SharedObject *gNoValue = NULL;
20 static UMutex gCacheMutex = U_MUTEX_INITIALIZER;
21 static UConditionVar gInProgressValueAddedCond = U_CONDITION_INITIALIZER;
22 static icu::UInitOnce gCacheInitOnce = U_INITONCE_INITIALIZER;
23
24 U_CDECL_BEGIN
25 static UBool U_CALLCONV unifiedcache_cleanup() {
26 gCacheInitOnce.reset();
27 if (gCache) {
28 delete gCache;
29 gCache = NULL;
30 }
31 if (gNoValue) {
32 delete gNoValue;
33 gNoValue = NULL;
34 }
35 return TRUE;
36 }
37 U_CDECL_END
38
39
40 U_NAMESPACE_BEGIN
41
42 U_CAPI int32_t U_EXPORT2
43 ucache_hashKeys(const UHashTok key) {
44 const CacheKeyBase *ckey = (const CacheKeyBase *) key.pointer;
45 return ckey->hashCode();
46 }
47
48 U_CAPI UBool U_EXPORT2
49 ucache_compareKeys(const UHashTok key1, const UHashTok key2) {
50 const CacheKeyBase *p1 = (const CacheKeyBase *) key1.pointer;
51 const CacheKeyBase *p2 = (const CacheKeyBase *) key2.pointer;
52 return *p1 == *p2;
53 }
54
55 U_CAPI void U_EXPORT2
56 ucache_deleteKey(void *obj) {
57 CacheKeyBase *p = (CacheKeyBase *) obj;
58 delete p;
59 }
60
61 CacheKeyBase::~CacheKeyBase() {
62 }
63
64 static void U_CALLCONV cacheInit(UErrorCode &status) {
65 U_ASSERT(gCache == NULL);
66 ucln_common_registerCleanup(
67 UCLN_COMMON_UNIFIED_CACHE, unifiedcache_cleanup);
68
69 // gNoValue must be created first to avoid assertion error in
70 // cache constructor.
71 gNoValue = new SharedObject();
72 gCache = new UnifiedCache(status);
73 if (gCache == NULL) {
74 status = U_MEMORY_ALLOCATION_ERROR;
75 }
76 if (U_FAILURE(status)) {
77 delete gCache;
78 delete gNoValue;
79 gCache = NULL;
80 gNoValue = NULL;
81 return;
82 }
83 // We add a softref because we want hash elements with gNoValue to be
84 // elligible for purging but we don't ever want gNoValue to be deleted.
85 gNoValue->addSoftRef();
86 }
87
88 const UnifiedCache *UnifiedCache::getInstance(UErrorCode &status) {
89 umtx_initOnce(gCacheInitOnce, &cacheInit, status);
90 if (U_FAILURE(status)) {
91 return NULL;
92 }
93 U_ASSERT(gCache != NULL);
94 return gCache;
95 }
96
97 UnifiedCache::UnifiedCache(UErrorCode &status) {
98 if (U_FAILURE(status)) {
99 return;
100 }
101 U_ASSERT(gNoValue != NULL);
102 fHashtable = uhash_open(
103 &ucache_hashKeys,
104 &ucache_compareKeys,
105 NULL,
106 &status);
107 if (U_FAILURE(status)) {
108 return;
109 }
110 uhash_setKeyDeleter(fHashtable, &ucache_deleteKey);
111 }
112
113 int32_t UnifiedCache::keyCount() const {
114 Mutex lock(&gCacheMutex);
115 return uhash_count(fHashtable);
116 }
117
118 void UnifiedCache::flush() const {
119 Mutex lock(&gCacheMutex);
120
121 // Use a loop in case cache items that are flushed held hard references to
122 // other cache items making those additional cache items eligible for
123 // flushing.
124 while (_flush(FALSE));
125 umtx_condBroadcast(&gInProgressValueAddedCond);
126 }
127
128 #ifdef UNIFIED_CACHE_DEBUG
129 #include <stdio.h>
130
131 void UnifiedCache::dump() {
132 UErrorCode status = U_ZERO_ERROR;
133 const UnifiedCache *cache = getInstance(status);
134 if (U_FAILURE(status)) {
135 fprintf(stderr, "Unified Cache: Error fetching cache.\n");
136 return;
137 }
138 cache->dumpContents();
139 }
140
141 void UnifiedCache::dumpContents() const {
142 Mutex lock(&gCacheMutex);
143 _dumpContents();
144 }
145
146 // Dumps content of cache.
147 // On entry, gCacheMutex must be held.
148 // On exit, cache contents dumped to stderr.
149 void UnifiedCache::_dumpContents() const {
150 int32_t pos = -1;
151 const UHashElement *element = uhash_nextElement(fHashtable, &pos);
152 char buffer[256];
153 int32_t cnt = 0;
154 for (; element != NULL; element = uhash_nextElement(fHashtable, &pos)) {
155 const SharedObject *sharedObject =
156 (const SharedObject *) element->value.pointer;
157 const CacheKeyBase *key =
158 (const CacheKeyBase *) element->key.pointer;
159 if (!sharedObject->allSoftReferences()) {
160 ++cnt;
161 fprintf(
162 stderr,
163 "Unified Cache: Key '%s', error %d, value %p, total refcount %d, soft refcount %d\n",
164 key->writeDescription(buffer, 256),
165 key->creationStatus,
166 sharedObject == gNoValue ? NULL :sharedObject,
167 sharedObject->getRefCount(),
168 sharedObject->getSoftRefCount());
169 }
170 }
171 fprintf(stderr, "Unified Cache: %d out of a total of %d still have hard refe rences\n", cnt, uhash_count(fHashtable));
172 }
173 #endif
174
175 UnifiedCache::~UnifiedCache() {
176 // Try our best to clean up first.
177 flush();
178 {
179 // Now all that should be left in the cache are entries that refer to
180 // each other and entries with hard references from outside the cache.
181 // Nothing we can do about these so proceed to wipe out the cache.
182 Mutex lock(&gCacheMutex);
183 _flush(TRUE);
184 }
185 uhash_close(fHashtable);
186 }
187
188 // Flushes the contents of the cache. If cache values hold references to other
189 // cache values then _flush should be called in a loop until it returns FALSE.
190 // On entry, gCacheMutex must be held.
191 // On exit, those values with only soft references are flushed. If all is true
192 // then every value is flushed even if hard references are held.
193 // Returns TRUE if any value in cache was flushed or FALSE otherwise.
194 UBool UnifiedCache::_flush(UBool all) const {
195 UBool result = FALSE;
196 int32_t pos = -1;
197 const UHashElement *element = uhash_nextElement(fHashtable, &pos);
198 for (; element != NULL; element = uhash_nextElement(fHashtable, &pos)) {
199 const SharedObject *sharedObject =
200 (const SharedObject *) element->value.pointer;
201 if (all || sharedObject->allSoftReferences()) {
202 uhash_removeElement(fHashtable, element);
203 sharedObject->removeSoftRef();
204 result = TRUE;
205 }
206 }
207 return result;
208 }
209
210 // Places a new value and creationStatus in the cache for the given key.
211 // On entry, gCacheMutex must be held. key must not exist in the cache.
212 // On exit, value and creation status placed under key. Soft reference added
213 // to value on successful add. On error sets status.
214 void UnifiedCache::_putNew(
215 const CacheKeyBase &key,
216 const SharedObject *value,
217 const UErrorCode creationStatus,
218 UErrorCode &status) const {
219 if (U_FAILURE(status)) {
220 return;
221 }
222 CacheKeyBase *keyToAdopt = key.clone();
223 if (keyToAdopt == NULL) {
224 status = U_MEMORY_ALLOCATION_ERROR;
225 return;
226 }
227 keyToAdopt->creationStatus = creationStatus;
228 uhash_put(fHashtable, keyToAdopt, (void *) value, &status);
229 if (U_SUCCESS(status)) {
230 value->addSoftRef();
231 }
232 }
233
234 // Places value and status at key if there is no value at key or if cache
235 // entry for key is in progress. Otherwise, it leaves the current value and
236 // status there.
237 // On entry. gCacheMutex must not be held. value must be
238 // included in the reference count of the object to which it points.
239 // On exit, value and status are changed to what was already in the cache if
240 // something was there and not in progress. Otherwise, value and status are left
241 // unchanged in which case they are placed in the cache on a best-effort basis.
242 // Caller must call removeRef() on value.
243 void UnifiedCache::_putIfAbsentAndGet(
244 const CacheKeyBase &key,
245 const SharedObject *&value,
246 UErrorCode &status) const {
247 Mutex lock(&gCacheMutex);
248 const UHashElement *element = uhash_find(fHashtable, &key);
249 if (element != NULL && !_inProgress(element)) {
250 _fetch(element, value, status);
251 return;
252 }
253 if (element == NULL) {
254 UErrorCode putError = U_ZERO_ERROR;
255 // best-effort basis only.
256 _putNew(key, value, status, putError);
257 return;
258 }
259 _put(element, value, status);
260 }
261
262 // Attempts to fetch value and status for key from cache.
263 // On entry, gCacheMutex must not be held value must be NULL and status must
264 // be U_ZERO_ERROR.
265 // On exit, either returns FALSE (In this
266 // case caller should try to create the object) or returns TRUE with value
267 // pointing to the fetched value and status set to fetched status. When
268 // FALSE is returned status may be set to failure if an in progress hash
269 // entry could not be made but value will remain unchanged. When TRUE is
270 // returned, caler must call removeRef() on value.
271 UBool UnifiedCache::_poll(
272 const CacheKeyBase &key,
273 const SharedObject *&value,
274 UErrorCode &status) const {
275 U_ASSERT(value == NULL);
276 U_ASSERT(status == U_ZERO_ERROR);
277 Mutex lock(&gCacheMutex);
278 const UHashElement *element = uhash_find(fHashtable, &key);
279 while (element != NULL && _inProgress(element)) {
280 umtx_condWait(&gInProgressValueAddedCond, &gCacheMutex);
281 element = uhash_find(fHashtable, &key);
282 }
283 if (element != NULL) {
284 _fetch(element, value, status);
285 return TRUE;
286 }
287 _putNew(key, gNoValue, U_ZERO_ERROR, status);
288 return FALSE;
289 }
290
291 // Gets value out of cache.
292 // On entry. gCacheMutex must not be held. value must be NULL. status
293 // must be U_ZERO_ERROR.
294 // On exit. value and status set to what is in cache at key or on cache
295 // miss the key's createObject() is called and value and status are set to
296 // the result of that. In this latter case, best effort is made to add the
297 // value and status to the cache. value will be set to NULL instead of
298 // gNoValue. Caller must call removeRef on value if non NULL.
299 void UnifiedCache::_get(
300 const CacheKeyBase &key,
301 const SharedObject *&value,
302 const void *creationContext,
303 UErrorCode &status) const {
304 U_ASSERT(value == NULL);
305 U_ASSERT(status == U_ZERO_ERROR);
306 if (_poll(key, value, status)) {
307 if (value == gNoValue) {
308 SharedObject::clearPtr(value);
309 }
310 return;
311 }
312 if (U_FAILURE(status)) {
313 return;
314 }
315 value = key.createObject(creationContext, status);
316 U_ASSERT(value == NULL || !value->allSoftReferences());
317 U_ASSERT(value != NULL || status != U_ZERO_ERROR);
318 if (value == NULL) {
319 SharedObject::copyPtr(gNoValue, value);
320 }
321 _putIfAbsentAndGet(key, value, status);
322 if (value == gNoValue) {
323 SharedObject::clearPtr(value);
324 }
325 }
326
327 // Store a value and error in given hash entry.
328 // On entry, gCacheMutex must be held. Hash entry element must be in progress.
329 // value must be non NULL.
330 // On Exit, soft reference added to value. value and status stored in hash
331 // entry. Soft reference removed from previous stored value. Waiting
332 // threads notified.
333 void UnifiedCache::_put(
334 const UHashElement *element,
335 const SharedObject *value,
336 const UErrorCode status) {
337 U_ASSERT(_inProgress(element));
338 const CacheKeyBase *theKey = (const CacheKeyBase *) element->key.pointer;
339 const SharedObject *oldValue = (const SharedObject *) element->value.pointer ;
340 theKey->creationStatus = status;
341 value->addSoftRef();
342 UHashElement *ptr = const_cast<UHashElement *>(element);
343 ptr->value.pointer = (void *) value;
344 oldValue->removeSoftRef();
345
346 // Tell waiting threads that we replace in-progress status with
347 // an error.
348 umtx_condBroadcast(&gInProgressValueAddedCond);
349 }
350
351 // Fetch value and error code from a particular hash entry.
352 // On entry, gCacheMutex must be held. value must be either NULL or must be
353 // included in the ref count of the object to which it points.
354 // On exit, value and status set to what is in the hash entry. Caller must
355 // eventually call removeRef on value.
356 // If hash entry is in progress, value will be set to gNoValue and status will
357 // be set to U_ZERO_ERROR.
358 void UnifiedCache::_fetch(
359 const UHashElement *element,
360 const SharedObject *&value,
361 UErrorCode &status) {
362 const CacheKeyBase *theKey = (const CacheKeyBase *) element->key.pointer;
363 status = theKey->creationStatus;
364 SharedObject::copyPtr(
365 (const SharedObject *) element->value.pointer, value);
366 }
367
368 // Determine if given hash entry is in progress.
369 // On entry, gCacheMutex must be held.
370 UBool UnifiedCache::_inProgress(const UHashElement *element) {
371 const SharedObject *value = NULL;
372 UErrorCode status = U_ZERO_ERROR;
373 _fetch(element, value, status);
374 UBool result = (value == gNoValue && status == U_ZERO_ERROR);
375 SharedObject::clearPtr(value);
376 return result;
377 }
378
379 U_NAMESPACE_END
OLDNEW
« no previous file with comments | « source/common/unifiedcache.h ('k') | source/common/uniset_props.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698