////////////////////////////////////////////////////////////
//
//     	Implementation of classes
//	AThreadAttributes, AThread, AAsyncStep, AThreadControl
//
//      Copyright 2001 
//
////////////////////////////////////////////////////////////

#include "AThread.h"
#include "AWait.h"
#include "ASchedule.h"


	// AThreadAttributes

AThreadAttributes::AThreadAttributes()
{ 
	  status = ::pthread_attr_init(&attributes);
	  
	  // as default in HP
	  
	  int z=64*1024; // if(z<PTHREAD_STACK_MIN) z=PTHREAD_STACK_MIN; 
	  
	  if(!status) status=::pthread_attr_setstacksize(&attributes,(size_t)z);
	  
	  if(!status) status=::pthread_attr_setinheritsched(&attributes,PTHREAD_INHERIT_SCHED);
	  
	  if(!status) status=::pthread_attr_setscope(&attributes,PTHREAD_SCOPE_SYSTEM);
}
	  
	  
AThreadAttributes::~AThreadAttributes()
{ 
	  if( !status ) 
	  	::pthread_attr_destroy(&attributes); 
}


int AThreadAttributes::set_priority(int z) 
{ 
	if( status ) return -1;
	
	AScheduleAttributes schedule_attributes;
	
	schedule_attributes.set_priority(z);
	
      	sched_param parameters; 
      	schedule_attributes.get(parameters);
      	
      	if( ::pthread_attr_setinheritsched(&attributes,PTHREAD_EXPLICIT_SCHED) ) return -2;
      	      	
      	return ::pthread_attr_setschedparam(&attributes,&parameters);
}


int AThreadAttributes::set_guard_size(int z) 
{ 
	if( status ) return -1; 
	
	if(z<0) return -2;
			
      	return ::pthread_attr_setguardsize(&attributes,(size_t)z);    	    	
}

int AThreadAttributes::set_stack_size(int z)
{ 
	if( status ) return -1; 
	
	// if(z<PTHREAD_STACK_MIN) return -2;
	
      	return ::pthread_attr_setstacksize(&attributes,(size_t)z);     	
}

int AThreadAttributes::set_detached() 
{ 
	if( status ) return -1;
		
    	return ::pthread_attr_setdetachstate(&attributes,PTHREAD_CREATE_DETACHED); 

}


int AThreadAttributes::set_schedule_policy(int z) 
{ 	
	if( status ) return -1;

 	// see sched.h, rtsched(2)
 	// need to set explicit schedule before setschedpolicy()
	
	if( ::pthread_attr_setinheritsched(&attributes,PTHREAD_EXPLICIT_SCHED) ) return -2;
	
      	return ::pthread_attr_setschedpolicy(&attributes,z); 

}

         	      	       	    	        	   	
int AThreadAttributes::set_process_scope() 
{ 
	if( status ) return -1;
	
      	return ::pthread_attr_setscope(&attributes,PTHREAD_SCOPE_PROCESS);
}
      	       	       	       	     	  

	  
        // AThread

void AThread::selfdestruct()
{ 
        // this order !
        destruct=true; 
        loop_sleep=0;
        wake_up();
}

int AThread::startup()
{
        int dummy;
        ::pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,&dummy);
        ::pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,&dummy);      
	return 0;
}


        // AAsyncStep

int AAsyncStep::loop(bool& continue_loop)
{
        continue_loop=true; 
        
        int g=respond(); 
        
        if(g) return g; 
        
        AWait(thread_condition,thread_mutex); 
        
        return 0;
}

        // AThreadControl
        

// creates the thread, which begins executing using the AThread object
int AThreadControl::launch( AThread& cAThread, AThreadAttributes& a )
{

	int g=0;
        thread_object=&cAThread;
        
        
        if(! thread_object || a.status)
                return 1;

    	g = pthread_create( &handle, &a.attributes, thread_procedure, thread_object);
    	
    	if(g) return 2;
    	
	return 0; 	
}

// creates the thread, which begins executing using the AThread object
int AThreadControl::launch( AThread& cAThread)
{
        thread_object=&cAThread;
        
        if(! thread_object )
                return -1;
                    
    	return  pthread_create( &handle, 0, thread_procedure, thread_object);
}


int AThreadControl::get_priority(int& priority)
{
	if(! thread_object)  return -1;
	
	int g=0;
	int policy;
	sched_param parameters;
	g=pthread_getschedparam(handle,&policy,&parameters);
	if(g) priority=parameters.sched_priority;
	return g;
}

int AThreadControl::change_priority(int priority)
{
	if(! thread_object)  return -1;
	
	int g=0;
	int policy;
	sched_param parameters;
	g=pthread_getschedparam(handle,&policy,&parameters);
	if(!g)
	{
		parameters.sched_priority=priority;
		g=pthread_setschedparam(handle,policy,&parameters);
	}
	return g;		
}

int AThreadControl::detach()
{
	if(! thread_object)  return -1;
	
	return pthread_detach(handle);		
}


// suspend the thread
int AThreadControl::suspend() 
{
        if(! thread_object)  return -1;
 
    	return ::pthread_suspend(handle);
}

// continue the thread
int AThreadControl::resume() 
{
        if(! thread_object) return -1;

    	return ::pthread_continue( handle);
}


// cancel the thread
int AThreadControl::terminate() 
{
        if(! thread_object)  return -1;

    	return ::pthread_cancel( handle);
}


void* AThreadControl::thread_procedure(void *pAThread) 
{

        int hr1=0,hr2=0,hr3=0,hr4=0;
        bool loop=true;
    	//AThread *p_thread = static_cast<AThread *>(pAThread);
    	AThread *p_thread = (AThread *)(pAThread);
 
        if(p_thread->destruct)
        {
                return (void*)destructed;
        }
               
        p_thread->thread_mutex.lock(); 
        
        
        hr1=p_thread->startup(); 
    

    	if(! hr1)
                while(loop)
                {
                	pthread_testcancel();

                        if(p_thread->destruct)
                        {
                                hr2=1;
                                break;
                        }

                        hr3=p_thread->loop(loop);

                        if(hr3) break;

                        if(p_thread->destruct)
                        {
                                hr2=1;
                                break;
                        }

                        if(p_thread->loop_sleep>0)
                                AWait(p_thread->thread_condition,p_thread->thread_mutex,p_thread->loop_sleep);
                                //sleep(p_thread->loop_sleep);
                                                                
                }

        hr4=p_thread->cleanup();
        
        p_thread->thread_mutex.unlock();


        unsigned hr=0;

        if(hr1) hr=failed_startup;
        else if(hr2) hr=destructed;
        else if(hr3) hr=bad_loop;
        else if(hr4) hr=failed_cleanup;
         
        return (void*)hr;
}