/* * function handle_requests_loop(): infinite loop of requests handling * algorithm: forever, if there are requests to handle, take the first * and handle it. Then wait on the given condition variable, * and when it is signaled, re-do the loop. * increases number of pending requests by one. * input: id of thread, for printing purposes. * output: none. */ void* handle_requests_loop(void* thread_params) { int rc; /* return code of pthreads functions. */ struct request* a_request; /* pointer to a request. */ struct handler_thread_params *data; /* hadler thread's parameters */ /* sanity check -make sure data isn't NULL */ data = (struct handler_thread_params*)thread_params; assert(data); printf("Starting thread '%d'\n", data->thread_id); fflush(stdout); /* set my cancel state to 'enabled', and cancel type to 'defered'. */ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); /* set thread cleanup handler */ pthread_cleanup_push(cleanup_free_mutex, (void*)data->request_mutex); /* lock the mutex, to access the requests list exclusively. */ rc = pthread_mutex_lock(data->request_mutex); #ifdef DEBUG printf("thread '%d' after pthread_mutex_lock\n", data->thread_id); fflush(stdout); #endif /* DEBUG */ /* do forever.... */ while (1) { int num_requests = get_requests_number(data->requests); #ifdef DEBUG printf("thread '%d', num_requests = %d\n", data->thread_id, num_requests); fflush(stdout); #endif /* DEBUG */ if (num_requests > 0) { /* a request is pending */ a_request = get_request(data->requests); if (a_request) { /* got a request - handle it and free it */ /* unlock mutex - so other threads would be able to handle */ /* other reqeusts waiting in the queue paralelly. */ rc = pthread_mutex_unlock(data->request_mutex); handle_request(a_request, data->thread_id); free(a_request); /* and lock the mutex again. */ rc = pthread_mutex_lock(data->request_mutex); } } else { /* the thread checks the flag before waiting */ /* on the condition variable. */ /* if no new requests are going to be generated, exit. */ if (done_creating_requests) { pthread_mutex_unlock(data->request_mutex); printf("thread '%d' exiting\n", data->thread_id); fflush(stdout); pthread_exit(NULL); } /* wait for a request to arrive. note the mutex will be */ /* unlocked here, thus allowing other threads access to */ /* requests list. */ #ifdef DEBUG printf("thread '%d' before pthread_cond_wait\n", data->thread_id); fflush(stdout); #endif /* DEBUG */ rc = pthread_cond_wait(data->got_request, data->request_mutex); /* and after we return from pthread_cond_wait, the mutex */ /* is locked again, so we don't need to lock it ourselves */ #ifdef DEBUG printf("thread '%d' after pthread_cond_wait\n", data->thread_id); fflush(stdout); #endif /* DEBUG */ } } /* remove thread cleanup handler. never reached, but we must use */ /* it here, according to pthread_cleanup_push's manual page. */ pthread_cleanup_pop(0); }
/* like any C program, program's execution begins in main */ int main(int argc, char* argv[]) { int i; /* loop counter */ struct timespec delay; /* used for wasting time */ struct requests_queue* requests = NULL; /* pointer to requests queue */ struct handler_threads_pool* handler_threads = NULL; /* list of handler threads */ /* create the requests queue */ requests = init_requests_queue(&request_mutex, &got_request); assert(requests); /* create the handler threads list */ handler_threads = init_handler_threads_pool(&request_mutex, &got_request, requests); assert(handler_threads); /* create the request-handling threads */ for (i=0; i<NUM_HANDLER_THREADS; i++) { add_handler_thread(handler_threads); } /* run a loop that generates requests */ for (i=0; i<600; i++) { int num_requests; // number of requests waiting to be handled. int num_threads; // number of active handler threads. add_request(requests, i); num_requests = get_requests_number(requests); num_threads = get_handler_threads_number(handler_threads); /* if there are too many requests on the queue, spawn new threads */ /* if there are few requests and too many handler threads, cancel */ /* a handler thread. */ if (num_requests > HIGH_REQUESTS_WATERMARK && num_threads < MAX_NUM_HANDLER_THREADS) { printf("main: adding thread: '%d' requests, '%d' threads\n", num_requests, num_threads); add_handler_thread(handler_threads); } if (num_requests < LOW_REQUESTS_WATERMARK && num_threads > NUM_HANDLER_THREADS) { printf("main: deleting thread: '%d' requests, '%d' threads\n", num_requests, num_threads); delete_handler_thread(handler_threads); } /* pause execution for a little bit, to allow */ /* other threads to run and handle some requests. */ if (rand() > 3*(RAND_MAX/4)) { /* this is done about 25% of the time */ delay.tv_sec = 0; delay.tv_nsec = 1; nanosleep(&delay, NULL); } } /* modify the flag to tell the handler threads no */ /* new requests will be generated. */ { int rc; rc = pthread_mutex_lock(&request_mutex); done_creating_requests = 1; rc = pthread_cond_broadcast(&got_request); rc = pthread_mutex_unlock(&request_mutex); } /* cleanup */ delete_handler_threads_pool(handler_threads); delete_requests_queue(requests); printf("Glory, we are done.\n"); return 0; }