int request_enqueue(REQUEST *request) { dispatch_block_t block; block = ^{ radius_handle_request(request, fun); }; dispatch_async(thread_pool.queue, block); return 1; }
/* * Assign a new request to a free thread. * * If there isn't a free thread, then try to create a new one, * up to the configured limits. */ int thread_pool_addrequest(REQUEST *request, RAD_REQUEST_FUNP fun) { time_t now = request->timestamp; request->process = fun; /* * We've been told not to spawn threads, so don't. */ if (!thread_pool.spawn_flag) { radius_handle_request(request, fun); #ifdef WNOHANG /* * Requests that care about child process exit * codes have already either called * rad_waitpid(), or they've given up. */ wait(NULL); #endif return 1; } /* * Add the new request to the queue. */ if (!request_enqueue(request, fun)) return 0; /* * If we haven't checked the number of child threads * in a while, OR if the thread pool appears to be full, * go manage it. */ if ((last_cleaned < now) || (thread_pool.active_threads == thread_pool.total_threads)) { thread_pool_manage(now); } return 1; }
/* * The main thread handler for requests. * * Wait on the semaphore until we have it, and process the request. */ static void *request_handler_thread(void *arg) { RAD_REQUEST_FUNP fun; THREAD_HANDLE *self = (THREAD_HANDLE *) arg; /* * Loop forever, until told to exit. */ do { /* * Wait to be signalled. */ DEBUG2("Thread %d waiting to be assigned a request", self->thread_id); re_wait: if (sem_wait(&thread_pool.semaphore) != 0) { /* * Interrupted system call. Go back to * waiting, but DON'T print out any more * text. */ if (errno == EINTR) { DEBUG2("Re-wait %d", self->thread_id); goto re_wait; } radlog(L_ERR, "Thread %d failed waiting for semaphore: %s: Exiting\n", self->thread_id, strerror(errno)); break; } DEBUG2("Thread %d got semaphore", self->thread_id); #ifdef HAVE_OPENSSL_ERR_H /* * Clear the error queue for the current thread. */ ERR_clear_error (); #endif /* * The server is exiting. Don't dequeue any * requests. */ if (thread_pool.stop_flag) break; /* * Try to grab a request from the queue. * * It may be empty, in which case we fail * gracefully. */ if (!request_dequeue(&self->request, &fun)) continue; self->request->thread_id = self->thread_id; self->request_count++; DEBUG2("Thread %d handling request %d, (%d handled so far)", self->thread_id, self->request->number, self->request_count); self->request->module = ""; radius_handle_request(self->request, fun); self->request = NULL; /* * Update the active threads. */ pthread_mutex_lock(&thread_pool.queue_mutex); rad_assert(thread_pool.active_threads > 0); thread_pool.active_threads--; pthread_mutex_unlock(&thread_pool.queue_mutex); /* * If the thread has handled too many requests, then make it * exit. */ if ((thread_pool.max_requests_per_thread > 0) && (self->request_count >= thread_pool.max_requests_per_thread)) { DEBUG2("Thread %d handled too many requests", self->thread_id); break; } } while (self->status != THREAD_CANCELLED); DEBUG2("Thread %d exiting...", self->thread_id); #ifdef HAVE_OPENSSL_ERR_H /* * If we linked with OpenSSL, the application * must remove the thread's error queue before * exiting to prevent memory leaks. */ ERR_remove_state(0); #endif pthread_mutex_lock(&thread_pool.queue_mutex); thread_pool.exited_threads++; pthread_mutex_unlock(&thread_pool.queue_mutex); /* * Do this as the LAST thing before exiting. */ self->request = NULL; self->status = THREAD_EXITED; return NULL; }