blob: 046dab48bc3dfca23212c273e6e530195bb85a3e [file] [log] [blame]
/*
* Copyright 2011 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.
*/
// Author: jhoch@google.com (Jason Hoch)
#ifndef PAGESPEED_KERNEL_SHAREDMEM_SHARED_DYNAMIC_STRING_MAP_H_
#define PAGESPEED_KERNEL_SHAREDMEM_SHARED_DYNAMIC_STRING_MAP_H_
#include <cstddef>
#include "pagespeed/kernel/base/abstract_mutex.h"
#include "pagespeed/kernel/base/abstract_shared_mem.h"
#include "pagespeed/kernel/base/basictypes.h"
#include "pagespeed/kernel/base/scoped_ptr.h"
#include "pagespeed/kernel/base/string.h"
#include "pagespeed/kernel/base/string_util.h"
namespace net_instaweb {
class MessageHandler;
class Writer;
struct Entry {
int value;
size_t string_offset;
};
// A shared memory string to int dictionary/map, no deletion.
// Currently the map is designed to fill with number_of_strings strings of
// average length average_string_length. Once the map is full it ignores
// attempts to add additional information.
// TODO(jhoch): make map dynamically sized
class SharedDynamicStringMap {
public:
// Number of strings will be rounded up to a power of 2.
// Average string length should include terminating null character.
// Map will be able to hold exactly number_of_strings * average_string_length
// chars worth of string data.
SharedDynamicStringMap(size_t number_of_strings,
size_t average_string_length,
AbstractSharedMem* shm_runtime,
const GoogleString& filename_prefix,
const GoogleString& filename_suffix);
// Initialize the shared memory segment. This method should complete before
// any other methods are executed.
// parent = true means invoked in root process, initialize the shared memory
// = false means invoked in child process -- attach to existing segment
bool InitSegment(bool parent, MessageHandler* message_handler);
// Increments value corresponding to given string by 1.
// Adds the string to the map with initial value 1 if the string is not
// present.
// Returns the new value corresponding to the element.
// If the map is full it does nothing and returns 0.
int IncrementElement(const StringPiece& string);
// Retrieve the value corresponding to the string (returns 0 if the string is
// not in the map)
int LookupElement(const StringPiece& string) const;
// Dumps table's strings into StringSet
void GetKeys(StringSet* strings);
// Retrieve the number of strings inserted into the table.
int GetNumberInserted() const;
// Destroy shared memory segment and other relevant clean-up
void GlobalCleanup(MessageHandler* message_handler);
// Iterates through the string data that is present at the time of calling
// and dumps out each string with its associated value. The value produced
// for a given string is going to be the value present whenever that string
// is dumped.
void Dump(Writer* writer, MessageHandler* message_handler);
private:
void ClearSegment(MessageHandler* message_handler);
// ***If lock = true, locks the mutex associated with the returned entry***
// (to be unlocked by caller)
// Finds the index of the entry with the given string, or the first empty
// entry encountered, which can either be used for insertion purposes or
// to know that the string is not present, since the table does not support
// deletion.
// -1 is returned if the entire table is traversed without finding the
// string or an empty spot (this should not happen if the table never exceeds
// 50% capacity).
// entry_pointer is an output parameter.
// Note that lock must always be set to true for any writing operation, and
// setting write to false for a read operation can result in a false read,
// albeit in very rare circumstances. The circumstance is that an entry "AB"
// is being added to the table, where A and B are arbitrary strings, and a
// read of "A" is occurring, and the read of "A" catches the write of "AB"
// mid-write at exactly the moment where it can only see "A."
// The fact that looking up without locking is possible relies on the
// assumption that entries are not deleted and that null characters fill up
// the char space upon initialization.
// If 100% accurate lookup is needed then a new LookupElement method could
// be added that calls FindEntry(lock = true).
int FindEntry(const StringPiece& string,
bool lock,
Entry** entry_pointer) const;
Entry* GetEntry(size_t n) const;
Entry* GetFirstEntry() const;
// Gets the mutex for the nth table entry.
AbstractMutex* GetMutex(size_t n) const;
// Inserts the given string into the table (if there is room), by adding it
// to char storage and setting the entry_pointer's char offset and value (the
// former to string_offset and the latter to 1), and returns the resulting
// value of the entry (1 if it was successfully inserted, 0 otherwise)
// The entry should be locked when this method is called.
int InsertString(const StringPiece& string, Entry* entry_pointer);
char* GetStringAtOffset(size_t offset) const;
// Math utility function, returns the smallest power of two greater than or
// equal to the input.
static size_t NextPowerOfTwo(size_t n);
// |
// Structure content | Offset
// |
// ___________________________ | mutex_offset_ = 0
// | Mutex0 | | - memory location = segment_->Base()
// | | |
// | Mutex1 | | mutex_offset_ + mutex_size
// | | |
// | Mutex2 | | mutex_offset_ + mutex_size_ * 2
// | . | |
// | . | | - each mutex has size mutex_size
// | . | |
// | | |
// | MutexN | | mutex_offset_ + mutex_size_ * N
// | . | |
// | . | | - there are table_size_ + 1 mutexes
// | . | | (last mutex is for string_offset
// | . | | and number_inserted)
// | . | |
// |___________________________| | strings_offset_ = mutex_offset_ +
// | String0======= | | kTableSize * mutex_size_
// | | | = String offset0
// | | |
// | String1======= | | String offset1
// | _____| |
// | | |
// | String2= | | String offset2
// | |________________ |
// | | |
// | String3================== | | etc.
// | _________| |
// | | |
// | String4======== | | - strings are variable length, null
// | |_ | terminated
// | | |
// | String5========== | | - total allocated space is
// | _| | number_of_strings times average
// | | | string length
// | String6======== | |
// | |_ | - there are as many strings as have been
// | . | | added
// | . | |
// | . | | - location at which to add next string is
// |___________________| | stored at string_offset_offset_
// | | | (see below)
// | | |
// | String offset | | string_offset_offset_ = strings_offset_ +
// |_________________| | number_of_strings_ *
// | | | average_string_length_
// | Number | |
// | Inse- | | number_inserted_offset_ =
// | rted | | string_offset_offset_ + kOffsetSize
// | | |
// |________|________________ | table_offset_ = number_inserted_offset_ +
// | Value0 | String offset0 | | kIntSize
// | (int) | (size_t) | |
// | | | |
// | Value1 | String offset1 | | table_offset_ + kEntrySize
// | | | |
// | Value2 | String offset2 | | table_offset_ + kEntrySize* 2
// | . | . | |
// | . | . | | - each value and string offset makes an
// | . | . | | Entry struct
// | | | |
// | ValueN | String offsetN | | table_offset_ + kEntrySize * N
// | . | . | |
// | . | . | | - there are table_size entries
// | . | . | |
// |________|________________| |
size_t number_of_strings_;
size_t average_string_length_;
// Sizes of various portions of the memory
size_t mutex_size_;
size_t table_size_;
// Offset from segment_->Base() at which various portions of the structure
// begin (see above depiction).
// mutex_offset_ is the beginning of the (table_size_ + 1) mutexes
// strings_offset_ is the beginning of the strings
// string_offset_offset_ is where the offset of the next string to be
// inserted is located
// number_inserted_offset_ is where the number of inserted strings is
// located
// table_offset_ is the beginning of the table_size_ entries
size_t mutex_offset_;
size_t strings_offset_;
size_t string_offset_offset_;
size_t number_inserted_offset_;
size_t table_offset_;
// Total size of shared memory segment
size_t total_size_;
// The mutex for inserting strings, i.e. the one shared by the
// string_offset_ and number_inserted_ values.
scoped_ptr<AbstractMutex> insert_string_mutex_;
const GoogleString segment_name_;
AbstractSharedMem* shm_runtime_;
scoped_ptr<AbstractSharedMemSegment> segment_;
DISALLOW_COPY_AND_ASSIGN(SharedDynamicStringMap);
};
} // namespace net_instaweb
#endif // PAGESPEED_KERNEL_SHAREDMEM_SHARED_DYNAMIC_STRING_MAP_H_