blob: 6f548552a71fdbdabc0ae3cfd2da354ae7312172 [file] [log] [blame]
/*
* Copyright 2010 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: abliss@google.com (Adam Bliss)
#ifndef PAGESPEED_KERNEL_BASE_MEM_FILE_SYSTEM_H_
#define PAGESPEED_KERNEL_BASE_MEM_FILE_SYSTEM_H_
#include <map>
#include "pagespeed/kernel/base/abstract_mutex.h"
#include "pagespeed/kernel/base/basictypes.h"
#include "pagespeed/kernel/base/callback.h"
#include "pagespeed/kernel/base/file_system.h"
#include "pagespeed/kernel/base/scoped_ptr.h"
#include "pagespeed/kernel/base/string.h"
#include "pagespeed/kernel/base/string_util.h"
#include "pagespeed/kernel/base/thread_annotations.h"
namespace net_instaweb {
class AbstractMutex;
class MessageHandler;
class MockTimer;
class ThreadSystem;
class Timer;
// An in-memory implementation of the FileSystem interface. This was originally
// for use in unit tests; but can also host the lock manager if needed.
// Does not fully support directories. Not particularly efficient.
// Not threadsafe except for lock methods.
// TODO(abliss): add an ability to block writes for arbitrarily long, to
// enable testing resilience to concurrency problems with real filesystems.
//
// TODO(jmarantz): make threadsafe.
class MemFileSystem : public FileSystem {
public:
typedef Callback1<const GoogleString&> FileCallback;
explicit MemFileSystem(ThreadSystem* threads, Timer* timer);
virtual ~MemFileSystem();
virtual InputFile* OpenInputFile(const char* filename,
MessageHandler* message_handler);
virtual OutputFile* OpenOutputFileHelper(const char* filename,
bool append,
MessageHandler* message_handler);
virtual OutputFile* OpenTempFileHelper(const StringPiece& prefix_name,
MessageHandler* message_handle);
virtual bool ListContents(const StringPiece& dir, StringVector* files,
MessageHandler* handler);
virtual bool MakeDir(const char* directory_path, MessageHandler* handler);
virtual bool RecursivelyMakeDir(const StringPiece& directory_path,
MessageHandler* handler);
virtual bool RemoveDir(const char* path, MessageHandler* handler);
virtual bool RemoveFile(const char* filename, MessageHandler* handler);
virtual bool RenameFileHelper(const char* old_file, const char* new_file,
MessageHandler* handler);
// We offer a "simulated atime" in which the clock ticks forward one
// second every time you read or write a file.
virtual bool Atime(const StringPiece& path, int64* timestamp_sec,
MessageHandler* handler);
virtual bool Mtime(const StringPiece& path, int64* timestamp_sec,
MessageHandler* handler);
virtual bool Size(const StringPiece& path, int64* size,
MessageHandler* handler);
virtual BoolOrError Exists(const char* path, MessageHandler* handler);
virtual BoolOrError IsDir(const char* path, MessageHandler* handler);
virtual BoolOrError TryLock(const StringPiece& lock_name,
MessageHandler* handler);
virtual BoolOrError TryLockWithTimeout(const StringPiece& lock_name,
int64 timeout_ms,
const Timer* timer,
MessageHandler* handler);
virtual bool Unlock(const StringPiece& lock_name, MessageHandler* handler);
// When atime is disabled, reading a file will not update its atime.
void set_atime_enabled(bool enabled) {
ScopedMutex lock(all_else_mutex_.get());
atime_enabled_ = enabled;
}
// In order to test file-system 'atime' code, we need to move mock
// time forward during tests by an entire second (aka 1000 ms).
// However, that's disruptive to other tests that try to use
// mock-time to examine millisecond-level timing, so we leave this
// behavior off by default.
bool advance_time_on_update() { return advance_time_on_update_; }
void set_advance_time_on_update(bool x, MockTimer* mock_timer) {
advance_time_on_update_ = x;
mock_timer_ = mock_timer;
}
// Empties out the entire filesystem. Should not be called while files
// are open.
void Clear();
// Test-specific functionality to disable and re-enable the filesystem.
void Disable() { enabled_ = false; }
void Enable() { enabled_ = true; }
// Access statistics.
void ClearStats() {
num_input_file_opens_ = 0;
num_input_file_stats_ = 0;
num_output_file_opens_ = 0;
num_temp_file_opens_ = 0;
}
int num_input_file_opens() const { return num_input_file_opens_; }
// returns number of times MTime was called.
int num_input_file_stats() const { return num_input_file_stats_; }
int num_output_file_opens() const { return num_output_file_opens_; }
int num_temp_file_opens() const { return num_temp_file_opens_; }
// Adds a callback to be called once after a file-write and then
// deleted.
//
// This is intended primarily for testing, and thus is not on the base
// class.
void set_write_callback(FileCallback* x) { write_callback_.reset(x); }
virtual bool WriteFile(const char* filename,
const StringPiece& buffer,
MessageHandler* handler);
virtual bool WriteTempFile(const StringPiece& prefix_name,
const StringPiece& buffer,
GoogleString* filename,
MessageHandler* handler);
private:
inline void UpdateAtime(const StringPiece& path)
SHARED_LOCKS_REQUIRED(all_else_mutex_);
inline void UpdateMtime(const StringPiece& path)
SHARED_LOCKS_REQUIRED(all_else_mutex_);
scoped_ptr<AbstractMutex> lock_map_mutex_; // controls access to lock_map_
scoped_ptr<AbstractMutex> all_else_mutex_; // controls access to all else.
// When disabled, OpenInputFile returns NULL.
bool enabled_ GUARDED_BY(all_else_mutex_);
// MemFileSystem::RemoveDir depends on string_map_ being sorted by key. If an
// unsorted data structure is used (say a hash_map) this implementation will
// need to be modified.
StringStringMap string_map_ GUARDED_BY(all_else_mutex_);
Timer* timer_;
// Used only for auto-advance functionality.
MockTimer* mock_timer_ GUARDED_BY(all_else_mutex_);
// atime_map_ holds times (in s) that files were last opened/modified. Each
// time we do such an operation, timer() advances by 1s (so all ATimes are
// distinct).
// ctime and mtime are updated only for moves and modifications.
std::map<GoogleString, int64> atime_map_ GUARDED_BY(all_else_mutex_);
std::map<GoogleString, int64> mtime_map_ GUARDED_BY(all_else_mutex_);
int temp_file_index_ GUARDED_BY(all_else_mutex_);
// lock_map_ holds times that locks were established (in ms).
// locking and unlocking don't advance time.
std::map<GoogleString, int64> lock_map_ GUARDED_BY(lock_map_mutex_);
bool atime_enabled_ GUARDED_BY(all_else_mutex_);
// Indicates whether MemFileSystem will advance mock time whenever
// a file is written.
bool advance_time_on_update_ GUARDED_BY(all_else_mutex_);
// Access statistics.
int num_input_file_opens_ GUARDED_BY(all_else_mutex_);
int num_input_file_stats_ GUARDED_BY(all_else_mutex_);
int num_output_file_opens_ GUARDED_BY(all_else_mutex_);
int num_temp_file_opens_ GUARDED_BY(all_else_mutex_);
// Hook to run after a file-write.
scoped_ptr<FileCallback> write_callback_;
DISALLOW_COPY_AND_ASSIGN(MemFileSystem);
};
} // namespace net_instaweb
#endif // PAGESPEED_KERNEL_BASE_MEM_FILE_SYSTEM_H_