// static void LLThread::yield() { #if LL_LINUX || LL_SOLARIS sched_yield(); // annoyingly, apr_thread_yield is a noop on linux... #else apr_thread_yield(); #endif }
/* Called by a thread to indicate it has completed a block operation and is * thus able to particpate in a GC run again. Note that this case needs some * special handling if it comes out of this mode when a GC run is taking place. */ void MVM_gc_mark_thread_unblocked(MVMThreadContext *tc) { /* Try to set it from unable to running. */ while (apr_atomic_cas32(&tc->gc_status, MVMGCStatus_NONE, MVMGCStatus_UNABLE) != MVMGCStatus_UNABLE) { /* We can't, presumably because a GC run is going on. We should wait * for that to finish before we go on, but without chewing CPU. */ apr_thread_yield(); } }
/* * This function checks the scoreboard, which is updated concurrently in the parent process, * right after the fork. So here we loop waiting when everything is ready. */ static int get_slot(void) { apr_proc_t proc; proc.pid = getpid(); int slot = find_child_by_pid(&proc); while (slot == -1) { apr_thread_yield(); slot = find_child_by_pid(&proc); } return slot; }
static APR_INLINE void busyloop_read32(tbox_t *tbox) { apr_uint32_t val; do { val = apr_atomic_read32(tbox->mem); if (val != tbox->preval) apr_thread_yield(); else break; } while (1); }
static void busyloop_cas32(tbox_t *tbox) { apr_uint32_t val; do { do { val = apr_atomic_cas32(tbox->mem, tbox->postval, tbox->preval); if (val != tbox->preval) apr_thread_yield(); else break; } while (1); } while (--tbox->loop); }
static void sig_term(int sig) { if (shutdown_pending == 1) { /* Um, is this _probably_ not an error, if the user has * tried to do a shutdown twice quickly, so we won't * worry about reporting it. */ return; } shutdown_pending = 1; DBPRINT0 ("waiting for threads\n"); while (wait_to_finish) { apr_thread_yield(); } DBPRINT0 ("goodbye\n"); }
static int make_child(server_rec *s, int slot) { int tid; int err=0; NXContext_t ctx; if (slot + 1 > ap_max_workers_limit) { ap_max_workers_limit = slot + 1; } ap_update_child_status_from_indexes(0, slot, WORKER_STARTING, (request_rec *) NULL); if (ctx = NXContextAlloc((void (*)(void *)) worker_main, (void*)slot, NX_PRIO_MED, ap_thread_stacksize, NX_CTX_NORMAL, &err)) { char threadName[32]; sprintf (threadName, "Apache_Worker %d", slot); NXContextSetName(ctx, threadName); err = NXThreadCreate(ctx, NX_THR_BIND_CONTEXT, &tid); if (err) { NXContextFree (ctx); } } if (err) { /* create thread didn't succeed. Fix the scoreboard or else * it will say SERVER_STARTING forever and ever */ ap_update_child_status_from_indexes(0, slot, WORKER_DEAD, (request_rec *) NULL); /* In case system resources are maxxed out, we don't want Apache running away with the CPU trying to fork over and over and over again. */ apr_thread_yield(); return -1; } ap_scoreboard_image->servers[0][slot].tid = tid; return 0; }
static int netware_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) { apr_status_t status=0; pconf = _pconf; ap_server_conf = s; if (setup_listeners(s)) { ap_log_error(APLOG_MARK, APLOG_ALERT, status, s, APLOGNO(00223) "no listening sockets available, shutting down"); return !OK; } restart_pending = shutdown_pending = 0; worker_thread_count = 0; if (!is_graceful) { if (ap_run_pre_mpm(s->process->pool, SB_NOT_SHARED) != OK) { return !OK; } } /* Only set slot 0 since that is all NetWare will ever have. */ ap_scoreboard_image->parent[0].pid = getpid(); ap_run_child_status(ap_server_conf, ap_scoreboard_image->parent[0].pid, ap_my_generation, 0, MPM_CHILD_STARTED); set_signals(); apr_pool_create(&pmain, pconf); ap_run_child_init(pmain, ap_server_conf); if (ap_threads_max_free < ap_threads_min_free + 1) /* Don't thrash... */ ap_threads_max_free = ap_threads_min_free + 1; request_count = 0; startup_workers(ap_threads_to_start); /* Allow the Apache screen to be closed normally on exit() only if it has not been explicitly forced to close on exit(). (ie. the -E flag was specified at startup) */ if (hold_screen_on_exit > 0) { hold_screen_on_exit = 0; } ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00224) "%s configured -- resuming normal operations", ap_get_server_description()); ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf, APLOGNO(00225) "Server built: %s", ap_get_server_built()); ap_log_command_line(plog, s); ap_log_common(s); show_server_data(); mpm_state = AP_MPMQ_RUNNING; while (!restart_pending && !shutdown_pending) { perform_idle_server_maintenance(pconf); if (show_settings) display_settings(); apr_thread_yield(); apr_sleep(SCOREBOARD_MAINTENANCE_INTERVAL); } mpm_state = AP_MPMQ_STOPPING; ap_run_child_status(ap_server_conf, ap_scoreboard_image->parent[0].pid, ap_my_generation, 0, MPM_CHILD_EXITED); /* Shutdown the listen sockets so that we don't get stuck in a blocking call. shutdown_listeners();*/ if (shutdown_pending) { /* Got an unload from the console */ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00226) "caught SIGTERM, shutting down"); while (worker_thread_count > 0) { printf ("\rShutdown pending. Waiting for %lu thread(s) to terminate...", worker_thread_count); apr_thread_yield(); } mpm_main_cleanup(); return DONE; } else { /* the only other way out is a restart */ /* advance to the next generation */ /* XXX: we really need to make sure this new generation number isn't in * use by any of the children. */ ++ap_my_generation; ap_scoreboard_image->global->running_generation = ap_my_generation; ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00227) "Graceful restart requested, doing restart"); /* Wait for all of the threads to terminate before initiating the restart */ while (worker_thread_count > 0) { printf ("\rRestart pending. Waiting for %lu thread(s) to terminate...", worker_thread_count); apr_thread_yield(); } printf ("\nRestarting...\n"); } mpm_main_cleanup(); return OK; }
/*static */ void worker_main(void *arg) { ap_listen_rec *lr, *first_lr, *last_lr = NULL; apr_pool_t *ptrans; apr_allocator_t *allocator; apr_bucket_alloc_t *bucket_alloc; conn_rec *current_conn; apr_status_t stat = APR_EINIT; ap_sb_handle_t *sbh; apr_thread_t *thd = NULL; apr_os_thread_t osthd; int my_worker_num = (int)arg; apr_socket_t *csd = NULL; int requests_this_child = 0; apr_socket_t *sd = NULL; fd_set main_fds; int sockdes; int srv; struct timeval tv; int wouldblock_retry; osthd = apr_os_thread_current(); apr_os_thread_put(&thd, &osthd, pmain); tv.tv_sec = 1; tv.tv_usec = 0; apr_allocator_create(&allocator); apr_allocator_max_free_set(allocator, ap_max_mem_free); apr_pool_create_ex(&ptrans, pmain, NULL, allocator); apr_allocator_owner_set(allocator, ptrans); apr_pool_tag(ptrans, "transaction"); bucket_alloc = apr_bucket_alloc_create_ex(allocator); atomic_inc (&worker_thread_count); while (!die_now) { /* * (Re)initialize this child to a pre-connection state. */ current_conn = NULL; apr_pool_clear(ptrans); if ((ap_max_requests_per_child > 0 && requests_this_child++ >= ap_max_requests_per_child)) { DBPRINT1 ("\n**Thread slot %d is shutting down", my_worker_num); clean_child_exit(0, my_worker_num, ptrans, bucket_alloc); } ap_update_child_status_from_indexes(0, my_worker_num, WORKER_READY, (request_rec *) NULL); /* * Wait for an acceptable connection to arrive. */ for (;;) { if (shutdown_pending || restart_pending || (ap_scoreboard_image->servers[0][my_worker_num].status == WORKER_IDLE_KILL)) { DBPRINT1 ("\nThread slot %d is shutting down\n", my_worker_num); clean_child_exit(0, my_worker_num, ptrans, bucket_alloc); } /* Check the listen queue on all sockets for requests */ memcpy(&main_fds, &listenfds, sizeof(fd_set)); srv = select(listenmaxfd + 1, &main_fds, NULL, NULL, &tv); if (srv <= 0) { if (srv < 0) { ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00217) "select() failed on listen socket"); apr_thread_yield(); } continue; } /* remember the last_lr we searched last time around so that we don't end up starving any particular listening socket */ if (last_lr == NULL) { lr = ap_listeners; } else { lr = last_lr->next; if (!lr) lr = ap_listeners; } first_lr = lr; do { apr_os_sock_get(&sockdes, lr->sd); if (FD_ISSET(sockdes, &main_fds)) goto got_listener; lr = lr->next; if (!lr) lr = ap_listeners; } while (lr != first_lr); /* if we get here, something unexpected happened. Go back into the select state and try again. */ continue; got_listener: last_lr = lr; sd = lr->sd; wouldblock_retry = MAX_WB_RETRIES; while (wouldblock_retry) { if ((stat = apr_socket_accept(&csd, sd, ptrans)) == APR_SUCCESS) { break; } else { /* if the error is a wouldblock then maybe we were too quick try to pull the next request from the listen queue. Try a few more times then return to our idle listen state. */ if (!APR_STATUS_IS_EAGAIN(stat)) { break; } if (wouldblock_retry--) { apr_thread_yield(); } } } /* If we got a new socket, set it to non-blocking mode and process it. Otherwise handle the error. */ if (stat == APR_SUCCESS) { apr_socket_opt_set(csd, APR_SO_NONBLOCK, 0); #ifdef DBINFO_ON if (wouldblock_retry < MAX_WB_RETRIES) { retry_success++; avg_retries += (MAX_WB_RETRIES-wouldblock_retry); } #endif break; /* We have a socket ready for reading */ } else { #ifdef DBINFO_ON if (APR_STATUS_IS_EAGAIN(stat)) { would_block++; retry_fail++; } else if ( #else if (APR_STATUS_IS_EAGAIN(stat) || #endif APR_STATUS_IS_ECONNRESET(stat) || APR_STATUS_IS_ETIMEDOUT(stat) || APR_STATUS_IS_EHOSTUNREACH(stat) || APR_STATUS_IS_ENETUNREACH(stat)) { ; } #ifdef USE_WINSOCK else if (APR_STATUS_IS_ENETDOWN(stat)) { /* * When the network layer has been shut down, there * is not much use in simply exiting: the parent * would simply re-create us (and we'd fail again). * Use the CHILDFATAL code to tear the server down. * @@@ Martin's idea for possible improvement: * A different approach would be to define * a new APEXIT_NETDOWN exit code, the reception * of which would make the parent shutdown all * children, then idle-loop until it detected that * the network is up again, and restart the children. * Ben Hyde noted that temporary ENETDOWN situations * occur in mobile IP. */ ap_log_error(APLOG_MARK, APLOG_EMERG, stat, ap_server_conf, APLOGNO(00218) "apr_socket_accept: giving up."); clean_child_exit(APEXIT_CHILDFATAL, my_worker_num, ptrans, bucket_alloc); } #endif else { ap_log_error(APLOG_MARK, APLOG_ERR, stat, ap_server_conf, APLOGNO(00219) "apr_socket_accept: (client socket)"); clean_child_exit(1, my_worker_num, ptrans, bucket_alloc); } } } ap_create_sb_handle(&sbh, ptrans, 0, my_worker_num); /* * We now have a connection, so set it up with the appropriate * socket options, file descriptors, and read/write buffers. */ current_conn = ap_run_create_connection(ptrans, ap_server_conf, csd, my_worker_num, sbh, bucket_alloc); if (current_conn) { current_conn->current_thread = thd; ap_process_connection(current_conn, csd); ap_lingering_close(current_conn); } request_count++; } clean_child_exit(0, my_worker_num, ptrans, bucket_alloc); }
/** * Yield the processor. * * @return none */ void VMCALL hythread_yield() { apr_thread_yield(); }
/* This is called when the allocator finds it has run out of memory and wants * to trigger a GC run. In this case, it's possible (probable, really) that it * will need to do that triggering, notifying other running threads that the * time has come to GC. */ void MVM_gc_enter_from_allocator(MVMThreadContext *tc) { GCORCH_LOG(tc, "Thread %d run %d : Entered from allocate\n"); /* Try to start the GC run. */ if (MVM_trycas(&tc->instance->gc_start, 0, 1)) { MVMThread *last_starter = NULL; MVMuint32 num_threads = 0; /* We are the winner of the GC starting race. This gives us some * extra responsibilities as well as doing the usual things. * First, increment GC sequence number. */ tc->instance->gc_seq_number++; GCORCH_LOG(tc, "Thread %d run %d : GC thread elected coordinator: starting gc seq %d\n", tc->instance->gc_seq_number); /* Ensure our stolen list is empty. */ tc->gc_work_count = 0; /* need to wait for other threads to reset their gc_status. */ while (tc->instance->gc_ack) apr_thread_yield(); add_work(tc, tc); /* grab our child */ signal_child(tc); do { if (tc->instance->threads && tc->instance->threads != last_starter) { MVMThread *head; MVMuint32 add; while (!MVM_trycas(&tc->instance->threads, (head = tc->instance->threads), NULL)); add = signal_all_but(tc, head, last_starter); last_starter = head; if (add) { GCORCH_LOG(tc, "Thread %d run %d : Found %d other threads\n", add); MVM_atomic_add(&tc->instance->gc_start, add); num_threads += add; } } } while (tc->instance->gc_start > 1); if (!MVM_trycas(&tc->instance->threads, NULL, last_starter)) MVM_panic(MVM_exitcode_gcorch, "threads list corrupted\n"); if (tc->instance->gc_finish != 0) MVM_panic(MVM_exitcode_gcorch, "finish votes was %d\n", tc->instance->gc_finish); tc->instance->gc_ack = tc->instance->gc_finish = num_threads + 1; GCORCH_LOG(tc, "Thread %d run %d : finish votes is %d\n", (int)tc->instance->gc_finish); /* signal to the rest to start */ if (MVM_atomic_decr(&tc->instance->gc_start) != 1) MVM_panic(MVM_exitcode_gcorch, "start votes was %d\n", tc->instance->gc_finish); run_gc(tc, MVMGCWhatToDo_All); } else { /* Another thread beat us to starting the GC sync process. Thus, act as * if we were interrupted to GC. */ GCORCH_LOG(tc, "Thread %d run %d : Lost coordinator election\n"); MVM_gc_enter_from_interrupt(tc); } }