/*
 *	PROGRAM:	JRD Access Method
 *	MODULE:		nbak.h
 *	DESCRIPTION:	New backup interface definitions
 *
 *  The contents of this file are subject to the Initial
 *  Developer's Public License Version 1.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.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl.
 *
 *  Software distributed under the License is distributed AS IS,
 *  WITHOUT WARRANTY OF ANY KIND, either express or implied.
 *  See the License for the specific language governing rights
 *  and limitations under the License.
 *
 *  The Original Code was created by Nickolay Samofatov
 *  for the Firebird Open Source RDBMS project.
 *
 *  Copyright (c) 2004 Nickolay Samofatov <nickolay@broadviewsoftware.com>
 *  and all contributors signed below.
 *
 *  All Rights Reserved.
 *  Contributor(s): ______________________________________.
 *
 *
 *
 */
 
#ifndef JRD_NBAK_H
#define JRD_NBAK_H

#include "../common/classes/tree.h"
#include "../common/classes/rwlock.h"
#include "../common/classes/alloc.h"
#include "../common/classes/fb_string.h"

// Uncomment this line if you need to trace backup-related activity
//#define NBAK_DEBUG

#ifdef NBAK_DEBUG
DEFINE_TRACE_ROUTINE(nbak_trace);
#define NBAK_TRACE(args) nbak_trace args
#define NBAK_TRACE_AST(message) gds__trace(message)
#else
#define NBAK_TRACE(args) /* nothing */
#define NBAK_TRACE_AST(message) /* nothing */
#endif

namespace Ods {
	struct pag;
}

namespace Jrd {

class Lock;
class Record;
class thread_db;
class Database;
class jrd_file;
class AstInhibit;

class AllocItem {
public:
	ULONG db_page; // Page number in the main database file
	ULONG diff_page; // Page number in the difference file
    static const ULONG& generate(const void *sender, const AllocItem& item) {
		return item.db_page;
    }
	AllocItem() {
	}
	AllocItem(ULONG db_pageL, ULONG diff_pageL) {
		this->db_page = db_pageL;
		this->diff_page = diff_pageL;
	}
};

typedef Firebird::BePlusTree<AllocItem, ULONG, MemoryPool, AllocItem> AllocItemTree;


// Flags manipulated normally
const UATOM NBAK_state_in_use = 1;
const UATOM NBAK_alloc_in_use = 2;

// Flags manipulated at AST level
const UATOM NBAK_state_blocking		= 1;
const UATOM NBAK_alloc_blocking		= 2;
const UATOM NBAK_database_blocking	= 4;
const UATOM NBAK_alloc_dirty		= 8;

// Note this flags MUST correspond with backup mask in ods.h
const SATOM nbak_state_normal	= 0x0;     // Normal mode. Changes are simply written to main files
const SATOM nbak_state_stalled	= 0x400;   // Main files are locked. Changes are written to diff file
const SATOM nbak_state_merge	= 0x800;   // Merging changes from diff file into main files
const SATOM nbak_state_unknown	= -1;      // State is unknown. Needs to be read from disk


class BackupManager {
public:
	// Subsystem initialization
	BackupManager(thread_db* tdbb, Database* _database, int ini_state);
	// Release locks in response to shutdown AST
	void shutdown_locks(thread_db* tdbb) throw();
	// Set difference file name in header. 
	// State must be locked and equal to nbak_state_normal to call this method
	void set_difference(thread_db* tdbb, const char* filename);
	// Prevent backup state from modification by others
	// You may or may not call unlock_state in case this function fails
	bool lock_state(thread_db* tdbb, bool thread_exit) throw();
	// Remove our interest in consistent backup state
	void unlock_state(thread_db* tdbb) throw();
	// Return whether we block someone
	bool is_blocking() const throw() {
#ifdef SUPERSERVER
		return false;
#else
		return (ast_flags & NBAK_database_blocking);
#endif
	}
	// Return current backup state
	int get_state() const throw() {
		return backup_state;
	}
	// Return current SCN for database
	ULONG get_current_scn() const throw() {
		return current_scn;
	}

	// Return the amount of pages in locked database files
	SLONG get_backup_pages() const throw() {
		return backup_pages;
	}

	// Initialize and open difference file for writing
	void begin_backup(thread_db* tdbb);

	// Merge difference file to main files (if needed) and unlink() difference 
	// file then. If merge is already in progress method silently returns false and 
	// does nothing (so it can be used for recovery on database startup). 
	void end_backup(thread_db* tdbb, bool recover);

	// Prevent allocation table from modification by other threads/processes
	// You may or may not call unlock function in case this functions fail
	bool lock_alloc(thread_db* tdbb, bool thread_exit) throw();
	bool lock_alloc_write(thread_db* tdbb, bool thread_exit) throw();
	// Remove our interest in static allocation table
	void unlock_alloc(thread_db* tdbb) throw();
	void unlock_alloc_write(thread_db* tdbb) throw();
	// Return page index in difference file that can be used in 
	// write_difference call later. 
	ULONG get_page_index(ULONG db_page) const throw();
	// Return next page index in the difference file to be allocated
	ULONG allocate_difference_page(thread_db* tdbb, ULONG db_page) throw();

	// Must have ISC_STATUS because it is called from write_page
	bool write_difference(ISC_STATUS* status, ULONG diff_page, Ods::pag* page) throw();

	bool read_difference(thread_db* tdbb, ULONG diff_page, Ods::pag* page) throw();


	// Routines to declare and release interest in the main database file
	bool get_sw_database_lock(thread_db* tdbb, AstInhibit* enable_signals) throw();
	void release_sw_database_lock(thread_db* tdbb) throw();

	void shutdown(thread_db* tdbb);

	// Subsystem finalization. Called from shutdown()
	~BackupManager();
#ifndef SUPERSERVER
	// Routines to declare and release deferred interest in the difference file
	void increment_diff_use_count() throw();
	void decrement_diff_use_count() throw();
	
	SLONG get_current_generation() const throw() {
		return diff_generation;
	}
#endif
private:
	Database* database;
	jrd_file* diff_file;
	AllocItemTree* alloc_table; // Cached allocation table of pages in difference file
	volatile SATOM backup_state;
	ULONG last_allocated_page; // Last physical page allocated in the difference file
	BYTE *temp_buffers_space;
	ULONG *alloc_buffer, *empty_buffer, *spare_buffer;
	ULONG current_scn;
	SLONG backup_pages; // Number of allocated pages as it was at point of backup lock
	Firebird::PathName diff_name;
	// Set if we need to close difference file in the next status update cycle
	// Used in CS builds to prevent closing difference file too early while it still
	// may be used inside the signal handlers
	bool diff_pending_close; 
#ifdef SUPERSERVER
	Firebird::RWLock* alloc_lock; // Lock to protect allocation table
	Firebird::RWLock* state_lock; // Lock to protect backup lock	
	Firebird::RWLock* database_lock; // Lock to protect write to database
	// Lock to protect initialization of alloc_table and diff_file 
	// outside of exclusive status lock to prevent possible race condition
	// in case of errors.
	Firebird::Mutex* adjust_state_lock; 
#else
	Lock* alloc_lock; // Lock to protect allocation table
	Lock* state_lock; // Lock to protect backup state
	Lock* database_lock; // Lock to protect writing to database

	// Absense of 'volatile' REALLY causes database corruption
	// with optimized build on Linux CS. Do not remove it !
	volatile UATOM flags;
	// Variables used at AST level
	volatile UATOM ast_flags;
	volatile SATOM database_use_count;
	volatile SATOM diff_use_count;
	volatile SATOM diff_generation;

	static int backup_state_ast(void *ast_object) throw();
	static int alloc_table_ast(void *ast_object) throw();
	static int backup_database_ast(void *ast_object) throw();
#endif
	bool try_lock_state_write(thread_db* tdbb);
	void lock_state_write(thread_db* tdbb, bool thread_exit);
	void unlock_state_write(thread_db* tdbb) throw();
	void generate_filename() throw();
	// Make appropriate information up-to-date
	bool actualize_state(thread_db* tdbb) throw();
	bool actualize_alloc(thread_db* tdbb) throw();
};

} //namespace Jrd

#endif /* JRD_NBAK_PROTO_H */
