Example #1
0
int tpInsertTask(ThreadPool *tp, void (*computeFunc)(void *), void *param) {
    int tpDestroyWasInvoked;
    if (sem_getvalue(&(tp->sem_tpDestroyWasInvoked), &tpDestroyWasInvoked)) {//ERROORRRREE
        return -1;
    }
    if (tpDestroyWasInvoked == 0) {
        return -1;//means that tpDestroy was invoked
    }
    if (pthread_mutex_lock(&(tp->mutex_taskQueue_lock))) {//ERROORRRREE
        return -1;
    }
    if (sem_getvalue(&(tp->sem_tpDestroyWasInvoked), &tpDestroyWasInvoked)) {//ERROORRRREE
        return -1;
    }
    if (tpDestroyWasInvoked == 0) {
        return -1;//means that tpDestroy was invoked
    }


    FuncAndParam *fap = malloc(sizeof(*fap));
    if(!fap){
        return -1;
    }
    fap->function = computeFunc;
    fap->param = param;
    osEnqueue(tp->taskQueue, (void *) fap);
    if (pthread_cond_signal(&(tp->cond_taskQueueNotEmpty))) {//ERROORRRREE
        return -1;
    }
    if (pthread_mutex_unlock(&(tp->mutex_taskQueue_lock))) {//ERROORRRREE
        return -1;
    }
    return 0;
}
Example #2
0
void tpDestroy(ThreadPool *tp, int shouldWaitForTasks) {
    if(sem_trywait(&(tp->sem_tpDestroyWasInvoked))){//we are already during destruction
        return;
    }
    tp->tpDestroyNeedsLock = 1;
    if (pthread_mutex_lock(&(tp->mutex_taskQueue_lock))) {//ERROORRRREE
        return;//TODO think about this
    }
    if(shouldWaitForTasks==0){
        while(!(osIsQueueEmpty(tp->taskQueue))){
            free(osDequeue(tp->taskQueue));
        }
    }
    int i;
    for(i = 0 ; i < tp->numOfThreads ; ++i){
        FuncAndParam* fap = malloc(sizeof(*fap));
        fap->function = selfDestruct;
        fap->param = NULL;
        osEnqueue(tp->taskQueue, (void*)fap);
    }
    tp->tpDestroyNeedsLock = 0;
    if (pthread_cond_broadcast(&(tp->cond_taskQueueNotEmpty))) {//ERROORRRREE
        return;
    }
    if (pthread_mutex_unlock(&(tp->mutex_taskQueue_lock))) {//ERROORRRREE
        return;
    }
    int elDestroyador = getpid();//id of the process that invoked tpDestroy
    int flagCurrentIsThread = 0;//true iff the process that invoked tpDestroy is one of the threads
    for(i = 0 ; i < tp->numOfThreads ; ++i){
        if((tp->threadIdArray)[i]==elDestroyador){
            flagCurrentIsThread = 1;
        }else{
            pthread_join((tp->threadIdArray)[i], NULL);
        }
    }
    while(!(osIsQueueEmpty(tp->taskQueue))){
        free(osDequeue(tp->taskQueue));
    }
    osDestroyQueue(tp->taskQueue);
    if (pthread_cond_destroy(&(tp->cond_taskQueueNotEmpty))) {//ERROORRRREE
        return;
    }
    if (pthread_mutex_destroy(&(tp->mutex_taskQueue_lock))) {//ERROORRRREE
        return;
    }
    if (sem_destroy(&(tp->sem_tpDestroyWasInvoked))) {//ERROORRRREE
        return;
    }
    free(tp->threadIdArray);
    free(tp);
    if(flagCurrentIsThread){//if the process that invoked tpDestroy is one of the threads
        pthread_exit(NULL);
    }
}
Example #3
0
/**
 * Add task.
 *
 * Basically:
 * 1. Make sure we're not being destroyed, and prevent destruction until we're done (dest_lock)
 * 2. Lock the task queue (unconditionally - no upper limit on number of tasks)
 * 3. Add a task
 * 4. Release the task queue lock and signal that the queue isn't empty (which one first?)
 * 5. Unlock the destruction lock (now we can kill the thread pool)
 */
int tpInsertTask(ThreadPool* tp, void (*func)(void *), void* param) {
	
	start_read(tp);				// Read the state, and generally prevent it's change
	if (tp->state != ALIVE) {	// If the thread pool is dying,
		end_read(tp);			// release the state for reading (no more writing will be done)
		return -1;				// and return indication of destruction.
	}
	
	// Try creating the task now, before locking the queue but after reading, because this
	// takes a long time so we don't want to prevent tasks from running. We're currently still
	// "reading" the thread pool state, but that's OK because a. it's a CREW lock so the threads
	// can read and see that the thread pool is a live and b. if a writer wants to write to the
	// state it's because the writer wants to destroy the thread pool - we want to prevent that
	// anyway at this point.
	Task* t = (Task*)malloc(sizeof(Task));
	if (!t) {
		end_read(tp);
		return -1;
	}
	t->func = func;
	t->param = param;
	
	// Editing the task queue now.
	// There's no danger of deadlock with thread acquiring this lock: If a thread isn't waiting
	// for a signal then all it does is read the state (we can allow that here even if we're
	// waiting for the task_lock) or do it's thing (dequeue or exit). Either way, it'll let go
	// of the lock eventually. We don't need to give the "add" function priority because the
	// worst case scenario is that it'll take some time to enqueue the task... But that's only
	// if the threads are busy, so the task won't get done anyway.
	pthread_mutex_lock(&tp->task_lock);
	PRINT("Adding a task, lock is locked\n");
	osEnqueue(tp->tasks,(void*)t);
	
	// Signal before releasing the lock - make a thread wait for the lock.
	pthread_cond_signal(&tp->queue_not_empty_or_dying);	
	PRINT("Task added, signal given, lock is locked\n");
	pthread_mutex_unlock(&tp->task_lock);
	PRINT("Task added, signal given, lock is unlocked\n");
	
	// Allow destruction of the thread pool (allow writing to pool->state)
	end_read(tp);
	
	return 0;
	
}