/////////////////////////////////////////////////////////////////////// // File: object_cache.h // Description: A string indexed object cache. // Author: David Eger // Created: Fri Jan 27 12:08:00 PST 2012 // // (C) Copyright 2012, Google Inc. // Licensed 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. // /////////////////////////////////////////////////////////////////////// #ifndef TESSERACT_CCUTIL_OBJECT_CACHE_H_ #define TESSERACT_CCUTIL_OBJECT_CACHE_H_ #include "ccutil.h" #include "errcode.h" #include "genericvector.h" #include "tesscallback.h" namespace tesseract { // A simple object cache which maps a string to an object of type T. // Usually, these are expensive objects that are loaded from disk. // Reference counting is performed, so every Get() needs to be followed later // by a Free(). Actual deletion is accomplished by DeleteUnusedObjects(). template class ObjectCache { public: ObjectCache() = default; ~ObjectCache() { mu_.Lock(); for (int i = 0; i < cache_.size(); i++) { if (cache_[i].count > 0) { tprintf("ObjectCache(%p)::~ObjectCache(): WARNING! LEAK! object %p " "still has count %d (id %s)\n", this, cache_[i].object, cache_[i].count, cache_[i].id.string()); } else { delete cache_[i].object; cache_[i].object = nullptr; } } mu_.Unlock(); } // Return a pointer to the object identified by id. // If we haven't yet loaded the object, use loader to load it. // If loader fails to load it, record a nullptr entry in the cache // and return nullptr -- further attempts to load will fail (even // with a different loader) until DeleteUnusedObjects() is called. // We delete the given loader. T *Get(STRING id, TessResultCallback *loader) { T *retval = nullptr; mu_.Lock(); for (int i = 0; i < cache_.size(); i++) { if (id == cache_[i].id) { retval = cache_[i].object; if (cache_[i].object != nullptr) { cache_[i].count++; } mu_.Unlock(); delete loader; return retval; } } cache_.push_back(ReferenceCount()); ReferenceCount &rc = cache_.back(); rc.id = id; retval = rc.object = loader->Run(); rc.count = (retval != nullptr) ? 1 : 0; mu_.Unlock(); return retval; } // Decrement the count for t. // Return whether we knew about the given pointer. bool Free(T *t) { if (t == nullptr) return false; mu_.Lock(); for (int i = 0; i < cache_.size(); i++) { if (cache_[i].object == t) { --cache_[i].count; mu_.Unlock(); return true; } } mu_.Unlock(); return false; } void DeleteUnusedObjects() { mu_.Lock(); for (int i = cache_.size() - 1; i >= 0; i--) { if (cache_[i].count <= 0) { delete cache_[i].object; cache_.remove(i); } } mu_.Unlock(); } private: struct ReferenceCount { STRING id; // A unique ID to identify the object (think path on disk) T *object; // A copy of the object in memory. Can be delete'd. int count; // A count of the number of active users of this object. }; CCUtilMutex mu_; GenericVector cache_; }; } // namespace tesseract #endif // TESSERACT_CCUTIL_OBJECT_CACHE_H_