/* * Each bot's entry point. 'arg' must be a dynamically allocated THREAD_ARG * pointer. The pointer is freed by the new thread. */ void* BotEntryPoint(void *arg) { THREAD_DATA *td = (THREAD_DATA*)arg; /* set the tls key */ pthread_setspecific(g_tls_key, td); init_thread_data(td); db_instance_init(); cmd_instance_init(td); player_instance_init(td); libman_instance_init(td); /* load the core pseudo-plugin */ libman_load_library(td, NULL); /* load the bots libraries */ int num_plugins = DelimCount(td->libstring, ' ') + 1; for (int i = 0; i < num_plugins; ++i) { char libname[64]; DelimArgs(libname, 64, td->libstring, i, ' ', false); if (strlen(libname) > 0) { libman_load_library(td, libname); } } /* mainloop */ mainloop(td); disconnect_from_server(td); libman_instance_shutdown(td); player_instance_shutdown(td); cmd_instance_shutdown(td); db_instance_shutdown(); botman_bot_exiting(td->botman_handle); free_thread_data(td); free(td); return NULL; }
static void mainloop(THREAD_DATA *td) { THREAD_DATA::net_t *n = td->net; ticks_ms_t acc, ticks, lticks; /* accumulator, current ticks, last iteration ticks */ int pktl; /* packet length */ uint8_t pkt[MAX_PACKET]; /* buffer space for a packet */ if (connect_to_server(td) != 0) { free_thread_data(td); LogFmt(OP_MOD, "Error performing initial connect"); return; } acc = 0; ticks = get_ticks_ms(); lticks = ticks; ticks_ms_t last_botman_checkin = ticks; ticks_ms_t last_botman_stopcheck = ticks; ticks_ms_t last_config_mtime_check = ticks; while (td->running >= 0) { ticks = get_ticks_ms(); acc += ticks - lticks; lticks = ticks; /* check in with the bot manager */ if (ticks - last_botman_checkin >= BOTMAN_CHECKIN_INTERVAL) { botman_bot_checkin(td->botman_handle); last_botman_checkin = ticks; } if (ticks - last_botman_stopcheck >= BOTMAN_STOPCHECK_INTERVAL) { if (botman_bot_shouldstop(td->botman_handle)) { td->running = -1; } last_botman_stopcheck = ticks; } /* flush out tick events to bots */ if (acc >= STEP_INTERVAL) { libman_expire_timers(td); while(acc >= STEP_INTERVAL) { /* event_tick */ libman_export_event(td, EVENT_TICK, NULL); acc -= STEP_INTERVAL; } } /* if the bot is disconnected, see if it is time to reconnect */ if (n->state == NS_DISCONNECTED) { if (ticks - n->ticks->disconnected > 60000) { free_thread_data(td); init_thread_data(td); connect_to_server(td); } else { usleep(50000); /* 50ms */ continue; } } /* see if the config file has been modified and if so send a reread event */ if (ticks - last_config_mtime_check >= CONFIG_MTIME_POLL_INTERVAL) { struct stat attr; memset(&attr, 0, sizeof(struct stat)); if (stat(td->config->filename, &attr) == 0) { if (td->config->last_modified_time != attr.st_mtime) { libman_export_event(td, EVENT_CONFIG_CHANGE, NULL); td->config->last_modified_time = attr.st_mtime; } } last_config_mtime_check = ticks; } /* use up to STEP_INTERVAL ms for the db thread */ ticks_ms_t ticks_taken = get_ticks_ms() - ticks; ticks_ms_t db_ticks = ticks_taken > STEP_INTERVAL ? STEP_INTERVAL : STEP_INTERVAL - ticks_taken; db_instance_export_events(db_ticks); /* read a packet or wait for a timeout */ ticks_taken = get_ticks_ms() - ticks; ticks_ms_t timeout = ticks_taken > STEP_INTERVAL ? 0 : STEP_INTERVAL - ticks_taken; while (poll(n->pfd, 1, (int)timeout) > 0) { /* process incoming packet, data is waiting */ pktl = (int)read(n->fd, pkt, MAX_PACKET); if (pktl >= 0) { ++n->stats->packets_read; n->ticks->last_pkt_received = get_ticks_ms(); if (n->encrypt->use_encryption) { if (pkt[0] == 0x00) { if (pktl >= 2) { decrypt_buffer(td, &pkt[2], pktl-2); } } else { decrypt_buffer(td, &pkt[1], pktl-1); } } if (td->debug->spew_packets) { spew_packet(pkt, pktl, DIR_INCOMING); } process_incoming_packet(td, pkt, pktl); } ticks_taken = get_ticks_ms() - ticks; timeout = timeout > ticks_taken ? timeout - ticks_taken : 0; } /* update the tick count after potential sleeping in poll() */ ticks = get_ticks_ms(); /* network state specfic actions */ if (n->state == NS_CONNECTING) { /* retransmit connection request if it was lost */ if (ticks - n->ticks->last_connection_request > 15000) { pkt_send_client_key(n->encrypt->client_key); n->ticks->last_connection_request = ticks; } } else if (ticks - n->ticks->last_pkt_received > 30*1000) { /* disconnect if no packets have been received for 30 seconds */ Log(OP_MOD, "No data received for 30 seconds, reconnecting..."); disconnect_from_server(td); continue; } /* transmit player position update if necessary */ if (n->state == NS_CONNECTED && td->in_arena) { if ((ticks - n->ticks->last_pos_update_sent > 100 && td->bot_ship != SHIP_SPECTATOR) || (ticks - n->ticks->last_pos_update_sent > 1000 && td->bot_ship == SHIP_SPECTATOR)) { pkt_send_position_update(td->bot_pos->x, td->bot_pos->y, td->bot_vel->x, td->bot_vel->y); n->ticks->last_pos_update_sent = ticks; } } /* send periodic info/einfo */ if (n->state == NS_CONNECTED) { // subtract 10000 to offset this by 10 seconds from *einfo to avoid filling buffers with commands/responses if (td->periodic->info && ticks - (td->periodic->last_info - 10000U) >= td->periodic->info) { int nhere = player_get_phere(td); PLAYER *parray = player_get_parray(td); for (int i = 0; i < nhere; ++i) { if (parray[i].here && td->enter->send_info) { PrivMessage(&parray[i], "*info"); } } td->periodic->last_info = ticks; } if (td->periodic->einfo && ticks - td->periodic->last_einfo >= td->periodic->einfo) { int nhere = player_get_phere(td); PLAYER *parray = player_get_parray(td); for (int i = 0; i < nhere; ++i) { if (parray[i].here && td->enter->send_einfo) { PrivMessage(&parray[i], "*einfo"); } } td->periodic->last_einfo = ticks; } } /* retransmit reliable packets that have not been acked */ rpacket_list_t *l = n->rel_o->queue; rpacket_list_t::iterator iter = l->begin(); while (iter != l->end()) { RPACKET *rp = *iter; if (ticks - rp->ticks > RELIABLE_RETRANSMIT_INTERVAL) { PACKET *p = allocate_packet(rp->len); memcpy(p->data, rp->data, rp->len); /* update packets retransmit tick */ rp->ticks = ticks; queue_packet(p, SP_HIGH); } ++iter; } /* free absent players if its time */ ticks_ms_t flush_check_interval = 60 * 60 * 1000; if (ticks - td->arena->ticks->last_player_flush > flush_check_interval) { player_free_absent_players(td, flush_check_interval, true); td->arena->ticks->last_player_flush = ticks; } /* write packets generated during loop iteration */ send_outgoing_packets(td); } /* while td->running != 0 */ }
int main(int argc, char **argv) { int i; const char *getopt_str; int opt_p=0, opt_g=0, opt_a=0, opt_m=0; #if TEST_MPI init_test_mpi(&argc, &argv); getopt_str = "pgamlvtdi:"; #else getopt_str = "pgalvtdi:"; #endif GASNET_Safe(gasnet_init(&argc, &argv)); GASNET_Safe(gasnet_attach(htable, HANDLER_TABLE_SIZE, TEST_SEGSZ_REQUEST, TEST_MINHEAPOFFSET)); #if TEST_MPI #define TEST_MPI_USAGE " -m use MPI calls \n" #else #define TEST_MPI_USAGE "" #endif #if GASNET_PAR #define TEST_THREAD_USAGE " [<threads_per_node>]\n\n" \ "<threads_per_node> must be between 1 and "_STRINGIFY(TEST_MAXTHREADS)" \n" #else #define TEST_THREAD_USAGE "\n\n" #endif test_init("testthreads",0, "[ -pgalvtd ] [ -i <iters> ]" TEST_THREAD_USAGE "no options means run all tests with "_STRINGIFY(DEFAULT_ITERS)" iterations\n" "options: \n" " -p use puts \n" " -g use gets \n" " -a use Active Messages \n" " -l use local Active Messages \n" TEST_MPI_USAGE " -v output information about actions taken \n" " -t include AM handler actions with -v \n" " -d dynamic thread creation stress test \n" " -i <iters> use <iters> iterations per thread \n"); while ((i = getopt (argc, argv, getopt_str)) != EOF) { switch (i) { case 'p': opt_p = 1; break; case 'g': opt_g = 1; break; case 'a': opt_a = 1; break; case 'm': opt_m = 1; break; case 'l': AM_loopback = 1; break; case 'i': iters = atoi(optarg); break; case 'v': verbose = 1; break; case 't': amtrace = 1; break; case 'd': threadstress = 1; break; default: test_usage(); } } if (opt_p) test_functions[functions_num++] = test_put; if (opt_g) test_functions[functions_num++] = test_get; if (opt_a) { test_functions[functions_num++] = test_amshort; test_functions[functions_num++] = test_ammedium; test_functions[functions_num++] = test_amlong; } #if TEST_MPI if (opt_m) test_functions[functions_num++] = test_mpi; #endif if (amtrace) verbose = 1; /* Assume all test functions if no option is passed */ if (functions_num == 0) { MSG("running all functions!"); memcpy(test_functions, test_functions_all, sizeof(test_functions_all)); functions_num = NUM_FUNCTIONS; } argc -= optind; if (argc > 1) test_usage(); else if (argc == 1) { argv += optind; threads_num = atoi(argv[0]); } if (threads_num > TEST_MAXTHREADS || threads_num < 1) { printf("ERROR: Threads must be between 1 and %i\n",TEST_MAXTHREADS); exit(EXIT_FAILURE); } /* limit sizes to a reasonable size */ #define LIMIT(sz) MIN(sz,4194304) { int sz = 0; sizes[sz++] = LIMIT(gasnet_AMMaxMedium()-1); sizes[sz++] = LIMIT(gasnet_AMMaxMedium()); sizes[sz++] = LIMIT(gasnet_AMMaxMedium()+1); sizes[sz++] = LIMIT(gasnet_AMMaxLongRequest()-1); sizes[sz++] = LIMIT(gasnet_AMMaxLongRequest()); sizes[sz++] = LIMIT(gasnet_AMMaxLongRequest()+1); sizes[sz++] = LIMIT(gasnet_AMMaxLongReply()-1); sizes[sz++] = LIMIT(gasnet_AMMaxLongReply()); sizes[sz++] = LIMIT(gasnet_AMMaxLongReply()+1); assert(sizes[sz] == 0); } alloc_thread_data(threads_num); #if TEST_MPI attach_test_mpi(); #endif #ifdef GASNET_PAR if (threadstress) { int spawniters = MAX(1,iters/threads_num); int i; MSG("Dynamic thread creation stress test, %d gasnet threads, (%d at a time)", spawniters*threads_num, threads_num); iters = 10; /* enough iters to ensure we get thread registration */ for (i = 0; i < spawniters; i++) { test_createandjoin_pthreads(threads_num, &threadmain, tt_thread_data, sizeof(threaddata_t)); TEST_PROGRESS_BAR(i, spawniters); } } else { MSG("Forking %d gasnet threads and running %d iterations", threads_num, iters); test_createandjoin_pthreads(threads_num, &threadmain, tt_thread_data, sizeof(threaddata_t)); } #else /* for testmpi-seq and -parsync */ #ifdef GASNET_SEQ MSG("Running with 1 thread/node for GASNET_SEQ mode"); #else MSG("Running with 1 thread/node for GASNET_PARSYNC mode"); #endif threadmain(tt_thread_data); #endif BARRIER(); free_thread_data(); MSG("Tests complete"); BARRIER(); gasnet_exit(0); return 0; }