#include <map>
#include <vector>
#include <string>
-// This file must work with autoconf on its public version,
-// so these includes are correct.
-#include "pattern.h"
+
+#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();
- void SetParameters(int64 address, int64 size);
- void IncreaseReferenceCounter();
- void DecreaseReferenceCounter();
- int GetReferenceCounter();
- void SetBlockAsInitialized();
- bool BlockIsInitialized();
- int64 GetAddress();
- int64 GetSize();
- void SetPattern(Pattern *p);
- Pattern *GetPattern();
- protected:
- int64 addr_; // address of first sector in block
- int64 size_; // size of block
- int references_; // reference counter
- bool initialized_; // flag indicating the block was written on disk
+
+ // 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_;
- pthread_mutex_t data_mutex_;
+ mutable pthread_mutex_t data_mutex_;
DISALLOW_COPY_AND_ASSIGN(BlockData);
};
-// Disk Block table - store data from blocks to be write / read by
-// a DiskThread
+// 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();
- // Get Number of elements stored on table
- int64 NumElems();
- // Clean all table data
- void CleanTable();
- // Get a random block from the list. Only returns if a element
- // is available (consider that other thread must have added them.
- BlockData *GetRandomBlock();
- // Set all initial parameters. Assumes all existent data is
+ // 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,
- string device_name);
- // Return a new block in a unused address.
+ 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);
- // Remove block from structure (called by write threads)
+
+ // Removes block from structure (called by write threads). Returns
+ // 1 if successful, 0 otherwise.
int RemoveBlock(BlockData *block);
- // Release block to be erased (called by random threads)
- int ReleaseBlock(BlockData *block);
- protected:
+ // 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();
- void InsertOnStructure(BlockData *block);
- // Generate a random 64-bit integer (virtual so it could be
- // override by the tests)
- virtual int64 Random64();
+ // 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;
};
-
- static const int kBlockRetry = 100; // Number of retries to allocate
- // sectors.
-
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_;
- int64 nelems_;
- 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
+
+ // 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_;