Beispiel #1
0
void
test_txn5_06()
{
	GlobalTransactionId gxid;
	int rc;

	SETUP();

	gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp);
	_ASSERT( gxid != InvalidGlobalTransactionId );

	rc = prepare_transaction(conn, gxid);
	_ASSERT( rc>=0 );

	rc = abort_transaction(conn, gxid);
	_ASSERT( rc>=0 );

	system("./promote.sh");

	GTMPQfinish(conn);
	connect2();

	rc = abort_transaction(conn, gxid);
	_ASSERT( rc>=0 );

	TEARDOWN();
}
Beispiel #2
0
int
RollbackTranGTM(GlobalTransactionId gxid)
{
	int ret = -1;

	if (!GlobalTransactionIdIsValid(gxid))
		return 0;
	CheckConnection();

	if (conn)
		ret = abort_transaction(conn, gxid);

	/*
	 * If something went wrong (timeout), try and reset GTM connection.
	 * We will abort the transaction locally anyway, and closing GTM will force
	 * it to end on GTM.
	 */
	if (ret < 0)
	{
		CloseGTM();
		InitGTM();
#ifdef XCP
		if (conn)
			ret = abort_transaction(conn, gxid);
#endif
	}

	currentGxid = InvalidGlobalTransactionId;
	return ret;
}
int64 LMDBFileIndex::get(const LMDBFileIndex::SIndexKey& key)
{
	begin_txn(MDB_RDONLY);

	MDB_val mdb_tkey;
	mdb_tkey.mv_data=const_cast<void*>(static_cast<const void*>(&key));
	mdb_tkey.mv_size=sizeof(SIndexKey);

	MDB_val mdb_tvalue;

	int rc=mdb_get(txn, dbi, &mdb_tkey, &mdb_tvalue);

	int64 ret = 0;
	if(rc==MDB_NOTFOUND)
	{
		
	}
	else if(rc)
	{
		Server->Log("LMDB: Failed to read ("+(std::string)mdb_strerror(rc)+")", LL_ERROR);
		_has_error=true;
	}
	else
	{
		CRData data((const char*)mdb_tvalue.mv_data, mdb_tvalue.mv_size);
		
		data.getVarInt(&ret);
	}

	abort_transaction();

	return ret;
}
Beispiel #4
0
void exec_operations(GSList *op_list) {
	struct operation *op;
	enum op_stats stats;

	if (op_list == NULL)
		return;

	lock_s_table = g_hash_table_new(g_str_hash, g_str_equal);
	lock_x_table = g_hash_table_new(g_str_hash, g_str_equal);
	wait_table = g_hash_table_new(g_str_hash, g_str_equal);
	aborted_transaction_list = NULL;

	int i = 0;
	while ((i < g_slist_length(op_list)) || (g_hash_table_size(wait_table) > 0)) {
		exec_waiting_operations();
		if (i < g_slist_length(op_list)) {
			op = g_slist_nth_data(op_list, i);
			if (did_aborted(op)) {
				i++;
				continue;
			}
			printf("EXEC: ");
			dump_operation(op);
			stats = operation_status(op);
			if (stats == OP_WAIT)
				add_transaction_to_wait(op);
			else if (stats != OP_OK) {
				printf("ERROR: ");
				dump_operation(op);
				abort_transaction(op);
			}
			i++;
		}
		check_deadlocks();
#ifdef DEBUG
		dump_wait_table();
		dump_lock_x_table();
		dump_lock_s_table();
#endif
	}

#ifdef DEBUG
	dump_lock_s_table();
	dump_lock_x_table();
	dump_wait_table();
	dump_unlocked_list();
	dump_aborted_list();
#endif

	if ((g_hash_table_size(lock_x_table) > 0) ||
			(g_hash_table_size(lock_s_table) > 0) ||
			(g_hash_table_size(wait_table) > 0))
		printf("ERROR!\n");

	g_hash_table_destroy(lock_s_table);
	g_hash_table_destroy(lock_x_table);
	g_hash_table_destroy(wait_table);
	g_slist_free(unlocked_transaction_list);
	g_slist_free(aborted_transaction_list);
}
Beispiel #5
0
static int __logfs_create(struct inode *dir, struct dentry *dentry,
		struct inode *inode, const char *dest, long destlen)
{
	struct logfs_super *super = logfs_super(dir->i_sb);
	struct logfs_inode *li = logfs_inode(inode);
	struct logfs_transaction *ta;
	int ret;

	ta = kzalloc(sizeof(*ta), GFP_KERNEL);
	if (!ta) {
		inode->i_nlink--;
		iput(inode);
		return -ENOMEM;
	}

	ta->state = CREATE_1;
	ta->ino = inode->i_ino;
	mutex_lock(&super->s_dirop_mutex);
	logfs_add_transaction(inode, ta);

	if (dest) {
		/* symlink */
		ret = logfs_inode_write(inode, dest, destlen, 0, WF_LOCK, NULL);
		if (!ret)
			ret = write_inode(inode);
	} else {
		/* creat/mkdir/mknod */
		ret = write_inode(inode);
	}
	if (ret) {
		abort_transaction(inode, ta);
		li->li_flags |= LOGFS_IF_STILLBORN;
		/* FIXME: truncate symlink */
		inode->i_nlink--;
		iput(inode);
		goto out;
	}

	ta->state = CREATE_2;
	logfs_add_transaction(dir, ta);
	ret = logfs_write_dir(dir, dentry, inode);
	/* sync directory */
	if (!ret)
		ret = write_inode(dir);

	if (ret) {
		logfs_del_transaction(dir, ta);
		ta->state = CREATE_2;
		logfs_add_transaction(inode, ta);
		logfs_remove_inode(inode);
		iput(inode);
		goto out;
	}
	d_instantiate(dentry, inode);
out:
	mutex_unlock(&super->s_dirop_mutex);
	return ret;
}
Beispiel #6
0
int
main(int argc, char *argv[])
{
	int ii;
	int jj;

#define TXN_COUNT		10000
#define LOOP_COUNT		10
	
	GlobalTransactionId gxid[TXN_COUNT];
	GTM_Conn *conn;
	char connect_string[100];

	sprintf(connect_string, "host=localhost port=6666 node_name=one remote_type=%d", GTM_NODE_COORDINATOR);

	conn = PQconnectGTM(connect_string);
	if (conn == NULL)
	{
		client_log(("Error in connection\n"));
		exit(1);
	}

	for (jj = 0; jj < LOOP_COUNT; jj++)
	{
		for (ii = 0; ii < TXN_COUNT; ii++)
		{
			int kk;
			GTM_Snapshot snapshot;

			gxid[ii] = begin_transaction(conn, GTM_ISOLATION_RC);
			if (gxid[ii] != InvalidGlobalTransactionId)
				client_log(("Started a new transaction (GXID:%u)\n", gxid[ii]));
			else
				client_log(("BEGIN transaction failed for ii=%d\n", ii));
			snapshot = get_snapshot(conn, gxid[ii], true);


			if (ii % 2 == 0)
			{
				if (!abort_transaction(conn, gxid[ii]))
					client_log(("ROLLBACK successful (GXID:%u)\n", gxid[ii]));
				else
					client_log(("ROLLBACK failed (GXID:%u)\n", gxid[ii]));
			}
			else
			{
				if (!commit_transaction(conn, gxid[ii]))
					client_log(("COMMIT successful (GXID:%u)\n", gxid[ii]));
				else
					client_log(("COMMIT failed (GXID:%u)\n", gxid[ii]));
			}
		}
	}

	GTMPQfinish(conn);
	return 0;
}
Beispiel #7
0
static void transact_command(ostream &out, istream &in)
{
   int choice ;
   int transaction ;
   int result ;

   do {
      choice = display_menu(out,in,true,3,
			    "Transactions",
			    "\t1. Start a transaction\n"
			    "\t2. End a transaction\n"
			    "\t3. Abort a transaction\n"
			    ) ;
      switch (choice)
	 {
	 case 0:
	    // do nothing
	    break ;
	 case 1:
	    transaction = start_transaction() ;
	    if (transaction == -1)
	       out << "Unable to start transaction" << endl ;
	    else
	       out << "Started transaction number " << transaction << endl ;
 	    break ;
	 case 2:
	    transaction = get_number(out,in,"Transaction number:",-1,
				     SHRT_MAX) ;
	    result = end_transaction(transaction) ;
	    if (result == -1)
	       out << "Failed while ending transaction, error code = "
		   << Fr_errno << endl ;
	    else
	       out << "Transaction ended successfully" << endl ;
 	    break ;
	 case 3:
	    transaction = get_number(out,in,"Transaction number:",-1,
				     SHRT_MAX) ;
	    result = abort_transaction(transaction) ;
	    if (result == -1)
	       {
	       out << "Failed while aborting transaction, error code = "
		   << Fr_errno << endl ;
	       }
	    else
	       out << "Transaction successfully rolled back" << endl ;
 	    break ;
	 default:
	    FrMissedCase("transact_command") ;
	    break ;
	 }
      } while (choice != 0) ;
   return ;
}
int64 LMDBFileIndex::get_prefer_client( const SIndexKey& key )
{
	begin_txn(MDB_RDONLY);

	MDB_cursor* cursor;

	mdb_cursor_open(txn, dbi, &cursor);

	SIndexKey orig_key = key;

	MDB_val mdb_tkey;
	mdb_tkey.mv_data=const_cast<void*>(static_cast<const void*>(&key));
	mdb_tkey.mv_size=sizeof(SIndexKey);

	MDB_val mdb_tvalue;

	int rc=mdb_cursor_get(cursor,&mdb_tkey, &mdb_tvalue, MDB_SET_RANGE);

	int64 ret = 0;
	int retry_prev=2;
	while(rc==0 && retry_prev>0 && !_has_error)
	{
		SIndexKey* curr_key = reinterpret_cast<SIndexKey*>(mdb_tkey.mv_data);

		if(rc==MDB_NOTFOUND)
		{
			retry_prev=0;
		}
		else if(rc)
		{
			Server->Log("LMDB: Failed to read ("+(std::string)mdb_strerror(rc)+")", LL_ERROR);
			_has_error=true;
		}
		else if( curr_key->isEqualWithoutClientid(orig_key))
		{
			CRData data((const char*)mdb_tvalue.mv_data, mdb_tvalue.mv_size);
			data.getVarInt(&ret);
			retry_prev=0;
		}
		else
		{
			rc=mdb_cursor_get(cursor, &mdb_tkey, &mdb_tvalue, MDB_PREV);
			--retry_prev;
		}
	}

	mdb_cursor_close(cursor);

	abort_transaction();

	return ret;
}
Beispiel #9
0
static int logfs_unlink(struct inode *dir, struct dentry *dentry)
{
	struct logfs_super *super = logfs_super(dir->i_sb);
	struct inode *inode = dentry->d_inode;
	struct logfs_transaction *ta;
	struct page *page;
	pgoff_t index;
	int ret;

	ta = kzalloc(sizeof(*ta), GFP_KERNEL);
	if (!ta)
		return -ENOMEM;

	ta->state = UNLINK_1;
	ta->ino = inode->i_ino;

	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;

	page = logfs_get_dd_page(dir, dentry);
	if (!page) {
		kfree(ta);
		return -ENOENT;
	}
	if (IS_ERR(page)) {
		kfree(ta);
		return PTR_ERR(page);
	}
	index = page->index;
	page_cache_release(page);

	mutex_lock(&super->s_dirop_mutex);
	logfs_add_transaction(dir, ta);

	ret = logfs_delete(dir, index, NULL);
	if (!ret)
		ret = write_inode(dir);

	if (ret) {
		abort_transaction(dir, ta);
		printk(KERN_ERR"LOGFS: unable to delete inode\n");
		goto out;
	}

	ta->state = UNLINK_2;
	logfs_add_transaction(inode, ta);
	ret = logfs_remove_inode(inode);
out:
	mutex_unlock(&super->s_dirop_mutex);
	return ret;
}
std::map<int, int64> LMDBFileIndex::get_all_clients( const SIndexKey& key )
{
	begin_txn(MDB_RDONLY);

	MDB_cursor* cursor;

	mdb_cursor_open(txn, dbi, &cursor);

	SIndexKey orig_key = key;

	MDB_val mdb_tkey;
	mdb_tkey.mv_data=const_cast<void*>(static_cast<const void*>(&key));
	mdb_tkey.mv_size=sizeof(SIndexKey);

	MDB_val mdb_tvalue;

	int rc=mdb_cursor_get(cursor,&mdb_tkey, &mdb_tvalue, MDB_SET_RANGE);

	std::map<int, int64> ret;

	SIndexKey* curr_key = reinterpret_cast<SIndexKey*>(mdb_tkey.mv_data);

	while(rc==0 &&
		orig_key.isEqualWithoutClientid(*curr_key))
	{
		CRData data((const char*)mdb_tvalue.mv_data, mdb_tvalue.mv_size);
		int64 entryid;
		data.getVarInt(&entryid);

		ret[curr_key->getClientid()] = entryid;

		rc=mdb_cursor_get(cursor, &mdb_tkey, &mdb_tvalue, MDB_NEXT);
		curr_key = reinterpret_cast<SIndexKey*>(mdb_tkey.mv_data);
	}


	if(rc && rc!=MDB_NOTFOUND)
	{
		Server->Log("LMDB: Failed to read ("+(std::string)mdb_strerror(rc)+")", LL_ERROR);
		_has_error=true;
	}

	mdb_cursor_close(cursor);

	abort_transaction();

	return ret;
}
Beispiel #11
0
/*
 * Cross-directory rename, target does not exist.  Just a little nasty.
 * Create a new dentry in the target dir, then remove the old dentry,
 * all the while taking care to remember our operation in the journal.
 */
static int logfs_rename_cross(struct inode *old_dir, struct dentry *old_dentry,
			      struct inode *new_dir, struct dentry *new_dentry)
{
	struct logfs_super *super = logfs_super(old_dir->i_sb);
	struct logfs_disk_dentry dd;
	struct logfs_transaction *ta;
	loff_t pos;
	int err;

	/* 1. locate source dd */
	err = logfs_get_dd(old_dir, old_dentry, &dd, &pos);
	if (err)
		return err;

	ta = kzalloc(sizeof(*ta), GFP_KERNEL);
	if (!ta)
		return -ENOMEM;

	ta->state = CROSS_RENAME_1;
	ta->dir = old_dir->i_ino;
	ta->pos = pos;

	/* 2. write target dd */
	mutex_lock(&super->s_dirop_mutex);
	logfs_add_transaction(new_dir, ta);
	err = logfs_write_dir(new_dir, new_dentry, old_dentry->d_inode);
	if (!err)
		err = write_inode(new_dir);

	if (err) {
		super->s_rename_dir = 0;
		super->s_rename_pos = 0;
		abort_transaction(new_dir, ta);
		goto out;
	}

	/* 3. remove source dd */
	ta->state = CROSS_RENAME_2;
	logfs_add_transaction(old_dir, ta);
	err = logfs_delete_dd(old_dir, pos);
	if (!err)
		err = write_inode(old_dir);
	LOGFS_BUG_ON(err, old_dir->i_sb);
out:
	mutex_unlock(&super->s_dirop_mutex);
	return err;
}
int validate_transaction(tr_submit_msg* t) {
	if (SKIP_VALIDATION) {
		commit_transaction(t);
		return 1;
	}
	
    if ((t->start >= (vs.ST - MaxPreviousST)) && (t->start <= vs.ST)) {
        if (validate(t)) {
            commit_transaction(t);
            return 1;
        }
    } else {
        too_old++;
    }
    abort_transaction(t);
    return 0;
}
void
test01()
{
    GlobalTransactionId gxid;
    int rc;
    char host[1024];

    printf("\n=== test01:node_register ===\n");

    setUp();

    node_get_local_addr(conn, host, sizeof(host));

    /*
     * starting
     */
    rc = node_register_internal(conn, GTM_NODE_GTM, host, 6667, "One zero two", "/tmp/pgxc/data/gtm_standby", NODE_DISCONNECTED);
    _ASSERT(rc == 0);
    rc = node_unregister(conn, GTM_NODE_GTM, "One zero two");
    _ASSERT(rc == 0);

    rc = node_register_internal(conn, GTM_NODE_GTM, host, 6667, "One zero two", "/tmp/pgxc/data/gtm_standby", NODE_CONNECTED);
    _ASSERT(rc == 0);

    sleep(10);

    gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp);
    _ASSERT( gxid!=InvalidGlobalTransactionId );

    rc = prepare_transaction(conn, gxid);
    _ASSERT( rc!=-1 );

    rc = abort_transaction(conn, gxid);
    _ASSERT( rc!=-1 );

    sleep(10);

    /*
     * closing
     */
    rc = node_unregister(conn, GTM_NODE_GTM, "One zero two");
    _ASSERT( rc==0 );

    tearDown();
}
Beispiel #14
0
static void check_deadlocks() {
	int key1, key2;
	char *sk1, *sk2;

	if (lock_waiting_list == NULL)
		return;

	GList *trans_waiting = g_hash_table_get_keys(wait_table);

	for (int i = 0; i < g_list_length(trans_waiting); i++) {
		sk1 = g_list_nth_data(trans_waiting, i);
		if (sk1 == NULL)
			continue;

		for (int j = 0; j < g_list_length(trans_waiting); j++) {
			sk2 = g_list_nth_data(trans_waiting, j);
			if (sk2 == NULL)
				continue;

			key1 = atoi(sk1);
			key2 = atoi(sk2);
			if (key1 == key2)
				continue;

			if (is_locking(key1, key2) && is_locking(key2, key1)) {
				printf("* DEADLOCK DETECTED *\n");

				// Selecionando pela operação mais velha
				sk1 = g_strdup_printf("%d", key1);
				GSList *op_list = g_hash_table_lookup(wait_table, sk1);
				g_free(sk1);

				if (g_slist_length(op_list) == 0)
					continue;

				struct operation *op = g_slist_nth_data(op_list, 0);
				if (op == NULL)
					continue;

				abort_transaction(op);
				return;
			}
		}
	}
}
int validate_phase2(tr_submit_msg* t, int commit) {
	int i;
	flat_key_hash* rs_hashes;
	
	if (commit) {
		rs_hashes = TR_SUBMIT_MSG_RS_HASH(t);
		for (i = 0; i < t->readset_count; i++) {
	        if (bloom_contains_hashes(vs.ws, rs_hashes[i].hash)) {
	            ws_conflict++;
				commit = 0;
				break;
	        }
	    }
	}
	
	if (commit)
		commit_transaction(t);
	else 
		abort_transaction(t);

	return commit;
}
Beispiel #16
0
void
test_txn4_02()
{
	GlobalTransactionId gxid;
	int rc;

	SETUP();

	gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp);
	_ASSERT( gxid != InvalidGlobalTransactionId );

	rc = prepare_transaction(conn, gxid);
	_ASSERT( rc>=0 );

	rc = abort_transaction(conn, gxid);
	_ASSERT( rc>=0 );

	_ASSERT( grep_count(LOG_STANDBY, "Sending transaction id 4")==1 );
	_ASSERT( grep_count(LOG_STANDBY, "Preparing transaction id 4")==1 );
	_ASSERT( grep_count(LOG_STANDBY, "Cancelling transaction id 4")==1 );

	TEARDOWN();
}
Beispiel #17
0
/* Target dentry exists - the worst case.  We need to attach the source
 * inode to the target dentry, then remove the orphaned target inode and
 * source dentry.
 */
static int logfs_rename_target(struct inode *old_dir, struct dentry *old_dentry,
			       struct inode *new_dir, struct dentry *new_dentry)
{
	struct logfs_super *super = logfs_super(old_dir->i_sb);
	struct inode *old_inode = old_dentry->d_inode;
	struct inode *new_inode = new_dentry->d_inode;
	int isdir = S_ISDIR(old_inode->i_mode);
	struct logfs_disk_dentry dd;
	struct logfs_transaction *ta;
	loff_t pos;
	int err;

	BUG_ON(isdir != S_ISDIR(new_inode->i_mode));
	if (isdir) {
		if (!logfs_empty_dir(new_inode))
			return -ENOTEMPTY;
	}

	/* 1. locate source dd */
	err = logfs_get_dd(old_dir, old_dentry, &dd, &pos);
	if (err)
		return err;

	ta = kzalloc(sizeof(*ta), GFP_KERNEL);
	if (!ta)
		return -ENOMEM;

	ta->state = TARGET_RENAME_1;
	ta->dir = old_dir->i_ino;
	ta->pos = pos;
	ta->ino = new_inode->i_ino;

	/* 2. attach source inode to target dd */
	mutex_lock(&super->s_dirop_mutex);
	logfs_add_transaction(new_dir, ta);
	err = logfs_replace_inode(new_dir, new_dentry, &dd, old_inode);
	if (err) {
		super->s_rename_dir = 0;
		super->s_rename_pos = 0;
		super->s_victim_ino = 0;
		abort_transaction(new_dir, ta);
		goto out;
	}

	/* 3. remove source dd */
	ta->state = TARGET_RENAME_2;
	logfs_add_transaction(old_dir, ta);
	err = logfs_delete_dd(old_dir, pos);
	if (!err)
		err = write_inode(old_dir);
	LOGFS_BUG_ON(err, old_dir->i_sb);

	/* 4. remove target inode */
	ta->state = TARGET_RENAME_3;
	logfs_add_transaction(new_inode, ta);
	err = logfs_remove_inode(new_inode);

out:
	mutex_unlock(&super->s_dirop_mutex);
	return err;
}
Beispiel #18
0
int
main(int argc, char *argv[])
{
	int ii;
	GlobalTransactionId gxid[4000];
	GTM_Conn *conn;
	char connect_string[100];

	for (ii = 0; ii < 3; ii++)
		fork();

	sprintf(connect_string, "host=localhost port=6666 node_name=one remote_type=%d", GTM_NODE_COORDINATOR);

	conn = PQconnectGTM(connect_string);
	if (conn == NULL)
	{
		client_log(("Error in connection\n"));
		exit(1);
	}

	for (ii = 0; ii < 20; ii++)
	{
		gxid[ii] = begin_transaction(conn, GTM_ISOLATION_RC);
		if (gxid[ii] != InvalidGlobalTransactionId)
			client_log(("Started a new transaction (GXID:%u)\n", gxid[ii]));
		else
			client_log(("BEGIN transaction failed for ii=%d\n", ii));
	}

	for (ii = 0; ii < 5; ii++)
	{
		int jj;
		GTM_Snapshot snapshot = get_snapshot(conn, gxid[ii], true);
		if (snapshot != NULL)
		{
			client_log(("Snapshot: GXID %u, xmin=%u, xmax=%u\n", gxid[ii],
					snapshot->sn_xmin, snapshot->sn_xmax));
			client_log(("xcnt=%d %s", snapshot->sn_xcnt,
					snapshot->sn_xcnt > 0 ? "xip=(" : ""));
			for (jj = 0; jj < snapshot->sn_xcnt; jj++)
				client_log(("%d%c ", snapshot->sn_xip[jj],
						((jj + 1) == snapshot->sn_xcnt) ? ')' : ','));
			client_log(("\n"));
		}
	}

	for (ii = 0; ii < 20; ii++)
	{
		if (!prepare_transaction(conn, gxid[ii]))
			client_log(("PREPARE successful (GXID:%u)\n", gxid[ii]));
		else
			client_log(("PREPARE failed (GXID:%u)\n", gxid[ii]));
	}

	for (ii = 0; ii < 20; ii++)
	{
		if (ii % 2 == 0)
		{
			if (!abort_transaction(conn, gxid[ii]))
				client_log(("ROLLBACK successful (GXID:%u)\n", gxid[ii]));
			else
				client_log(("ROLLBACK failed (GXID:%u)\n", gxid[ii]));
		}
		else
		{
			if (!commit_transaction(conn, gxid[ii]))
				client_log(("COMMIT successful (GXID:%u)\n", gxid[ii]));
			else
				client_log(("COMMIT failed (GXID:%u)\n", gxid[ii]));
		}
	}

	GTMPQfinish(conn);
	return 0;
}
Beispiel #19
0
bool arbiter_interested(struct ls_state *ls, bool just_finished_reschedule,
			bool *voluntary, bool *need_handle_sleep, bool *data_race,
			bool *joined, bool *xbegin)
{
	*voluntary = false;
	*need_handle_sleep = false;
	*data_race = false;
	*joined = false;
	*xbegin = false;

	/* Attempt to see if a "voluntary" reschedule is just ending - did the
	 * last thread context switch not because of a timer?
	 * Also make sure to ignore null switches (timer-driven or not). */
	if (ls->sched.last_agent != NULL &&
	    !ls->sched.last_agent->action.handling_timer &&
	    ls->sched.last_agent != ls->sched.cur_agent &&
	    just_finished_reschedule) {
		lsprintf(DEV, "a voluntary reschedule: ");
		print_agent(DEV, ls->sched.last_agent);
		printf(DEV, " to ");
		print_agent(DEV, ls->sched.cur_agent);
		printf(DEV, "\n");
#ifndef PINTOS_KERNEL
		/* Pintos includes a semaphore implementation which can go
		 * around its anti-paradise-lost while loop a full time without
		 * interrupts coming back on. So, there can be a voluntary
		 * reschedule sequence where an uninterruptible, blocked thread
		 * gets jammed in the middle of this transition. Issue #165. */
		if (ls->save.next_tid != ls->sched.last_agent->tid) {
			ASSERT_ONE_THREAD_PER_PP(ls);
		}
#endif
		assert(ls->sched.voluntary_resched_tid != TID_NONE);
		*voluntary = true;
		return true;
	/* is the kernel idling, e.g. waiting for keyboard input? */
	} else if (ls->instruction_text[0] == OPCODE_HLT) {
		lskprintf(INFO, "What are you waiting for? (HLT state)\n");
		*need_handle_sleep = true;
		ASSERT_ONE_THREAD_PER_PP(ls);
		return true;
	/* Skip the instructions before the test case itself gets started. In
	 * many kernels' cases this will be redundant, but just in case. */
	} else if (!ls->test.test_ever_caused ||
		   ls->test.start_population == ls->sched.most_agents_ever) {
		return false;
	/* check for data races */
	} else if (suspected_data_race(ls)
		   /* if xchg-blocked, need NOT set DR PP. other case below. */
		   && !XCHG_BLOCKED(&ls->sched.cur_agent->user_yield)
#ifdef DR_PPS_RESPECT_WITHIN_FUNCTIONS
		   // NB. The use of KERNEL_MEMORY here used to be !testing_userspace.
		   // I needed to change it to implement preempt-everywhere mode,
		   // to handle the case of userspace shms in deschedule() syscall.
		   // Not entirely sure of all implications of this change.
		   && ((!KERNEL_MEMORY(ls->eip) && user_within_functions(ls)) ||
		      (KERNEL_MEMORY(ls->eip) && kern_within_functions(ls)))
#endif
#ifndef HTM_WEAK_ATOMICITY
		   && !ls->sched.cur_agent->action.user_txn
#endif
		   ) {
		*data_race = true;
		ASSERT_ONE_THREAD_PER_PP(ls);
		return true;
	/* user-mode-only preemption points */
	} else if (testing_userspace()) {
		unsigned int mutex_addr;
		if (KERNEL_MEMORY(ls->eip)) {
#ifdef GUEST_YIELD_ENTER
#ifndef GUEST_YIELD_EXIT
			STATIC_ASSERT(false && "missing guest yield exit");
#endif
			if ((ls->eip == GUEST_YIELD_ENTER &&
			     READ_STACK(ls->cpu0, 1) == ls->sched.cur_agent->tid) ||
			    (ls->eip == GUEST_YIELD_EXIT &&
			     ((signed int)GET_CPU_ATTR(ls->cpu0, eax)) < 0)) {
				/* Busted yield. Pretend it was yield -1. */
				ASSERT_ONE_THREAD_PER_PP(ls);
				return true;
			}
#endif
			return false;
		} else if (XCHG_BLOCKED(&ls->sched.cur_agent->user_yield)) {
			/* User thread is blocked on an "xchg-continue" mutex.
			 * Analogous to HLT state -- need to preempt it. */
			ASSERT_ONE_THREAD_PER_PP(ls);
#ifndef HTM_WEAK_ATOMICITY
			/* under strong atomicity, if for whatever reason a txn
			 * blocks, there's no way it should ever succeed */
			if (ls->sched.cur_agent->action.user_txn) {
				abort_transaction(ls->sched.cur_agent->tid,
						  ls->save.current, _XABORT_CAPACITY);
				ls->end_branch_early = true;
				return false;
			}
#endif
			return true;
#ifndef PINTOS_KERNEL
		} else if (!check_user_address_space(ls)) {
			return false;
#endif
		} else if ((user_mutex_lock_entering(ls->cpu0, ls->eip, &mutex_addr) ||
			    user_mutex_unlock_exiting(ls->eip)) &&
			   user_within_functions(ls)) {
			ASSERT_ONE_THREAD_PER_PP(ls);
#ifndef HTM_WEAK_ATOMICITY
			/* by the equivalence proof, it's sound to skip this pp
			 * because if anything were to conflict with it, it'd be
			 * the same as if the txn aborted to begin with */
			if (ls->sched.cur_agent->action.user_txn) {
				return false;
			}
			/* on other hand, under weak memory maybe the user needs
			 * this mutex to protect against some non-txnal code */
#endif
			return true;
#ifdef USER_MAKE_RUNNABLE_EXIT
		} else if (ls->eip == USER_MAKE_RUNNABLE_EXIT) {
			/* i think the reference kernel version i have might
			 * predate the make runnable misbehave mode, because it
			 * seems not to be putting yield pps on it.*/
			ASSERT_ONE_THREAD_PER_PP(ls);
			return true;
#endif
#ifdef TRUSTED_THR_JOIN
		} else if (user_thr_join_exiting(ls->eip)) {
			/* don't respect within functions, obv; this pp is for
			 * happens-before purposes, not scheduling, anyway */
			ASSERT_ONE_THREAD_PER_PP(ls);
			*joined = true;
			return true;
#ifndef USER_MAKE_RUNNABLE_EXIT
		} else if (true) {
			assert(0 && "need mkrun pp for trusted join soundness");
#endif
#endif
		} else if (user_xbegin_entering(ls->eip) ||
			   user_xend_entering(ls->eip)) {
			/* Have to disrespect within functions to properly
			 * respect htm-blocking if there's contention. */
			ASSERT_ONE_THREAD_PER_PP(ls);
			*xbegin = user_xbegin_entering(ls->eip);
			return true;
		} else {
			return false;
		}
	/* kernel-mode-only preemption points */
#ifdef PINTOS_KERNEL
	} else if ((ls->eip == GUEST_SEMA_DOWN_ENTER || ls->eip == GUEST_SEMA_UP_EXIT) && kern_within_functions(ls)) {
		ASSERT_ONE_THREAD_PER_PP(ls);
		return true;
	} else if ((ls->eip == GUEST_CLI_ENTER || ls->eip == GUEST_STI_EXIT) &&
		   !ls->sched.cur_agent->action.kern_mutex_locking &&
		   !ls->sched.cur_agent->action.kern_mutex_unlocking &&
		   kern_within_functions(ls)) {
		ASSERT_ONE_THREAD_PER_PP(ls);
		return true;
#endif
	} else if (kern_decision_point(ls->eip) &&
		   kern_within_functions(ls)) {
		ASSERT_ONE_THREAD_PER_PP(ls);
		return true;
	} else {
		return false;
	}
}