/************************************************************************
The test module for the file system and buffer manager

(c) 1995 Innobase Oy

Created 11/16/1995 Heikki Tuuri
*************************************************************************/

#include "string.h"

#include "os0thread.h"
#include "os0file.h"
#include "ut0ut.h"
#include "ut0byte.h"
#include "sync0sync.h"
#include "mem0mem.h"
#include "fil0fil.h"
#include "..\buf0buf.h"
#include "..\buf0buf.h1"
#include "..\buf0buf.h2"
#include "..\buf0flu.h"
#include "..\buf0lru.h"
#include "mtr0buf.h"
#include "mtr0log.h"
#include "fsp0fsp.h"
#include "log0log.h"

os_file_t	files[1000];

mutex_t		ios_mutex;
ulint		ios;
ulint		n[5];


/************************************************************************
Io-handler thread function. */

ulint
handler_thread(
/*===========*/
	void*	arg)
{
	ulint	segment;
	void*	mess;
	ulint	i;
	bool	ret;
	
	segment = *((ulint*)arg);

	printf("Thread %lu starts\n", segment);

	for (i = 0;; i++) {
		ret = fil_aio_wait(segment, &mess);
		ut_a(ret);

		buf_page_io_complete((buf_block_t*)mess);
		
		mutex_enter(&ios_mutex);
		ios++;
		mutex_exit(&ios_mutex);
		
	}

	return(0);
}

/************************************************************************
Creates the test database files. */

void 
create_db(void)
/*===========*/
{
	ulint			i;
	buf_block_t*		block;
	byte*			frame;
	ulint			j;
	ulint			tm, oldtm;
	mtr_t			mtr; 
	
	oldtm = ut_clock();

	for (i = 0; i < 1; i++) {
		for (j = 0; j < 4096; j++) {
			mtr_start(&mtr);
			if (j == 0) {
				fsp_header_init(i, 4096, &mtr);

				block = mtr_page_get(i, j, NULL, &mtr);
			} else { 
				block = mtr_page_create(i, j, &mtr);
			}

			frame = buf_block_get_frame(block);

			mtr_page_x_lock(block, &mtr);

			mlog_write_ulint(frame + FIL_PAGE_PREV,
					j - 1, MLOG_4BYTES, &mtr);
			
			mlog_write_ulint(frame + FIL_PAGE_NEXT,
					j + 1, MLOG_4BYTES, &mtr);
			
			mlog_write_ulint(frame + FIL_PAGE_OFFSET,
					j, MLOG_4BYTES, &mtr);
			
			mtr_commit(&mtr);
		}
	}

	tm = ut_clock();
	printf("Wall clock time for test %lu milliseconds\n", tm - oldtm);

	/* Flush the pool of dirty pages by reading low-offset pages */
	for (i = 0; i < 1000; i++) {

		mtr_start(&mtr);
		block = mtr_page_get(0, i, NULL, &mtr);

		frame = buf_block_get_frame(block);

		mtr_page_s_lock(block, &mtr);

		ut_a(mtr_read_ulint(frame + FIL_PAGE_OFFSET, MLOG_4BYTES,
				   &mtr) == i);
			
		mtr_commit(&mtr);
	}

	os_thread_sleep(1000000);

	ut_a(buf_all_freed());
}
	
/*************************************************************************
Creates the files for the file system test and inserts them to
the file system. */

void
create_files(void)
/*==============*/
{
	bool		ret;
	ulint		i, k;
	char		name[20];
	os_thread_t	thr[5];
	os_thread_id_t	id[5];

	strcpy(name, "j:\\tsfile1");

	for (k = 0; k < 1; k++) {
	for (i = 0; i < 4; i++) {

		name[9] = (char)((ulint)'0' + i);
	
		files[i] = os_file_create(name, OS_FILE_CREATE,
					OS_FILE_TABLESPACE, &ret);

		if (ret == FALSE) {
			ut_a(os_file_get_last_error() ==
						OS_FILE_ALREADY_EXISTS);
	
			files[i] = os_file_create(
				name, OS_FILE_OPEN,
						OS_FILE_TABLESPACE, &ret);

			ut_a(ret);
		}

		ret = os_file_set_size(files[i], 4096 * 8192, 0);
		ut_a(ret);		
		
		ret = os_file_close(files[i]);
		ut_a(ret);

		if (i == 0) {
			fil_space_create("noname", k, OS_FILE_TABLESPACE);
		}

		ut_a(fil_validate());

		fil_node_create(name, 4096, k);
	}
	}

	ios = 0;

	mutex_create(&ios_mutex);
	
	for (i = 0; i < 5; i++) {
		n[i] = i;

		thr[i] = os_thread_create(handler_thread, n + i, id + i);
	}
}

/************************************************************************
Reads the test database files. */

void 
test1(void)
/*=======*/
{
	ulint			i, j, k;
	buf_block_t*		block;
	byte*			frame;
	ulint			tm, oldtm;

	buf_flush_batch(BUF_FLUSH_LIST, 1000);

	os_thread_sleep(1000000);

	buf_all_freed();
	
	oldtm = ut_clock();
	
	for (k = 0; k < 1; k++) {
	for (i = 0; i < 1; i++) {
		for (j = 0; j < 409; j++) {
			block = buf_page_get(i, j, NULL);

			frame = buf_block_get_frame(block);

			buf_page_s_lock(block);

			ut_a(*((ulint*)(frame + 16)) == j);
			
			buf_page_s_unlock(block);

			buf_page_release(block);
		}
	}
	}

	tm = ut_clock();
	printf("Wall clock time for test %lu milliseconds\n", tm - oldtm);

}

/************************************************************************
Reads the test database files. */

void 
test2(void)
/*=======*/
{
	ulint			i, j, k, rnd;
	buf_block_t*		block;
	byte*			frame;
	ulint			tm, oldtm;

	oldtm = ut_clock();

	rnd = 123;

	for (k = 0; k < 100; k++) {
		rnd += 23651;
		rnd = rnd % 4096;
	
		i = rnd / 4096; 
		j = rnd % 2048;
		
		block = buf_page_get(i, j, NULL);

		frame = buf_block_get_frame(block);

		buf_page_s_lock(block);

		ut_a(*((ulint*)(frame + 16)) == j);
			
		buf_page_s_unlock(block);

		buf_page_release(block);
	}

	tm = ut_clock();
	printf("Wall clock time for random read %lu milliseconds\n",
		tm - oldtm);
}

/************************************************************************
Reads the test database files. */

void 
test4(void)
/*=======*/
{
	ulint			i, j, k, rnd;
	buf_block_t*		block;
	byte*			frame;
	ulint			tm, oldtm;

	/* Flush the pool of high-offset pages */
	for (i = 0; i < 1000; i++) {
		
		block = buf_page_get(0, i, NULL);

		frame = buf_block_get_frame(block);

		buf_page_s_lock(block);

		ut_a(*((ulint*)(frame + 16)) == i);
			
		buf_page_s_unlock(block);

		buf_page_release(block);
	}

	printf("Test starts\n");
	
	oldtm = ut_clock();

	rnd = 123;

	for (k = 0; k < 400; k++) {

		rnd += 4357;
	
		i = 0;
		j = 1001 + rnd % 3000;
		
		block = buf_page_get(i, j, NULL);

		frame = buf_block_get_frame(block);

		buf_page_s_lock(block);

		ut_a(*((ulint*)(frame + 16)) == j);
			
		buf_page_s_unlock(block);

		buf_page_release(block);
	}

	tm = ut_clock();
	printf(
	   "Wall clock time for %lu random no read-ahead %lu milliseconds\n",
		k, tm - oldtm);

	/* Flush the pool of high-offset pages */
	for (i = 0; i < 1000; i++) {
		
		block = buf_page_get(0, i, NULL);

		frame = buf_block_get_frame(block);

		buf_page_s_lock(block);

		ut_a(*((ulint*)(frame + 16)) == i);
			
		buf_page_s_unlock(block);

		buf_page_release(block);
	}

	printf("Test starts\n");

	oldtm = ut_clock();

	rnd = 123;

	for (k = 0; k < 400; k++) {

		rnd += 4357;
	
		i = 0;
		j = 1001 + rnd % 400;
		
		block = buf_page_get(i, j, NULL);

		frame = buf_block_get_frame(block);

		buf_page_s_lock(block);

		ut_a(*((ulint*)(frame + 16)) == j);
			
		buf_page_s_unlock(block);

		buf_page_release(block);
	}

	tm = ut_clock();
	printf(
	   "Wall clock time for %lu random read-ahead %lu milliseconds\n",
		k, tm - oldtm);

}

/************************************************************************
Tests speed of CPU algorithms. */

void 
test3(void)
/*=======*/
{
	ulint			i, j;
	buf_block_t*		block;
	ulint			tm, oldtm;

	for (i = 0; i < 400; i++) {
		
		block = buf_page_get(0, i, NULL);

		buf_page_release(block);
	}

	os_thread_sleep(2000000);

	oldtm = ut_clock();

	for (j = 0; j < 500; j++) {
	for (i = 0; i < 200; i++) {
		
		block = buf_page_get(0, i, NULL);

/*
		buf_page_s_lock(block);

		buf_page_s_unlock(block);
*/

		buf_page_release(block);

	}
	}

	tm = ut_clock();
	printf("Wall clock time for %lu page get-release %lu milliseconds\n",
			i * j, tm - oldtm);

	oldtm = ut_clock();

	for (j = 0; j < 500; j++) {
	for (i = 0; i < 200; i++) {
		
		buf_page_get(0, i, NULL);
/*
		buf_page_s_lock(block);

		buf_page_s_unlock(block);
*/
		buf_page_release(block);
	}
	}

	tm = ut_clock();
	printf("Wall clock time for %lu block get-release %lu milliseconds\n",
			i * j, tm - oldtm);


	oldtm = ut_clock();

	for (i = 0; i < 100000; i++) {
		block = buf_block_alloc();
		buf_block_free(block);
	}

	tm = ut_clock();
	printf("Wall clock time for %lu block alloc-free %lu milliseconds\n",
			i, tm - oldtm);

	ha_print_info(buf_pool->page_hash);
}

/************************************************************************
Frees the spaces in the file system. */

void
free_system(void)
/*=============*/
{
	ulint	i;

	for (i = 0; i < 1; i++) {
		fil_space_free(i);
	}
}

/************************************************************************
Test for file space management. */

void
test5(void)
/*=======*/
{
	mtr_t		mtr;
	ulint		seg_page;
	ulint		new_page;
	ulint		seg_page2;
	ulint		new_page2;
	buf_block_t*	block;
	bool		finished;
	ulint		i;
	ulint		reserved;
	ulint		used;
	ulint		tm, oldtm;

	os_thread_sleep(1000000);

	buf_validate();

	buf_print();
	
	mtr_start(&mtr);

	seg_page = fseg_create(0, 0, 1000, 555, &mtr);

	mtr_commit(&mtr);

	os_thread_sleep(1000000);
	buf_validate();
	printf("Segment created: header page %lu\n", seg_page);

	mtr_start(&mtr);

	block = mtr_page_get(0, seg_page, NULL, &mtr);
	
	new_page = fseg_alloc_free_page(buf_block_get_frame(block) + 1000,
					2, FSP_UP, &mtr);	
	
	mtr_commit(&mtr);

	buf_validate();
	buf_print();
	printf("Segment page allocated %lu\n", new_page);

	finished = FALSE;

	while (!finished) {
	
		mtr_start(&mtr);

		block = mtr_page_get(0, seg_page, NULL, &mtr);
	
		finished = fseg_free_step(
			buf_block_get_frame(block) + 1000, &mtr);	
	
		mtr_commit(&mtr);
	}

	/***********************************************/
	os_thread_sleep(1000000);
	buf_validate();
	buf_print();
	mtr_start(&mtr);

	seg_page = fseg_create(0, 0, 1000, 557, &mtr);

	mtr_commit(&mtr);

	ut_a(seg_page == 1);
	
	printf("Segment created: header page %lu\n", seg_page);

	new_page = seg_page;
	for (i = 0; i < 1023; i++) {

		mtr_start(&mtr);

		block = mtr_page_get(0, seg_page, NULL, &mtr);
	
		new_page = fseg_alloc_free_page(
					buf_block_get_frame(block) + 1000,
					new_page + 1, FSP_UP, &mtr);
		if (i < FSP_EXTENT_SIZE - 1) {
			ut_a(new_page == 2 + i);
		} else {
			ut_a(new_page == i + FSP_EXTENT_SIZE + 1);
		}	

		printf("%lu %lu; ", i, new_page);
		if (i % 10 == 0) {
			printf("\n");
		}
	
		mtr_commit(&mtr);
	}

	buf_print();
	buf_validate();

	mtr_start(&mtr);

	block = mtr_page_get(0, seg_page, NULL, &mtr);

	mtr_page_s_lock(block, &mtr);
	
	reserved = fseg_n_reserved_pages(buf_block_get_frame(block) + 1000,
					&used, &mtr);

	ut_a(used == 1024);	
	ut_a(reserved >= 1024);	

	printf("Pages used in segment %lu reserved by segment %lu \n",
		used, reserved);
	
	mtr_commit(&mtr);

	finished = FALSE;

	while (!finished) {

		mtr_start(&mtr);

		block = mtr_page_get(0, seg_page, NULL, &mtr);
	
		finished = fseg_free_step(
			buf_block_get_frame(block) + 1000, &mtr);	
	
		mtr_commit(&mtr);
	}

	buf_print();
	buf_validate();

	/***********************************************/

	mtr_start(&mtr);

	seg_page = fseg_create(0, 0, 1000, 557, &mtr);

	mtr_commit(&mtr);

	ut_a(seg_page == 1);

	mtr_start(&mtr);

	seg_page2 = fseg_create(0, 0, 1000, 558, &mtr);

	mtr_commit(&mtr);

	ut_a(seg_page2 == 2);
	
	new_page = seg_page;
	new_page2 = seg_page2;

	for (;;) {

		mtr_start(&mtr);

		block = mtr_page_get(0, seg_page, NULL, &mtr);
	
		new_page = fseg_alloc_free_page(
					buf_block_get_frame(block) + 1000,
					new_page + 1, FSP_UP, &mtr);

		printf("1:%lu %lu; ", i, new_page);
		if (i % 10 == 0) {
			printf("\n");
		}
	
		new_page = fseg_alloc_free_page(
					buf_block_get_frame(block) + 1000,
					new_page + 1, FSP_UP, &mtr);

		printf("1:%lu %lu; ", i, new_page);
		if (i % 10 == 0) {
			printf("\n");
		}

		mtr_commit(&mtr);

		mtr_start(&mtr);

		block = mtr_page_get(0, seg_page2, NULL, &mtr);
	
		new_page2 = fseg_alloc_free_page(
					buf_block_get_frame(block) + 1000,
					new_page2 + 1, FSP_UP, &mtr);

		printf("2:%lu %lu; ", i, new_page2);
		if (i % 10 == 0) {
			printf("\n");
		}
	
		mtr_commit(&mtr);

		if (new_page2 == FIL_NULL) {
			break;
		}
	}

	mtr_start(&mtr);

	block = mtr_page_get(0, seg_page, NULL, &mtr);

	mtr_page_s_lock(block, &mtr);
	
	reserved = fseg_n_reserved_pages(buf_block_get_frame(block) + 1000,
					&used, &mtr);

	printf("Pages used in segment 1 %lu, reserved by segment %lu \n",
		used, reserved);
	
	mtr_commit(&mtr);

	mtr_start(&mtr);

	block = mtr_page_get(0, seg_page2, NULL, &mtr);

	mtr_page_s_lock(block, &mtr);
	
	reserved = fseg_n_reserved_pages(buf_block_get_frame(block) + 1000,
					&used, &mtr);

	printf("Pages used in segment 2 %lu, reserved by segment %lu \n",
		used, reserved);
	
	mtr_commit(&mtr);
	
	for (;;) {

		mtr_start(&mtr);

		block = mtr_page_get(0, seg_page, NULL, &mtr);
	
		fseg_free_step(
			      buf_block_get_frame(block) + 1000, &mtr);
	
		block = mtr_page_get(0, seg_page2, NULL, &mtr);
	
		finished = fseg_free_step(
			      buf_block_get_frame(block) + 1000, &mtr);

		mtr_commit(&mtr);

		if (finished) {
			break;
		}
	}

	mtr_start(&mtr);

	seg_page2 = fseg_create(0, 0, 1000, 558, &mtr);

	mtr_commit(&mtr);

	i = 0;
	for (;;) {
		mtr_start(&mtr);

		block = mtr_page_get(0, seg_page2, NULL, &mtr);
	
		new_page2 = fseg_alloc_free_page(
					buf_block_get_frame(block) + 1000,
					557, FSP_DOWN, &mtr);

		printf("%lu %lu; ", i, new_page2);
		mtr_commit(&mtr);

		if (new_page2 == FIL_NULL) {
			break;
		}
		i++;
	}

	for (;;) {

		mtr_start(&mtr);

		block = mtr_page_get(0, seg_page, NULL, &mtr);
	
		finished = fseg_free_step(
			      buf_block_get_frame(block) + 1000, &mtr);
	
		mtr_commit(&mtr);

		if (finished) {
			break;
		}
	}

	for (;;) {

		mtr_start(&mtr);

		block = mtr_page_get(0, seg_page2, NULL, &mtr);
	
		finished = fseg_free_step(
			      buf_block_get_frame(block) + 1000, &mtr);
	
		mtr_commit(&mtr);

		if (finished) {
			break;
		}
	}

	
	/***************************************/
	
	oldtm = ut_clock();

    for (i = 0; i < 1000; i++) {	
	mtr_start(&mtr);

	seg_page = fseg_create(0, 0, 1000, 555, &mtr);

	mtr_commit(&mtr);

	mtr_start(&mtr);

	block = mtr_page_get(0, seg_page, NULL, &mtr);
	
	new_page = fseg_alloc_free_page(buf_block_get_frame(block) + 1000,
					2, FSP_UP, &mtr);	
	
	mtr_commit(&mtr);

	finished = FALSE;

	while (!finished) {
	
		mtr_start(&mtr);

		block = mtr_page_get(0, seg_page, NULL, &mtr);
	
		finished = fseg_free_step(
			buf_block_get_frame(block) + 1000, &mtr);	
	
		mtr_commit(&mtr);
	}
    }

	tm = ut_clock();
	printf("Wall clock time for %lu seg crea+free %lu millisecs\n",
			i, tm - oldtm);

	buf_validate();

	buf_flush_batch(BUF_FLUSH_LIST, 500);

	os_thread_sleep(1000000);
	
	buf_all_freed();
}	


/************************************************************************
Main test function. */

void 
main(void) 
/*======*/
{
	ulint	tm, oldtm;
	ulint	n;

	oldtm = ut_clock();
	
	os_aio_init(160, 5);
	sync_init();
	mem_init();
	fil_init(26);	/* Allow 25 open files at a time */
	buf_pool_init(1000, 1000);
	log_init();
	
	buf_validate();

	ut_a(fil_validate());
	
	create_files();

	create_db();

	buf_validate();

	test5();
/*
	test1();

	test3();

	test4();

	test2();
*/
	buf_validate();

	n = buf_flush_batch(BUF_FLUSH_LIST, 500);

	os_thread_sleep(1000000);
	
	buf_all_freed();
	
	free_system();	
	
	tm = ut_clock();
	printf("Wall clock time for test %lu milliseconds\n", tm - oldtm);
	printf("TESTS COMPLETED SUCCESSFULLY!\n");
}