<commit_before>/*******************************************************************************
 * Copyright 2016 vanilladb.org
 * 
 * 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.
 ******************************************************************************/
package org.vanilladb.core.storage.buffer;

import static org.junit.Assert.assertEquals;

import java.sql.Connection;

import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.vanilladb.core.server.ServerInit;
import org.vanilladb.core.server.VanillaDb;
import org.vanilladb.core.storage.file.BlockId;
import org.vanilladb.core.storage.tx.Transaction;

/**
 * All blocks used by this class should be numbered from 0 to
 * {@link VanillaDb#BUFFER_SIZE}.
 */
public class BufferMgrConcurrencyTest {
	
	private static final String TEST_FILE_NAME = "_tempbuffermgrtest";
	
	private static String result = "";
	
	@BeforeClass
	public static void init() {
		ServerInit.init(BufferMgrConcurrencyTest.class);
	}
	
	@Before
	public void before() {
		result = "";
	}

	@Test
	public void testRepinning() {
		Transaction initTx = VanillaDb.txMgr().newTransaction(
				Connection.TRANSACTION_SERIALIZABLE, false);
		
		// leave only two buffers available in buffer pool
		int avail = initTx.bufferMgr().available();
		for (int i = 0; i < avail - 2; i++) {
			// leave blocks 0 to 3 for testing
			BlockId blk = new BlockId(TEST_FILE_NAME, i + 4);
			initTx.bufferMgr().pin(blk);
		}
		
		// start testing
		try {
			TxClientD thD = new TxClientD(0, 1000);
			thD.start();
			TxClientE thE = new TxClientE(500, 1500);
			thE.start();
			try {
				thD.join();
				thE.join();
			} catch (InterruptedException e) {
			}
			String expected = "Tx D: pin 1 start\n" + "Tx D: pin 1 end\n"
					+ "Tx E: pin 2 start\n" + "Tx E: pin 2 end\n"
					+ "Tx D: pin 3 start\n" + "Tx E: pin 4 start\n"
					+ "Tx E: pin 4 end\n" + "Tx D: pin 3 end\n";
			assertEquals("*****TxTest: bad tx history", expected, result);
		} finally {
			initTx.rollback();
		}
	}

	synchronized static void appendToResult(String s) {
		result += s + "\n";
	}
	
	abstract class TxClient extends Thread {
		protected int[] pauses;
		protected boolean deadlockAborted;

		TxClient(int... pauses) {
			this.pauses = pauses;
		}

		boolean isDeadlockAborted() {
			return deadlockAborted;
		}
	}

	class TxClientD extends TxClient {
		TxClientD(int... pauses) {
			super(pauses);
		}

		@Override
		public void run() {
			Transaction tx = VanillaDb.txMgr().newTransaction(
					Connection.TRANSACTION_SERIALIZABLE, false);
			try {
				if (pauses[0] > 0)
					Thread.sleep(pauses[0]);

				appendToResult("Tx D: pin 1 start");
				BlockId blk1 = new BlockId(TEST_FILE_NAME, 0);
				tx.bufferMgr().pin(blk1);
				appendToResult("Tx D: pin 1 end");

				if (pauses[1] > 0)
					Thread.sleep(pauses[1]);

				appendToResult("Tx D: pin 3 start");
				BlockId blk3 = new BlockId(TEST_FILE_NAME, 2);
				tx.bufferMgr().pin(blk3);
				appendToResult("Tx D: pin 3 end");
			} catch (InterruptedException e) {
			} finally {
				tx.rollback();
			}
		}
	}

	class TxClientE extends TxClient {
		TxClientE(int... pauses) {
			super(pauses);
		}

		@Override
		public void run() {
			Transaction tx = VanillaDb.txMgr().newTransaction(
					Connection.TRANSACTION_SERIALIZABLE, false);
			try {
				if (pauses[0] > 0)
					Thread.sleep(pauses[0]);

				appendToResult("Tx E: pin 2 start");
				BlockId blk2 = new BlockId(TEST_FILE_NAME, 1);
				tx.bufferMgr().pin(blk2);
				appendToResult("Tx E: pin 2 end");

				if (pauses[1] > 0)
					Thread.sleep(pauses[1]);

				appendToResult("Tx E: pin 4 start");
				BlockId blk4 = new BlockId(TEST_FILE_NAME, 3);
				tx.bufferMgr().pin(blk4);
				appendToResult("Tx E: pin 4 end");
			} catch (InterruptedException e) {<commit_msg>Update the test case to make it easier reproduce bug #3<commit_after>/*******************************************************************************
 * Copyright 2016 vanilladb.org
 * 
 * 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.
 ******************************************************************************/
package org.vanilladb.core.storage.buffer;

import static org.junit.Assert.assertEquals;

import java.sql.Connection;

import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.vanilladb.core.server.ServerInit;
import org.vanilladb.core.server.VanillaDb;
import org.vanilladb.core.storage.file.BlockId;
import org.vanilladb.core.storage.tx.Transaction;

/**
 * All blocks used by this class should be numbered from 0 to
 * {@link VanillaDb#BUFFER_SIZE}.
 */
public class BufferMgrConcurrencyTest {
	
	private static final String TEST_FILE1_NAME = "_tempbufferconmgrtest1";
	private static final String TEST_FILE2_NAME = "_tempbufferconmgrtest2";
	
	private static String result = "";
	
	@BeforeClass
	public static void init() {
		ServerInit.init(BufferMgrConcurrencyTest.class);
	}
	
	@Before
	public void before() {
		result = "";
	}

	@Test
	public void testConcurrentRepinning() {
		Transaction initTx = VanillaDb.txMgr().newTransaction(
				Connection.TRANSACTION_SERIALIZABLE, false);
		
		// leave 11 buffers available in buffer pool
		int avail = initTx.bufferMgr().available();
		for (int i = 0; i < avail - 11; i++) {
			BlockId blk = new BlockId(TEST_FILE1_NAME, i);
			initTx.bufferMgr().pin(blk);
		}
		
		// start testing
		try {
			TxClientA a = new TxClientA(0, 1000);
			a.start();
			TxClientB b = new TxClientB(500, 1500);
			b.start();
			try {
				a.join();
				b.join();
			} catch (InterruptedException e) {
			}
			
			// Generate the expected result
			StringBuilder expected = new StringBuilder();
			for (int blkNum = 0; blkNum < 10; blkNum++) {
				expected.append("Tx A: pin " + blkNum + " start\n");
				expected.append("Tx A: pin " + blkNum + " end\n");
			}
			expected.append("Tx B: pin 10 start\n");
			expected.append("Tx B: pin 10 end\n");
			expected.append("Tx A: pin 11 start\n");
			expected.append("Tx B: pin 12 start\n");
			expected.append("Tx B: pin 12 end\n");
			expected.append("Tx A: pin 11 end\n");
			
			assertEquals("*****TxTest: bad tx history", expected.toString(), result);
		} finally {
			initTx.rollback();
		}
	}

	synchronized static void appendToResult(String s) {
		result += s + "\n";
	}
	
	abstract class TxClient extends Thread {
		// TODO: It may be better to use barriers here, instead of using time slicing
		protected int[] pauses;
		protected boolean deadlockAborted;
		
		private String threadName;

		TxClient(String name, int... pauses) {
			this.threadName = name;
			this.pauses = pauses;
		}

		boolean isDeadlockAborted() {
			return deadlockAborted;
		}
		
		void pin(Transaction tx, int blkNum) {
			appendToResult(threadName + ": pin " + blkNum + " start");
			
			BlockId blk = new BlockId(TEST_FILE2_NAME, blkNum);
			tx.bufferMgr().pin(blk);
			
			appendToResult(threadName + ": pin " + blkNum + " end");
		}
	}

	class TxClientA extends TxClient {
		TxClientA(int... pauses) {
			super("Tx A", pauses);
		}

		@Override
		public void run() {
			Transaction tx = VanillaDb.txMgr().newTransaction(
					Connection.TRANSACTION_SERIALIZABLE, false);
			try {
				if (pauses[0] > 0)
					Thread.sleep(pauses[0]);
				
				// pin 0 ~ 9
				for (int blkNum = 0; blkNum < 10; blkNum++)
					pin(tx, blkNum);

				if (pauses[1] > 0)
					Thread.sleep(pauses[1]);
				
				// pin 11
				pin(tx, 11);
			} catch (InterruptedException e) {
			} finally {
				tx.rollback();
			}
		}
	}

	class TxClientB extends TxClient {
		TxClientB(int... pauses) {
			super("Tx B", pauses);
		}

		@Override
		public void run() {
			Transaction tx = VanillaDb.txMgr().newTransaction(
					Connection.TRANSACTION_SERIALIZABLE, false);
			try {
				if (pauses[0] > 0)
					Thread.sleep(pauses[0]);
				
				// pin 10
				pin(tx, 10);

				if (pauses[1] > 0)
					Thread.sleep(pauses[1]);
				
				// pin 12
				pin(tx, 12);
			} catch (InterruptedException e) {
{'fraction_non_alphanumeric': 0.15418011800066792, 'fraction_numerical': 0.015918958031837915, 'mean_word_length': 3.4299802761341223, 'pattern_counts': {'":': 2, '<': 7, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5566142', 'n_tokens_mistral': 3454, 'n_tokens_neox': 2885, 'n_words': 830}