From: Rusty Russell Date: Fri, 21 Jan 2011 05:29:34 +0000 (+1030) Subject: lca2011: turn it into a true Usenet Oracle. X-Git-Url: http://git.ozlabs.org/?p=ccan-lca-2011.git;a=commitdiff_plain;h=a1ee151190dc9ac9bf17163ab4d31f0491b8bbee lca2011: turn it into a true Usenet Oracle. We switch the port, since we keep the previous parrot version running. This version crashes on client disconnect. --- diff --git a/ccan/oserver/oserver.c b/ccan/oserver/oserver.c index 9e1f2b0..2d4cac4 100644 --- a/ccan/oserver/oserver.c +++ b/ccan/oserver/oserver.c @@ -15,10 +15,16 @@ #include #include #include +#include enum state { SENDING_GREETING, RECEIVING_USER_QUESTION, + AWAITING_A_SUBCLIENT, + SENDING_OTHER_QUESTION_PREFIX, + SENDING_OTHER_QUESTION, + RECEIVING_OTHER_ANSWER, + AWAITING_OUR_ORACLE, SENDING_ANSWER_PREFIX, SENDING_ANSWER, FINISHED @@ -27,6 +33,11 @@ enum state { static uint16_t state_flag_map[] = { [SENDING_GREETING] = TEVENT_FD_WRITE, [RECEIVING_USER_QUESTION] = TEVENT_FD_READ, + [AWAITING_A_SUBCLIENT] = 0, + [SENDING_OTHER_QUESTION_PREFIX] = TEVENT_FD_WRITE, + [SENDING_OTHER_QUESTION] = TEVENT_FD_WRITE, + [RECEIVING_OTHER_ANSWER] = TEVENT_FD_READ, + [AWAITING_OUR_ORACLE] = 0, [SENDING_ANSWER_PREFIX] = TEVENT_FD_WRITE, [SENDING_ANSWER] = TEVENT_FD_WRITE, [FINISHED] = 0 @@ -41,11 +52,15 @@ struct client { /* The question we read from client. */ char *question; /* The answer to the client. */ - const char *answer; + char *answer; /* How many bytes of the reply we sent so far. */ size_t bytes_sent; /* Our server. */ struct oserver *oserver; + /* Whose question this client is answering. */ + struct client *subclient; + /* Who is answering our question. */ + struct client *oracle; }; struct oserver { @@ -53,7 +68,6 @@ struct oserver { struct client *clients[5]; int fd; struct tevent_fd *fde; - const char *last_answer; }; static ssize_t write_string(int fd, const char *str) @@ -105,6 +119,40 @@ static bool send_string(struct client *c, const char *str) return true; } +static bool get_subclient(struct client *me) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(me->oserver->clients); i++) { + struct client *c = me->oserver->clients[i]; + if (!c || c == me) + continue; + if (c->oracle == NULL && input_finished(c->question)) { + me->subclient = c; + c->oracle = me; + return true; + } + } + return false; +} + +static bool get_oracle(struct client *me) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(me->oserver->clients); i++) { + struct client *c = me->oserver->clients[i]; + if (!c || c == me) + continue; + if (c->subclient == NULL && input_finished(c->question)) { + me->oracle = c; + c->subclient = me; + return true; + } + } + return false; +} + static void service_client(struct tevent_context *ev, struct tevent_fd *fde, uint16_t flags, void *_c) { @@ -121,14 +169,50 @@ static void service_client(struct tevent_context *ev, if (len <= 0) goto fail; if (input_finished(c->question)) { - c->answer = talloc_steal(c, c->oserver->last_answer); - c->oserver->last_answer - = talloc_steal(c->oserver, c->question); - set_state(c, SENDING_ANSWER_PREFIX); + /* Look for someone to be oracle to. */ + if (get_subclient(c)) { + set_state(c, SENDING_OTHER_QUESTION_PREFIX); + } else { + /* We sit here until someone find_oracles us */ + set_state(c, AWAITING_A_SUBCLIENT); + } + + /* Look for an oracle for ourselves. */ + if (get_oracle(c)) { + assert(c->oracle->state + == AWAITING_A_SUBCLIENT); + set_state(c->oracle, + SENDING_OTHER_QUESTION_PREFIX); + } + } + break; + case SENDING_OTHER_QUESTION_PREFIX: + if (!send_string(c, "While the Oracle ponders," + " please answer the following question:\n")) + goto fail; + break; + case SENDING_OTHER_QUESTION: + if (!send_string(c, c->subclient->question)) + goto fail; + break; + case RECEIVING_OTHER_ANSWER: + len = read_string(c->fd, &c->subclient->answer); + if (len <= 0) + goto fail; + if (input_finished(c->subclient->answer)) { + /* Did our oracle answer for us already? */ + if (input_finished(c->answer)) + set_state(c, SENDING_ANSWER_PREFIX); + else + set_state(c, AWAITING_OUR_ORACLE); + + /* If they were waiting for an answer, move them. */ + if (c->subclient->state == AWAITING_OUR_ORACLE) + set_state(c->subclient, SENDING_ANSWER_PREFIX); } break; case SENDING_ANSWER_PREFIX: - if (!send_string(c, "I believe a better question is ")) + if (!send_string(c, "The Oracle spake thus:\n")) goto fail; break; case SENDING_ANSWER: @@ -177,6 +261,9 @@ static void add_client(struct tevent_context *ev, client->bytes_sent = 0; client->question = talloc_strdup(client, ""); client->oserver = oserver; + client->oracle = NULL; + client->subclient = NULL; + client->answer = talloc_strdup(client, ""); client->fde = tevent_add_fd(ev, client, client->fd, state_flag_map[client->state], service_client, client); @@ -220,9 +307,6 @@ struct oserver *oserver_setup(struct tevent_context *ev, unsigned short port) talloc_free(oserver); return NULL; } - oserver->last_answer = talloc_strdup(oserver, - "how many manly men mendaciously" - " mention mending mansions?\n"); talloc_set_destructor(oserver, destroy_oserver); diff --git a/ccan/oserver/oserver.h b/ccan/oserver/oserver.h index 6b9a3e7..a465b2d 100644 --- a/ccan/oserver/oserver.h +++ b/ccan/oserver/oserver.h @@ -26,5 +26,5 @@ */ struct oserver *oserver_setup(struct tevent_context *ev, unsigned short port); -#define OSERVER_PORT 2727 +#define OSERVER_PORT 2828 #endif /* CCAN_OSERVER_H */ diff --git a/ccan/oserver/test/run.c b/ccan/oserver/test/run.c index e553f3d..03180c9 100644 --- a/ccan/oserver/test/run.c +++ b/ccan/oserver/test/run.c @@ -75,7 +75,7 @@ int main(int argc, char *argv[]) char c; /* This is how many tests you plan to run */ - plan_tests(15); + plan_tests(20); pipe(readyfd); pipe(exitfd); @@ -106,19 +106,31 @@ int main(int argc, char *argv[]) ok1(input_is(sfd1, "Welcome. Please ask your question.\n")); ok1(input_is(sfd2, "Welcome. Please ask your question.\n")); - ok1(write_sall(sfd1, "question")); - ok1(write_sall(sfd2, "question")); - /* It shouldn't say anything until we've finished! */ + ok1(write_sall(sfd1, "QUESTION")); + ok1(write_sall(sfd2, "QUESTION")); + ok1(write_sall(sfd1, " 1\n")); + + /* It can't ask a question yet, since client 2 isn't finished. */ ok1(no_input(sfd1)); - ok1(no_input(sfd2)); - ok1(write_sall(sfd1, " 1\n")); /* Make sure that arrives first! */ sleep(1); ok1(write_sall(sfd2, " 2\n")); - ok1(input_is(sfd1, "I believe a better question is how many manly men mendaciously mention mending mansions?\n")); - ok1(input_is(sfd2, "I believe a better question is question 1\n")); + ok1(input_is(sfd1, "While the Oracle ponders," + " please answer the following question:\nQUESTION 2\n")); + ok1(input_is(sfd2, "While the Oracle ponders," + " please answer the following question:\nQUESTION 1\n")); + + ok1(write_sall(sfd1, "ANSWER 2\n")); + ok1(write_sall(sfd2, "ANSWER 1")); + + /* Nothing, until client 2 answers. */ + ok1(no_input(sfd1)); + ok1(write_sall(sfd2, "\n")); + + ok1(input_is(sfd1, "The Oracle spake thus:\nANSWER 1\n")); + ok1(input_is(sfd2, "The Oracle spake thus:\nANSWER 2\n")); /* Sockets should be dead now. */ ok1(read(sfd1, &c, 1) == 0);