Ejemplo n.º 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);
    }
}
Ejemplo n.º 2
0
void osDestroyQueue(OSQueue* q)
{
   if(q == NULL)
      return;

   while(osDequeue(q) != NULL);
      
   free(q);
}
Ejemplo n.º 3
0
void* getAndExecuteTasksForever(void *voidPtrTP) {
    ThreadPool *tp = (ThreadPool *) voidPtrTP;
    while (1) {
        if (pthread_mutex_lock(&(tp->mutex_taskQueue_lock))) {//ERROORRRREE
            return NULL;
        }
        while (osIsQueueEmpty(tp->taskQueue) || tp->tpDestroyNeedsLock==1) {
            if (pthread_cond_wait(&(tp->cond_taskQueueNotEmpty), &(tp->mutex_taskQueue_lock))) {//ERROORRRREE
                return NULL;
            }
        }
        void *FAPAddress = (osDequeue(tp->taskQueue));
        if (pthread_mutex_unlock(&(tp->mutex_taskQueue_lock))) {//ERROORRRREE
            return NULL;
        }
        FuncAndParam FAP = *((FuncAndParam*)FAPAddress);
        free(FAPAddress);
        FAP.function(FAP.param);

    }

}
Ejemplo n.º 4
0
/**
 * The function passed to created threads.
 *
 * NOTE: is_empty(queue) is called a lot: It should be noted that we
 * must make sure it's only called when we have the queue lock!
 *
 * 1. Lock the task queue. We're using a condition lock, so we'll
 *    give up the lock until there is a task to run OR the tpDestroy
 *    function sent a broadcast to all threads that they should clean
 *    up.
 * 2. Wait for the signal (task inserted, or tp being destroyed).
 * 3. Now that the queue is locked, check the destruction state. This
 *    state should be valid because a. the change from ALIVE to
 *    something else is a one-way change, b. even if the following
 *    happened:
 *    - Task added
 *    - Thread got out of the WHILE loop
 *    - CONTEXT SWITCH
 *    - Main thread (pool creator) called tp_destroy, state changed
 *    - CONTEXT SWITCH
 *    - Back to our thread, got to the switch() statement and found
 *      out we're dying
 *    This is the desired behaviour (Piazza @281) - we do not need to
 *    make sure tasks added before calls to tpDestroy will  be executed
 *    if tpDestroy is called in DO_RUN mode, even if all threads were
 *    available when the task was added.
 * 4. If we're ALIVE, that means pool->queue IS NOT EMPTY (otherwise we
 *    would still be in the while loop, because you can't change DO_RUN
 *    or DO_ALL back to ALIVE so there's no danger we left the while()
 *    loop because of state!=ALIVE but got to state==ALIVE in the
 *    switch), so we can just dequeue a task and run it (remember to
 *    unlock before running!).
 * 5. If we're DO_ALL, it's like ALIVE but first check if there's
 *    something to run (unlike the ALIVE state, we don't know for sure).
 *    If there is, run it; otherwise, exit (no more tasks will come).
 * 6. If we're DO_RUN, exit. Don't take another task, leave them to rot.
 * 7. Rinse and repeat
 */
void* thread_func(void* void_tp) {
	
	int pid = TID();
	
	// Some useful variables
	State state;
	Task* t;
	ThreadPool* tp = (ThreadPool*)void_tp;
	
#if HW3_DEBUG
	// Initialize tp->tids
	pthread_t self = pthread_self();
	int thread_i;
	for (thread_i=0; thread_i<tp->N; ++thread_i)
		if (pthread_equal(tp->threads[thread_i],self)) {
			tp->tids[thread_i]=pid;
			break;
		}
#endif
	PRINT("Thread %d started it's function\n",pid);
	
	// Main thread task
	while(1) {
		
		// Get the initial state and the task lock, when we need it (task to do or we're dying)
		// IMPORTANT: LOCK THE TASK LOCK BEFORE READING THE STATE!
		// Otherwise, we can see this situation:
		// - T1 reads the state, it's ALIVE
		// - CS-->main thread
		// - Main thread calls tpDestroy
		// - Main thread broadcasts, starts waiting for all threads
		// - CS-->T1
		// - T1 locks the task lock (remember: state==ALIVE)
		// - The task queue is empty and state==ALIVE so T1 will wait for a signal that will never come.
		// Hence, DO NOT do this:
		// 1. state = read_state(tp);
		// 2. pthread_mutex_lock(&tp->task_lock);
		// But do it the other way round:
		pthread_mutex_lock(&tp->task_lock);										// This is OK because during INIT, we don't lock the task queue (after its creation)
		state = read_state(tp);
		PRINT("Thread %d locked the task queue\n",pid);
		while (osIsQueueEmpty(tp->tasks) && state == ALIVE) {					// Wait for a task OR the destruction of the pool
			PRINT("Thread %d started waiting for a signal\n",pid);
			pthread_cond_wait(&tp->queue_not_empty_or_dying,&tp->task_lock);	// Either one gives a signal
			state = read_state(tp);
			PRINT("Thread %d got the signal and locked the lock\n",pid);
		}
		PRINT("Thread %d got out of the while() loop, state==%s\n",pid,state_string(read_state(tp)));
		switch(state) {
			case ALIVE:											// If we're not dying, take a task and do it.
				t = (Task*)osDequeue(tp->tasks);
				pthread_mutex_unlock(&tp->task_lock);
				PRINT("Thread %d doing it's task\n",pid);
				t->func(t->param);
				free(t);
				break;
			case DO_ALL:										// If we're dying, but we should clean up the queue:
				if (!osIsQueueEmpty(tp->tasks)) {				// THIS TEST IS NOT USELESS! We may have got here
					t = (Task*)osDequeue(tp->tasks);			// via a broadcast() call from tp_destroy and the
					pthread_mutex_unlock(&tp->task_lock);		// state may be DO_ALL but is_empty() may be true...
					PRINT("Thread %d doing it's task\n",pid);	// Thus, the while() loop terminated and we got here.
					t->func(t->param);
					free(t);
				}
				else {											// If we're here, there are no more tasks to dequeue!
					pthread_mutex_unlock(&tp->task_lock);		// As we're being destroyed anyway, exit.
					PRINT("Thread %d unlocked the lock and returning\n",pid);
					return NULL;
				}
				break;
			case DO_RUN:										// If we're dying and no more tasks should be done,
				pthread_mutex_unlock(&tp->task_lock);			// just exit before dequeuing anything...
				PRINT("Thread %d unlocked the lock and returning\n",pid);
				return NULL;
				break;
		}
	}
}
Ejemplo n.º 5
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;
}