/****************************************************************************/
/*	Copyright (c) 2009, Bendor Research Pty. Ltd. All rights reserved.		*/
/*	This software is released under the GNU General Public License, version	*/
/*	3, with the exemptions that 1) the header files installed by this 	 	*/
/*	package can be included in closed-source code and 2) the libraries		*/
/*	built by this package can be statically linked into a closed-source 	*/
/*	binary. See the file LICENSE for the details.							*/
/*	This software is provided "AS IS", without any warranty or guarantee.	*/
/*	By using this software you acknowledge that neither Bendor Research nor	*/
/*	any of its personnel or affiliates can be held liable for any damages,	*/
/*	including, but not limited to, loss of business, loss of money, loss of */
/*	life, loss of reputation, arising out of this software being faulty or	*/
/*	not performing as advertised, even if Bendor Research was aware of such	*/
/*	faults in the software. You use this software at your own risk, any and */
/*	all liabilities that arise out of your using this software are strictly	*/
/*	yours.																	*/
/****************************************************************************/

#include "kernel.h"

/****************************************************************************/
/*				Mutating object functionality								*/
/****************************************************************************/

/*
*	A process obtained a priority mutating object
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*	The object is the first parameter, the process is the second.
*/

void	_bx_PmutAdd( void *par1, void *par2 )
{
BX_PMUT		*pmut = par1;
BX_PROC		*proc = par2;
BX_PMUT		*test;
unsigned	prio;

	// Set the object's owner to the process

	pmut->proc = proc;

	// Insert the object to the process's waiting list

	for ( test=proc->head ; test && test->prio > pmut->prio ; test=test->next );

	if ( test )

		_bx_PmutInsBefore( proc, pmut, test );
	else
		_bx_PmutAddTail( proc, pmut );

	// If the object's current priority is above that of the
	// process, then we must adjust the priority of the process
	// itself.

	if ( pmut->prio > proc->epri ) {

		prio = pmut->prio;
		_bx_PmutPrio( proc, &prio  );
	}
}

/*
*	A process released a priority mutating object
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*	The object is the first parameter, the process is the second.
*/

void	_bx_PmutRem( void *par1, void *par2 )
{
BX_PMUT		*pmut = par1;
BX_PROC		*proc = par2;
unsigned	prio;

	// Remove the object from the process's list of mutating objects

	_bx_PmutDel( proc, pmut );

	// Now we have to do two things. First, adjust the priority
	// of the process now that it does not own the object any more.
	// This can lead to a priority adjustment cascade.

	prio = 0;
	if ( proc->head ) prio = proc->head->prio;
	if ( proc->rpri > prio ) prio = proc->rpri;

	if ( proc->epri != prio ) {

		_bx_PmutPrio( proc, &prio );
	}
}

/*
*	A mutating object's priority was changed
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*	pmut points to the object
*	proc points to the owner of the object
*	prio points to the new priority of the object
*/

BX_PROC		*_bx_PmutMut( BX_PMUT *pmut, BX_PROC *proc, unsigned *prio )
{
unsigned	newp;
BX_PMUT		*test;

	if ( ! proc ) return( NULL );

	// We may need to move the object on the process's mutator list
	// Find the new position

	newp = *prio;
	test = pmut;

	if ( *prio > pmut->prio ) {

		// The object's priority went up, search towards the head

		while ( test->prev && test->prev->prio < newp ) test = test->prev;

		if ( test != pmut ) {

			_bx_PmutDel( proc, pmut );
			_bx_PmutInsBefore( proc, pmut, test );
		}
	}
	else {

		while ( test->next && test->next->prio > newp ) test = test->next;

		if ( test != pmut ) {

			_bx_PmutDel( proc, pmut );
			_bx_PmutInsAfter( proc, pmut, test );
		}
	}

	// Set the new priority for the object

	pmut->prio = newp;

	// Now we have to check if the priority of the process that owns
	// the mutator object was changed or not

	newp = 0;
	if ( proc->head ) newp = proc->head->prio;
	if ( proc->rpri > newp ) newp = proc->rpri;

	if ( newp != proc->epri ) {

		// The priority of the process should change

		*prio = newp;
		return( proc );
	}
	else {

		// No further change

		return( NULL );
	}
}

void	_bx_PmutIns( BX_PMUT *pmut, unsigned prio )
{
BX_PROC	*proc;

	if ( prio > pmut->prio ) {

		// Object priority went up, initiate a priority adjustment cascade

		if ( ( proc = _bx_PmutMut( pmut, pmut->proc, &prio ) ) ) {

			// The owner's priority must be changed. This might lead
			// to a priority cascade.

			_bx_PmutPrio( proc, &prio );
		}
	}
}

/*
*	A process's priority has changed due to a pmut object
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*	This is the function where all process priority changes are done.
*	The loop in the function is responsible for the priority change
*	cascades, when processes are waiting on objects that can mutate
*	the effective priority of their owners.
*/

void	_bx_PmutPrio( BX_PROC *proc, unsigned *prio )
{
BX_PMUT		*wait;
unsigned	minp;

	while ( proc ) {

		// Work out the minimum priority for the process

		minp = 0;
		if ( proc->head ) minp = proc->head->prio;
		if ( minp < proc->rpri ) minp = proc->rpri;
		if ( minp < *prio ) minp = *prio;

		if ( proc->epri == minp ) {

			// The priority is already what it should be

			break;
		}

		if ( ! ( wait = (BX_PMUT *) proc->wait ) ) {

			// The process is not on any waiting list, we're done

			proc->epri = *prio;
			break;
		}

		// The process is waiting for an object. We have to notify
		// the object. In turn, it will change the priority of the
		// process. The function returns the process that owns the
		// waiting object or NULL.

		proc = (*wait->wait.ftab->chg)( &wait->wait, proc, prio );

		if ( ! proc ) {

			// If no process was returned, then the object's
			// own priority did not change, so we're done

			break;
		}

		// A process was returned, meaning that the priority change
		// of the process caused the priority represented by the object
		// also change. This means that the object has a mutator function,
		// we have to call that. If the priority of the process that owns
		// the object has to change, then the process is retruned, if the
		// priority is just fine, then NULL is returned.

		proc = (*wait->wait.ftab->mut)( wait, proc, prio );
	}
}

