Example #1
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 #2
0
/**
 * Destroys the thread pool.
 *
 * 1. Lock the destruction lock (top priority... don't want to starve here)
 * 2. Change the state
 * 3. Unlock the destruction lock (now, no more tasks can be added because the state
 *	  is checked first, and the threads should know to check the state before dequeuing
 *    anything)
 * 4. Lock the tasks lock, so we're sure all threads are waiting for a signal (or for
 *    the lock) to prevent deadlock (see comments bellow).
 * 5. Broadcast to all threads waiting for tasks: if there's nothing now, there never
 *    will be!
 * 6. Unlock the tasks lock (to allow the threads to do their thing)
 * 7. Wait for all threads to finish
 * 8. Destroy all fields of the pool
 */
void tpDestroy(ThreadPool* tp, int should_wait_for_tasks) {
	
	if (!tp)												// Sanity check
		return;
	
	if (!start_write(tp)) {									// Someone is already writing to pool->state!
		return;												// This should only happen ONCE...
	}
	if (tp->state != ALIVE) {								// Destruction already in progress.
		end_write(tp);										// This can happen if destroy() is called twice fast
		return;
	}
	tp->state = should_wait_for_tasks ? DO_ALL : DO_RUN;	// Enter destroy mode
	PRINT("Destroying the pool! Now allowing state read.\n");
	end_write(tp);											// Allow reading the state

	// Make sure the queue isn't busy.
	// We shouldn't encounter deadlock/livelock here because threads wait for signals, and the only
	// possible source of a signal is here or in tpInsertTask().
	// If we lock this only AFTER we change the/ destruction state, we can create a situation where
	// a thread waits forever for a signal that will never come:
	// - A thread is in it's while loop, the queue is
	//   empty and the pool isn't being destroyed
	//   so it enters the body of the loop. Before it
	//   starts waiting for a signal...
	// - CS-->Main thread
	// - Main thread calls destroy, doesn't wait for
	//   the task_lock and writes the new destruction
	//   state. Then, broadcasts a signal to listening
	//   threads.
	// - CS-->Previous thread
	// - Starts waiting for a signal that will never
	//   come
	// By locking here before broadcasting the destruction, we make sure all threads are waiting for
	// the lock or for the signal! Either way, after the signal, the threads will know what to do and
	// exit the loop.
	pthread_mutex_lock(&tp->task_lock);
	PRINT("Pool destroyed, task lock locked, sending broadcast...\n");
	pthread_cond_broadcast(&tp->queue_not_empty_or_dying);
	PRINT("... done. Unlocking the task lock.\n");
	pthread_mutex_unlock(&tp->task_lock);
	
	// Wait for all threads to exit (this could take a while)
	int i;
	for (i=0; i<tp->N; ++i) {
#if HW3_DEBUG
		PRINT("Waiting for T%2d to finish (tid=%d)...\n", i+1, tp->tids[i]);
#else
		PRINT("Waiting for thread %d to finish...\n", i+1);
#endif
		pthread_join(tp->threads[i], NULL);
#if HW3_DEBUG
		PRINT("T%2d (tid=%d) done.\n", i+1, tp->tids[i]);
#else
		PRINT("Thread %d done.\n", i+1);
#endif
	}
	PRINT("All threads done! Locking the task lock and destroying the task queue...\n");
	
	// Cleanup!
	// Tasks (we can still lock here):
	pthread_mutex_lock(&tp->task_lock);
	while (!osIsQueueEmpty(tp->tasks)) {
		Task* t = (Task*)osDequeue(tp->tasks);
		free(t);
	}
	osDestroyQueue(tp->tasks);
	PRINT("Done. Unlocking the task queue\n");
	pthread_mutex_unlock(&tp->task_lock);
	
	// Locks:
	PRINT("Doing thread pool cleanup...\n");
	pthread_mutex_destroy(&tp->task_lock);
	pthread_mutexattr_destroy(&tp->mutex_type);
	sem_destroy(&tp->r_num_mutex);
	sem_destroy(&tp->w_flag_mutex);
	sem_destroy(&tp->r_entry);
	sem_destroy(&tp->r_try);
	sem_destroy(&tp->state_lock);
	pthread_cond_destroy(&tp->queue_not_empty_or_dying);
	
	// Last cleanup, and out:
#if HW3_DEBUG
	free(tp->tids);
#endif
	free(tp->threads);
	free(tp);
	PRINT("Done destroying thread pool.\n");
	return;
}