sl@0: // Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies).
sl@0: // All rights reserved.
sl@0: // This component and the accompanying materials are made available
sl@0: // under the terms of "Eclipse Public License v1.0"
sl@0: // which accompanies this distribution, and is available
sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html".
sl@0: //
sl@0: // Initial Contributors:
sl@0: // Nokia Corporation - initial contribution.
sl@0: //
sl@0: // Contributors:
sl@0: //
sl@0: // Description:
sl@0: // Name     : pthreadmisc.cpp
sl@0: // Part of  : PThread library
sl@0: // Thread Miscellaneous functions are implemented in this file.
sl@0: // Version:
sl@0: //
sl@0: 
sl@0: 
sl@0: 
sl@0: #include <pthread.h>
sl@0: #include <stdio.h>
sl@0: #include <errno.h>
sl@0: #include <e32base.h>
sl@0: #include "threadglobals.h"
sl@0: 
sl@0: #define THREAD_NAME_WIDTH 8
sl@0: #define THREAD_COUNT_ZERO 0
sl@0: #define MAX_THREAD_CREATE_FAILURE_COUNT 20
sl@0: #define MAX_THREAD_NAME_LEN 255
sl@0: 
sl@0: #ifdef __EPOC32__
sl@0: class XPthreadTLSCleanup
sl@0: 	{
sl@0: public:
sl@0: 	~XPthreadTLSCleanup()
sl@0: 		{
sl@0: 		Dll::FreeTls();
sl@0: 		}
sl@0: 	};
sl@0: 
sl@0: // Create a global here. It's desctructor invoked on libpthread unload will reset libpthread's TLS
sl@0: XPthreadTLSCleanup gPthreadTLSCleanup;
sl@0: #endif
sl@0: 
sl@0: int _copy_already_defined_keys(_pthread_node_t* prev_node, _pthread_node_t* new_node);
sl@0: 
sl@0: // Internal Function
sl@0: // return : NULL - means no memory
sl@0: void* _pthread_getTls()
sl@0: {
sl@0:     _pthread_node_t *selfNodePtr;
sl@0:     _global_data_t *glbPtr = glbHeadNode;
sl@0:     
sl@0:     THR_PRINTF("[pthread] Begin _pthread_getTls\n");
sl@0:     // Get the TLS contents. 
sl@0:     TAny *tlsAddr = Dll::Tls();
sl@0:     
sl@0:     // If TLS is already initialised then just return the node address
sl@0:     if (NULL != tlsAddr)
sl@0:     {
sl@0:         THR_PRINTF("[pthread] End _pthread_getTls\n");
sl@0:         return ((void*) tlsAddr);
sl@0:     }
sl@0:     
sl@0:     // Create node for main thread and also global data structure.
sl@0:     selfNodePtr = new _pthread_node_t;
sl@0:     THR_NULL_ASSERT(selfNodePtr,NULL,
sl@0:           "[pthread] FATAL: Memory alloc failed for threadnode");
sl@0:     
sl@0:     //Initialize the node
sl@0:     selfNodePtr->next = NULL;
sl@0:     if (selfNodePtr->lockNode.CreateLocal() != KErrNone)
sl@0:     {
sl@0:         delete selfNodePtr;
sl@0:         THR_PRINTF("[pthread] FATAL :Mutex create failed");
sl@0:         return ((void*)NULL);        
sl@0:     }
sl@0:     selfNodePtr->glbDataPtr = glbPtr;
sl@0:     selfNodePtr->detachState = PTHREAD_CREATE_JOINABLE;
sl@0:     selfNodePtr->threadState = _THREAD_RUNNING;
sl@0:     selfNodePtr->returnValue = NULL;
sl@0:     selfNodePtr->hasAnyThreadJoined = EFalse;
sl@0:     selfNodePtr->threadId = (TUint)selfNodePtr->rtHandle.Id();
sl@0:     selfNodePtr->priority = DEFAULT_THREAD_PRIORITY; 
sl@0:     selfNodePtr->mainFlag = _MAIN_THREAD;
sl@0:     selfNodePtr->tlsHead = NULL;
sl@0:     selfNodePtr->cleanStackPtr = NULL; //added to solve memory leak problem
sl@0: 
sl@0: //    Insert the node in to the global linked list and increment thread count.
sl@0:     glbPtr->lockThreadTable.Wait();
sl@0: 	//copy the already defined keys
sl@0: 	if( _copy_already_defined_keys(glbPtr->start, selfNodePtr))
sl@0: 	{
sl@0: 		delete selfNodePtr;
sl@0:         THR_PRINTF("[pthread] FATAL :failed copying defined keys");
sl@0: 		
sl@0: 		glbPtr->lockThreadTable.Signal();
sl@0:         return NULL;
sl@0: 	}
sl@0:     selfNodePtr->next = glbPtr->start;
sl@0:     
sl@0: 	glbPtr->start = selfNodePtr;
sl@0:     glbPtr->threadCount++;
sl@0: 	
sl@0:     glbPtr->lockThreadTable.Signal();
sl@0:         
sl@0:     //Set node address to TLS
sl@0:     Dll::SetTls((TAny*)selfNodePtr);
sl@0:     THR_PRINTF("[pthread] End _pthread_getTls\n");
sl@0:     
sl@0:     // libc needs to know whether application is multithreaded or not...
sl@0:     __isthreaded = 1;
sl@0:     
sl@0:     return ((void*) selfNodePtr);
sl@0: }
sl@0: 
sl@0: int _copy_already_defined_keys(_pthread_node_t* prev_node, _pthread_node_t* new_node)
sl@0: {
sl@0: 	if(!prev_node || !new_node)
sl@0: 		return 0;
sl@0: 
sl@0: 	_pkey_node_t *tlsPtr;
sl@0: 	_pkey_node_t *newTlsPtr, *prevTlsPtr=NULL;
sl@0:     for (tlsPtr = prev_node->tlsHead; tlsPtr != NULL; tlsPtr = tlsPtr->next)
sl@0: 	{
sl@0: 		newTlsPtr = new _pkey_node_t;
sl@0: 		if (NULL == newTlsPtr)
sl@0:         {
sl@0:             return -1;
sl@0:         }
sl@0: 		if(new_node->tlsHead == NULL)
sl@0: 			new_node->tlsHead = newTlsPtr;
sl@0: 		else{
sl@0: 			prevTlsPtr->next = newTlsPtr;
sl@0: 		}
sl@0:         newTlsPtr->tls = NULL;
sl@0:         newTlsPtr->keyNumber = tlsPtr->keyNumber;
sl@0: 		prevTlsPtr = newTlsPtr;
sl@0: 	}
sl@0: 	 
sl@0: 	return 0;
sl@0: }
sl@0: 
sl@0: // This function has to be called holding global lock and node lock.
sl@0: // Before returning all lock will be opened
sl@0: void _pthread_deleteNode(_pthread_node_t *selfNodePtr, 
sl@0:                            _global_data_t *glbPtr,
sl@0:                            void ** retValPtr)
sl@0: {
sl@0:     _pthread_node_t *tempPtr;
sl@0:     _pthread_node_t *prevPtr = NULL;
sl@0:     
sl@0:     THR_PRINTF("[pthread] Begin _pthread_deleteNode\n");
sl@0:     // Traverse through the list, till node which has to be deleted is found
sl@0:     for (tempPtr = glbPtr->start; 
sl@0:          ((tempPtr != selfNodePtr) && (tempPtr != NULL)); 
sl@0:          tempPtr = tempPtr->next)
sl@0:     {
sl@0:         prevPtr = tempPtr;             // Store the previous node ptr
sl@0:     }
sl@0:     
sl@0:     if (NULL == tempPtr)              //Not found; this should never happen
sl@0:     {
sl@0:         THR_PRINTF("[pthread] FATAL: Unable to delete the node");
sl@0:         selfNodePtr->lockNode.Signal();
sl@0:         glbPtr->lockThreadTable.Signal();  //release global lock
sl@0:         return;
sl@0:     }
sl@0:     if (tempPtr == glbPtr->start)       // Deleting first node
sl@0:     {
sl@0:         glbPtr->start = tempPtr->next;  //Update the link list
sl@0:     }
sl@0:     else
sl@0:     {
sl@0:         prevPtr->lockNode.Wait();       //Acquire the previous node lock
sl@0:         prevPtr->next = tempPtr->next;  //Update the link list
sl@0:         prevPtr->lockNode.Signal();     //Release the previous node lock
sl@0:     }
sl@0:     
sl@0:     if (NULL != retValPtr)               // Get the return value
sl@0:     {
sl@0:         *retValPtr = selfNodePtr->returnValue;
sl@0:     }
sl@0: 
sl@0:     selfNodePtr->lockNode.Signal();     // Unlock & Free the node 
sl@0:     selfNodePtr->lockNode.Close();
sl@0:     selfNodePtr->rtHandle.Close();
sl@0:     
sl@0:     delete selfNodePtr;
sl@0: 
sl@0:     //--(glbPtr->threadCount);           // Update the thread count
sl@0:     //if (THREAD_COUNT_ZERO == glbPtr->threadCount)
sl@0:     //{
sl@0:     //    RProcess rp;
sl@0:     //    rp.Kill(0);                    // Terminate the process
sl@0:     //}
sl@0:     
sl@0:     glbPtr->lockThreadTable.Signal();  // release thread table lock
sl@0:     THR_PRINTF("[pthread] End of _pthread_deleteNode\n");
sl@0: }
sl@0: 
sl@0: 
sl@0: void* _getKeyValueSetNull(int keyNumber,_pkey_node_t *tlsPtr)
sl@0: {
sl@0:     void* retVal;
sl@0:     for ( ; tlsPtr != NULL; tlsPtr = tlsPtr->next)
sl@0:     {
sl@0:         if (tlsPtr->keyNumber == keyNumber)
sl@0:         {
sl@0:                 retVal = tlsPtr->tls;
sl@0:                 tlsPtr->tls = NULL;
sl@0:                 return (retVal);
sl@0:         }
sl@0:     }
sl@0:     //This should never happen; hence dest function will not be called
sl@0:     return NULL;
sl@0: }
sl@0: // Before calling this function, global lock and node lock must be open
sl@0: void _pthread_destroyKeys(_global_data_t *glbPtr,
sl@0:                             _pthread_node_t *selfNodePtr)
sl@0: {
sl@0:     int loopvar;
sl@0:     int allempty;
sl@0:     int keyNumber;
sl@0:     destructor_routine dest;
sl@0:     void *arg;
sl@0:     
sl@0:     glbPtr->lockThreadTable.Wait();    // Acquire the thread table lock
sl@0:     selfNodePtr->lockNode.Wait();      // Lock the TCB
sl@0: 
sl@0:     for (loopvar = 0, allempty = 1;
sl@0:         allempty && (loopvar < PTHREAD_DESTRUCTOR_ITERATIONS);loopvar++) 
sl@0:     {
sl@0:         allempty = 0; // Assuming all keys are not used.
sl@0:         
sl@0:         for (keyNumber = 0; keyNumber < PTHREAD_KEYS_MAX; keyNumber++)
sl@0:         {
sl@0:             int temp;
sl@0:             temp = keyNumber / 32;
sl@0:             
sl@0:             int bitPos;
sl@0:             bitPos = keyNumber % 32;
sl@0:         
sl@0:             if (((glbPtr->statusflag[temp]) & (0x1<<bitPos)) &&      //Key used
sl@0:                 (glbPtr->pthread_key_list[keyNumber].destr != NULL))
sl@0:             {
sl@0:                 if ((arg = _getKeyValueSetNull(keyNumber,selfNodePtr->tlsHead)) != NULL)
sl@0:                 {
sl@0:                     dest = glbPtr->pthread_key_list[keyNumber].destr;
sl@0:                 
sl@0:                     selfNodePtr->lockNode.Signal();    // Unlock TCB node
sl@0:                     glbPtr->lockThreadTable.Signal();  // release thread table lock
sl@0: 
sl@0:                     dest(arg);  //Call destructor
sl@0: 
sl@0:                     glbPtr->lockThreadTable.Wait();// Acquire the thread table lock
sl@0:                     selfNodePtr->lockNode.Wait();      // Lock the TCB
sl@0:                     
sl@0:                     allempty = 1;   // Atleast one key was used
sl@0:                 }
sl@0:             }
sl@0:         }
sl@0:     }
sl@0:     
sl@0:     selfNodePtr->lockNode.Signal();    // Unlock TCB node
sl@0:     glbPtr->lockThreadTable.Signal();  // release thread table lock
sl@0: }
sl@0: 
sl@0: // End of File