// pong_thread(threadarg) // Connect to the server at the position indicated by `threadarg` // (which is a pointer to a `pong_args` structure). void* pong_thread(void* threadarg) { pthread_detach(pthread_self()); // Copy thread arguments onto our stack. pong_args pa = *((pong_args*) threadarg); char url[256]; snprintf(url, sizeof(url), "move?x=%d&y=%d&style=on", pa.x, pa.y); http_connection* conn = http_connect(pong_addr); http_send_request(conn, url); http_receive_response_headers(conn); if (conn->status_code != 200) fprintf(stderr, "%.3f sec: warning: %d,%d: " "server returned status %d (expected 200)\n", elapsed(), pa.x, pa.y, conn->status_code); http_receive_response_body(conn); double result = strtod(conn->buf, NULL); if (result < 0) { fprintf(stderr, "%.3f sec: server returned error: %s\n", elapsed(), http_truncate_response(conn)); exit(1); } http_close(conn); // signal the main thread to continue pthread_cond_signal(&condvar); // and exit! pthread_exit(NULL); }
// main(argc, argv) // The main loop. int main(int argc, char** argv) { // parse arguments int ch, nocheck = 0; while ((ch = getopt(argc, argv, "nh:p:u:")) != -1) { if (ch == 'h') pong_host = optarg; else if (ch == 'p') pong_port = optarg; else if (ch == 'u') pong_user = optarg; else if (ch == 'n') nocheck = 1; else usage(); } if (optind == argc - 1) pong_user = argv[optind]; else if (optind != argc) usage(); // look up network address of pong server struct addrinfo hints; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_NUMERICSERV; int r = getaddrinfo(pong_host, pong_port, &hints, &pong_addr); if (r != 0) { fprintf(stderr, "problem looking up %s: %s\n", pong_host, gai_strerror(r)); exit(1); } // reset pong board and get its dimensions int width, height; { http_connection* conn = http_connect(pong_addr); http_send_request(conn, nocheck ? "reset?nocheck=1" : "reset"); http_receive_response_headers(conn); http_receive_response_body(conn); if (conn->status_code != 200 || sscanf(conn->buf, "%d %d\n", &width, &height) != 2 || width <= 0 || height <= 0) { fprintf(stderr, "bad response to \"reset\" RPC: %d %s\n", conn->status_code, http_truncate_response(conn)); exit(1); } http_close(conn); } // measure future times relative to this moment elapsed_base = timestamp(); // print display URL printf("Display: http://%s:%s/%s/%s\n", pong_host, pong_port, pong_user, nocheck ? " (NOCHECK mode)" : ""); // initialize global synchronization objects pthread_mutex_init(&mutex, NULL); pthread_cond_init(&condvar, NULL); // play game int x = 0, y = 0, dx = 1, dy = 1; while (1) { // create a new thread to handle the next position pong_args pa; pa.x = x; pa.y = y; pthread_t pt; r = pthread_create(&pt, NULL, pong_thread, &pa); if (r != 0) { fprintf(stderr, "%.3f sec: pthread_create: %s\n", elapsed(), strerror(r)); exit(1); } // wait until that thread signals us to continue pthread_mutex_lock(&mutex); pthread_cond_wait(&condvar, &mutex); pthread_mutex_unlock(&mutex); // update position x += dx; y += dy; if (x < 0 || x >= width) { dx = -dx; x += 2 * dx; } if (y < 0 || y >= height) { dy = -dy; y += 2 * dy; } // wait 0.1sec usleep(100000); } }
// pong_thread(threadarg) // Connect to the server at the position indicated by `threadarg` // (which is a pointer to a `pong_args` structure). void* pong_thread(void* threadarg) { pthread_detach(pthread_self()); // Copy thread arguments onto our stack. pong_args pa = *((pong_args*) threadarg); char url[256]; snprintf(url, sizeof(url), "move?x=%d&y=%d&style=on", pa.x, pa.y); http_connection* conn; size_t sleeptime = 10000; retry: // find reusable connection using LIFO access pthread_mutex_lock(&table_lock); if(conn_done_num > 0) { conn = conn_done_table[conn_done_num - 1]; --conn_done_num; pthread_mutex_unlock(&table_lock); } else { // if not, create a new connection pthread_mutex_unlock(&table_lock); conn = http_connect(pong_addr); } // let other threads wait for a sleeping thread due to server down pthread_mutex_lock(&time_lock); while(stop_time != 0) { pthread_cond_wait(&stop_time_cond, &time_lock); } pthread_mutex_unlock(&time_lock); // send request after the server has waken up http_send_request(conn, url); http_receive_response_headers(conn); // failed connection while(conn->state == HTTP_BROKEN && conn->status_code == -1) { http_close(conn); // usleep less than 1 second if(sleeptime < 1000000) usleep(sleeptime); else { // sleep longer than a second sleep(1); usleep(sleeptime % 1000000); } // keep sleep time less than 2 seconds and double each retry if(sleeptime <= 1000000) sleeptime += sleeptime; goto retry; } if (conn->status_code != 200) fprintf(stderr, "%.3f sec: warning: %d,%d: " "server returned status %d (expected 200)\n", elapsed(), pa.x, pa.y, conn->status_code); // signal more thread creation after having received header pthread_cond_signal(&condvar); http_receive_response_body(conn); // if server sends STOP, read the time in millisecond to stop_time pthread_mutex_lock(&time_lock); if(stop_time == 0 && sscanf(http_truncate_response(conn), "+%d STOP", &stop_time) && stop_time != 0) { pthread_mutex_unlock(&time_lock); // sleep less than 1 second if(stop_time < 1000) usleep(stop_time * 1000); else { // sleep longer than 1 second sleep(stop_time / 1000); usleep((stop_time % 1000) * 1000); } pthread_mutex_lock(&time_lock); stop_time = 0; // woke up, tell all threads to continue pthread_cond_broadcast(&stop_time_cond); pthread_mutex_unlock(&time_lock); } else if (stop_time != 0) { while(stop_time != 0) { pthread_cond_wait(&stop_time_cond, &time_lock); } if(sscanf(http_truncate_response(conn), "+%d STOP", &stop_time) && stop_time != 0) { pthread_mutex_unlock(&time_lock); if(stop_time < 1000) usleep(stop_time * 1000); else { sleep(stop_time / 1000); usleep((stop_time % 1000) * 1000); } pthread_mutex_lock(&time_lock); stop_time = 0; pthread_cond_broadcast(&stop_time_cond); pthread_mutex_unlock(&time_lock); } else { pthread_mutex_unlock(&time_lock); } } else { pthread_mutex_unlock(&time_lock); } double result = strtod(conn->buf, NULL); if (result < 0) { fprintf(stderr, "%.3f sec: server returned error: %s\n", elapsed(), http_truncate_response(conn)); exit(1); } // if the connection has status done, keep it in the connection table pthread_mutex_lock(&table_lock); if(conn->state == HTTP_DONE && conn_done_num < 29) { conn_done_table[conn_done_num] = conn; ++conn_done_num; pthread_mutex_unlock(&table_lock); } else { pthread_mutex_unlock(&table_lock); http_close(conn); } // signal the main thread to continue // pthread_cond_signal(&condvar); // and exit! pthread_exit(NULL); }