// Copyright 2008 Google Inc. All Rights Reserved.
// 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.
// Interface for a thread-safe container of disk blocks
#ifndef STRESSAPPTEST_DISK_BLOCKS_H_
#define STRESSAPPTEST_DISK_BLOCKS_H_
#include <sys/types.h>
#include <pthread.h>
#include <time.h>
#include <sys/time.h>
#include <errno.h>
#include <map>
#include <vector>
#include <string>
#include "sattypes.h"
class Pattern;
// Data about a block written to disk so that it can be verified later.
// Thread-unsafe, must be used with locks on non-const methods,
// except for initialized accessor/mutator, which are thread-safe
// (and in fact, is the only method supposed to be accessed from
// someone which is not the thread-safe DiskBlockTable).
class BlockData {
public:
BlockData();
~BlockData();
// These are reference counters used to control how many
// threads currently have a copy of this particular block.
void IncreaseReferenceCounter() { references_++; }
void DecreaseReferenceCounter() { references_--; }
int GetReferenceCounter() const { return references_; }
// Controls whether the block was written on disk or not.
// Once written, you cannot "un-written" then without destroying
// this object.
void set_initialized();
bool initialized() const;
// Accessor methods for some data related to blocks.
void set_address(uint64 address) { address_ = address; }
uint64 address() const { return address_; }
void set_size(uint64 size) { size_ = size; }
uint64 size() const { return size_; }
void set_pattern(Pattern *p) { pattern_ = p; }
Pattern *pattern() { return pattern_; }
private:
uint64 address_; // Address of first sector in block
uint64 size_; // Size of block
int references_; // Reference counter
bool initialized_; // Flag indicating the block was written on disk
Pattern *pattern_;
mutable pthread_mutex_t data_mutex_;
DISALLOW_COPY_AND_ASSIGN(BlockData);
};
// A thread-safe table used to store block data and control access
// to these blocks, letting several threads read and write blocks on
// disk.
class DiskBlockTable {
public:
DiskBlockTable();
virtual ~DiskBlockTable();
// Returns number of elements stored on table.
uint64 Size();
// Sets all initial parameters. Assumes all existent data is
// invalid and, therefore, must be removed.
void SetParameters(int sector_size, int write_block_size,
int64 device_sectors,
int64 segment_size,
const string& device_name);
// During the regular execution, there will be 2 types of threads:
// - Write thread: gets a large number of blocks using GetUnusedBlock,
// writes them on disk (if on destructive mode),
// reads block content ONCE from disk and them removes
// the block from queue with RemoveBlock. After a removal a
// block is not available for read threads, but it is
// only removed from memory if there is no reference for
// this block. Note that a write thread also counts as
// a reference.
// - Read threads: get one block at a time (if available) with
// GetRandomBlock, reads its content from disk,
// checking whether it is correct or not, and releases
// (Using ReleaseBlock) the block to be erased by the
// write threads. Since several read threads are allowed
// to read the same block, a reference counter is used to
// control when the block can be REALLY erased from
// memory, and all memory management is made by a
// DiskBlockTable instance.
// Returns a new block in a unused address. Does not
// grant ownership of the pointer to the caller
// (use RemoveBlock to delete the block from memory instead).
BlockData *GetUnusedBlock(int64 segment);
// Removes block from structure (called by write threads). Returns
// 1 if successful, 0 otherwise.
int RemoveBlock(BlockData *block);
// Gets a random block from the list. Only returns if an element
// is available (a write thread has got this block, written it on disk,
// and set this block as initialized). Does not grant ownership of the
// pointer to the caller (use RemoveBlock to delete the block from
// memory instead).
BlockData *GetRandomBlock();
// Releases block to be erased (called by random threads). Returns
// 1 if successful, 0 otherwise.
int ReleaseBlock(BlockData *block);
protected:
struct StorageData {
BlockData *block;
int pos;
};
typedef map<int64, StorageData*> AddrToBlockMap;
typedef vector<int64> PosToAddrVector;
// Inserts block in structure, used in tests and by other methods.
void InsertOnStructure(BlockData *block);
// Generates a random 64-bit integer.
// Virtual method so it can be overridden by the tests.
virtual int64 Random64();
// Accessor methods for testing.
const PosToAddrVector& pos_to_addr() const { return pos_to_addr_; }
const AddrToBlockMap& addr_to_block() const { return addr_to_block_; }
int sector_size() const { return sector_size_; }
int write_block_size() const { return write_block_size_; }
const string& device_name() const { return device_name_; }
int64 device_sectors() const { return device_sectors_; }
int64 segment_size() const { return segment_size_; }
private:
// Number of retries to allocate sectors.
static const int kBlockRetry = 100;
// Actual tables.
PosToAddrVector pos_to_addr_;
AddrToBlockMap addr_to_block_;
// Configuration parameters for block selection
int sector_size_; // Sector size, in bytes
int write_block_size_; // Block size, in bytes
string device_name_; // Device name
int64 device_sectors_; // Number of sectors in device
int64 segment_size_; // Segment size in bytes
uint64 size_; // Number of elements on table
pthread_mutex_t data_mutex_;
pthread_cond_t data_condition_;
pthread_mutex_t parameter_mutex_;
DISALLOW_COPY_AND_ASSIGN(DiskBlockTable);
};
#endif // STRESSAPPTEST_BLOCKS_H_