/* monitor.cpp
   Copyright (C) 2005, 2006 Free Software Foundation, Inc.

This file is part of Mysaifu JVM

Mysaifu JVM is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.

Mysaifu JVM is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with GNU Classpath; see the file COPYING.  If not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA.
*/

#include "StdAfx.h"
#include "monitor.h"
#include "java_object.h"
#include "java_thread.h"
#include "common_funcs.h"

typedef	DWORD	thread_id;


/* 
* improved monitor
*   ToDo: in general, we would want to enter the Struct before leaving the globalCS
*   Return values not Thread save?

*/



//forward here for use in waiting_thread_info
static void releaseObject(monitor* pmonitor) ;


struct monitor_t ;		//for access before declaration, not used

/**
 * The information regarding the thread which is waiting
 */
struct waiting_thread_info {

	/**
	 * The object which initiated the wait and which we will return to
	 */
	jobject waiting_objref ;

	/**
	 * The ID of the thread which waits
	 */
	thread_id	waiting_thread_id;

	/**
	 * The lock count number which has been actual when starting to wait
	 */
	unsigned int w_lock_count;

	/**
	 * notify()/notifyAll() event for this thread 
	 */
	HANDLE hNotifyEvent;



	/**
	 * thread wait function
	 *
	 */
	inline int doWait(monitor_t* pmonitor, unsigned int timeout) {

		releaseObject(pmonitor) ; //Release with a function because I dont know how to acess monitor_t here

		int result = MONITOR_WAIT_NOT_A_OWNER;

		// Until the event occurs, you wait
		HANDLE events[2];
		events[0] = this->hNotifyEvent;								// 0th as for notify () /notifyAll () business
		events[1] = get_interrupt_event(this->waiting_thread_id);	// 1st as for interrupt () business
		DWORD dwResult = WaitForMultipleObjects(
						sizeof(events) / sizeof(HANDLE),	// The number of events which wait
						events,								// Arrangement of the event which waits
						FALSE,								// returns when the state of any one of the objects set to is signaled.
						timeout);							// Timeout value
		
		switch (dwResult) {
			case WAIT_OBJECT_0: // notify()/notifyAll()
				result = MONITOR_WAIT_NOTIFIED;
				break;

			case WAIT_TIMEOUT: 	// Timeout
				result = MONITOR_WAIT_TIMEOUT;
				break;

			case WAIT_OBJECT_0 + 1: // Thread.interrupt()
				result = MONITOR_WAIT_INTERRUPTED;
				break;		
			default:
				assert(false);
		}

		// Do final processing in the monitor
		EnterCriticalSection(&g_global_critical_section);

		return result ;

	}

};


/**
 * Object monitor structure
 */
typedef struct monitor_t {
	/**
	 * The thread which acquires the lock of object
	 */
	thread_id	owner_thread;

	/**
	 * Frequency of monitor lock
	 */
	unsigned int lock_count;

	/**
	 * wait() The number of threads which it has done
	 */
	unsigned int	waiting_threads_count;

	/**
	 * wait() Information of the thread which it has done
	 */
	waiting_thread_info* waiting_threads;

	/**
	 * The critical section to protect accesses to the contents of this structure
	 */
	CRITICAL_SECTION	struct_lock;

	/**
	 * Lock of object
	 */
	CRITICAL_SECTION	object_lock;


	/**
	 * get initial Lock of object, must own g_global_critical_section to enter here
	 */
	inline void getInitialLock(thread_id current_thread_id) {
		LeaveCriticalSection(&g_global_critical_section);
		// Wait to become the owner of the monitor
		EnterCriticalSection(&this->object_lock);
		EnterCriticalSection(&this->struct_lock);
		this->owner_thread = current_thread_id;			
		this->lock_count = 1;
		LeaveCriticalSection(&this->struct_lock);
	}

	/**
	 * get increased Lock of object, must own g_global_critical_section to enter here
	 */
	inline void getIncreasedLock(thread_id current_thread_id) {
		LeaveCriticalSection(&g_global_critical_section);
		EnterCriticalSection(&this->struct_lock);
		this->owner_thread = current_thread_id;		//ToDo: could that be different??	
		this->lock_count++;
		LeaveCriticalSection(&this->struct_lock);
	}

	/**
	 * release Lock of object, must own g_global_critical_section to enter here
	 */
	inline bool decreasedLock(thread_id current_thread_id) {
		LeaveCriticalSection(&g_global_critical_section);
		EnterCriticalSection(&this->struct_lock);

		this->lock_count--;

		if (this->lock_count == 0) {
			// It releases the object lock
			this->owner_thread = NULL;
			LeaveCriticalSection(&this->object_lock);
		}

		LeaveCriticalSection(&this->struct_lock);
		return true ; //We would have to keep the struct_lock to have a thread safe result
	}


	/**
	 * pepare wait, must own g_global_critical_section to enter here
	 *
	 *  returns with MONITOR_WAIT_NOT_A_OWNER if we are not the owner
	 */
	inline int startWait(jobject objref, thread_id current_thread_id, unsigned int timeout, waiting_thread_info* pinfo) {

		int result = MONITOR_WAIT_NOT_A_OWNER;

		LeaveCriticalSection(&g_global_critical_section);
		EnterCriticalSection(&this->struct_lock);

		pinfo->waiting_objref = objref ;
		pinfo->waiting_thread_id = current_thread_id;
		pinfo->w_lock_count = this->lock_count;
		pinfo->hNotifyEvent = CreateEvent(NULL,		// SECURITY_ATTRIBUTE There is no setting
										  FALSE,	// Automatic operation reset
										  FALSE,	// As for initial condition non- signal
										  NULL);	// There is no name
		if (! pinfo->hNotifyEvent) {
			fatal_error(_T("monitor.startWait(): CreateEvent() failed"));
		}

		//go to the Thread structure to do the wait
		result = pinfo->doWait(this, timeout) ;

		
		LeaveCriticalSection(&g_global_critical_section);
		EnterCriticalSection(&(this->object_lock));
		EnterCriticalSection(&this->struct_lock);

		thread_id w_thread_id = GetCurrentThreadId();

		waiting_thread_info* info = NULL;
		for (unsigned int i = 0; i < this->waiting_threads_count; ++i) {
			waiting_thread_info* p = &this->waiting_threads[i];
			if (p != NULL && (p->waiting_thread_id == w_thread_id)) {
				info = p;
				break;
			}
		}

		if (info != NULL) {
		
			this->owner_thread = info->waiting_thread_id;			
			this->lock_count = info->w_lock_count;

			//close notify Handle
			CloseHandle(info->hNotifyEvent) ;
			memset(info, 0, sizeof(waiting_thread_info)) ;
		} else {
			// waiting thread not existing, how could this happen??
			fatal_error(_T("Waiting thread info error."));

		}

		
		LeaveCriticalSection(&this->struct_lock);
		return result ;
	
	}


	/**
	 * notify selected object
	 */
	inline bool objectNotify(thread_id current_thread_id, bool all) {
		EnterCriticalSection(&this->struct_lock);
		LeaveCriticalSection(&g_global_critical_section);

		for (unsigned int i = 0; i < this->waiting_threads_count; ++i) {
			// The event is put in signal state
			// waiting_thread_info* pinfo = pmonitor->waiting_threads[i];
			waiting_thread_info* pinfo = &this->waiting_threads[i];
		
			if (pinfo != NULL) {
				SetEvent(pinfo->hNotifyEvent);
				if (! all) {
					// for notify() just one notifying is done
					break;
				}
			}
		}

		LeaveCriticalSection(&this->struct_lock);
		return true;
	}


} monitor;


static monitor* load_monitor(jobject objref);
static monitor* create_monitor(jobject objref);
static waiting_thread_info* add_waiting_thread(monitor* pmonitor, thread_id current_thread_id);



/**
 * Setting of monitor processing is initialized 
 * before  processing related to monitor, is necessary 1 time to call by all means
 *
 */
void init_monitor_settings() {
	InitializeCriticalSection(&g_global_critical_section);
}



/**
 * The monitor of object is acquired
 *
 * @param	objref	Reference of the object which becomes the acquisition object
 */
bool monitor_enter(jobject objref) {

	EnterCriticalSection(&g_global_critical_section);
	monitor_t* pmonitor = load_monitor(objref);

	// If we are already the owner of the monitor, we just increase the Lock
	thread_id current_thread_id = GetCurrentThreadId();
	if (pmonitor->owner_thread == current_thread_id) {
		// We are the owner of the monitor
		pmonitor->getIncreasedLock(current_thread_id) ;

	} else {
		// We are not the owner of the monitor, but wait to become it
		pmonitor->getInitialLock(current_thread_id) ;
	}
	return true;
}


/**
 * The object monitor is released
 *
 * @param	objref	Reference of the object which becomes the release object 
 * @return	false when the thread which it calls has not acquired the monitor.
 *			true when it can release normally.
 */
bool monitor_exit(jobject objref) {

	EnterCriticalSection(&g_global_critical_section);
	monitor* pmonitor = load_monitor(objref);
	thread_id current_thread_id = GetCurrentThreadId();
			
	// It must be the owner of the monitor to change anything
	if (pmonitor->owner_thread == current_thread_id) {

		return pmonitor->decreasedLock(current_thread_id) ;
	}
	else {
		LeaveCriticalSection(&g_global_critical_section);
		return false ;
	}
}



/**
 * Object.wait() The processing which is suitable is done.
 *
 * @param	objref	Reference to object of monitor acquisition being completed
 * @param	timeout	Timeout value.  When 0 is appointed, timeout it does not do.
 * @return	The reason which returns from this method.
 *			MONITOR_WAIT_NOT_A_OWNER - The thread which calls this method has not acquired the  monitor.
 *			MONITOR_WAIT_NOTIFIED    - notify()/notifyAll() Was called, this thread became executable state
 *			MONITOR_WAIT_TIMEOUT     - Timeout it did
 *			MONITOR_WAIT_INTERRUPTED - It was interrupted by the other thread
 */
int monitor_wait(jobject objref, unsigned int timeout) {

	EnterCriticalSection(&g_global_critical_section);
	
	if (timeout == 0) {
		timeout = INFINITE;
	}
	
	monitor* pmonitor = load_monitor(objref);
	thread_id current_thread_id = GetCurrentThreadId();

	int result = MONITOR_WAIT_NOT_A_OWNER;

	if (pmonitor->owner_thread != current_thread_id) {
		// It is not the owner of the monitor
		LeaveCriticalSection(&g_global_critical_section);
		return result ; //We would have to keep the struct_lock to have a thread safe result
	} else {
		waiting_thread_info* tinfo = add_waiting_thread(pmonitor, current_thread_id) ;
		return pmonitor->startWait(objref, current_thread_id, timeout, tinfo) ;
	}
	
}

/**
 * Object.notify() The processing which is suitable is done.
 *
 * @param	objref	Reference to object of monitor acquisition being completed
 * @param	all		wait() When it notifies to all thread of state, the true is  appointed.
 * @return	When processing completes normally, the true.
 *			When the thread which calls this function has not acquired  the monitor, the false.
 */
bool monitor_notify(jobject objref, bool all) {

	
	EnterCriticalSection(&g_global_critical_section);
	monitor* pmonitor = load_monitor(objref);

	thread_id current_thread_id = GetCurrentThreadId();

	// We can only notify if we are the monitor owner
	if (pmonitor->owner_thread == current_thread_id) {
		return pmonitor->objectNotify(current_thread_id, all) ;
	}
	else {
		LeaveCriticalSection(&g_global_critical_section);
		return false ;
	}

}

/**
 * It returns whether it possesses the monitor of the object where the present thread is appointed
 * This function is not thread safe and probably not so usefull
 */
bool is_monitor_owner(jobject objref) {
	monitor* pmonitor = load_monitor(objref);
	return pmonitor->owner_thread == GetCurrentThreadId();
}



/**
 * The monitor ist released
 */
static void releaseObject(monitor* pmonitor) {
		LeaveCriticalSection(&pmonitor->object_lock);
		LeaveCriticalSection(&pmonitor->struct_lock);
}



/**
 * The monitor which in the object reference which is appointed is returned.  
 *  When the monitor does not exist, the monitor is constructed
 *  Will allways be called from within the g_global_critical_section
 */
static monitor* load_monitor(jobject objref) {
	monitor* pmonitor = get_monitor(objref);
	if (pmonitor == NULL) {
		// The new monitor is drawn up
		pmonitor = create_monitor(objref);
		set_monitor(objref, pmonitor);
	}
	
	return pmonitor;
}


/**
 * The new monitor is coonstructed
 */
monitor* create_monitor(jobject objref) {

	monitor* pmonitor = (monitor*) malloc(sizeof(monitor));
	if (pmonitor == NULL) {
		fatal_error(FATAL_ERROR_NO_MEMORY);
	}
	// The critical section is initialized
	InitializeCriticalSection(&(pmonitor->struct_lock));
	InitializeCriticalSection(&(pmonitor->object_lock));
	
	// Owner thread information is initialized
	pmonitor->owner_thread = NULL;
	pmonitor->lock_count = 0;
	
	// wait Thread information is initialized
	pmonitor->waiting_threads_count = 0;
	pmonitor->waiting_threads = NULL;
	
	return pmonitor;
}

/**
 * The monitor is deleted
 */
void delete_monitor(jobject objref) {

	monitor* pmonitor = get_monitor(objref);
	if (pmonitor == NULL) {
		return;
	}

	// The critical section is deleted
	DeleteCriticalSection(&(pmonitor->struct_lock));
	DeleteCriticalSection(&(pmonitor->object_lock));
	
	// wait Thread information is released
	for (unsigned int i = 0; i < pmonitor->waiting_threads_count; ++i) {
		assert(! pmonitor->waiting_threads[i].hNotifyEvent);
	}
	if (pmonitor->waiting_threads != NULL) {
		free(pmonitor->waiting_threads);
	}

	// The structure is released
	free(pmonitor);
}




/**
 * The thread which is appointed is inserted in waiting arrangement
 *  Called in g_global_critical_section
 */
static waiting_thread_info* add_waiting_thread(monitor* pmonitor, thread_id current_thread_id) {
	
	// A empty slot is searched
	unsigned int empty_index = 0xffffffff;
	for (unsigned int i = 0; i < pmonitor->waiting_threads_count; ++i) {
		if (! pmonitor->waiting_threads[i].hNotifyEvent) {
			empty_index = i;
			break;
		}
	}

	if (empty_index == 0xffffffff) {
		// Array is expanded
		pmonitor->waiting_threads_count++;
		size_t size = sizeof(waiting_thread_info) * pmonitor->waiting_threads_count;
		pmonitor->waiting_threads = (waiting_thread_info*) realloc(pmonitor->waiting_threads, size);
		if (! pmonitor->waiting_threads) {
			fatal_error(FATAL_ERROR_NO_MEMORY);
		}
		empty_index = pmonitor->waiting_threads_count - 1;
	}

if (empty_index > 2) {
DBG(_T(" more than one.\n"));
}
	
	// waiting_thread_info It initializes
	waiting_thread_info* pinfo = &pmonitor->waiting_threads[empty_index];
//	pinfo->waiting_thread_id = current_thread_id;
//	pinfo->w_lock_count = pmonitor->lock_count;
//	pinfo->hNotifyEvent = CreateEvent(NULL,		// SECURITY_ATTRIBUTE There is no setting
//									  FALSE,	// Automatic operation reset
//									  FALSE,	// As for initial condition non- signal
//									  NULL);	// There is no name
//	if (! pinfo->hNotifyEvent) {
//		fatal_error(_T("add_waiting_thread(): CreateEvent() failed"));
//	}
	return pinfo;
}

