From 83057033caaf4f0c08679adee2badbe206e943b7 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 25 Jan 2011 10:59:15 +1030 Subject: [PATCH] lca2011: fix client disconnect during answer (poorly). We seek an oracle or a client when we need one, and unset when we are destroyed. This can cause crossed answers; better would be to actually move the questions/answers around. --- ccan/oserver/oserver.c | 77 +++++++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 31 deletions(-) diff --git a/ccan/oserver/oserver.c b/ccan/oserver/oserver.c index 2d4cac4..c293dd5 100644 --- a/ccan/oserver/oserver.c +++ b/ccan/oserver/oserver.c @@ -20,11 +20,9 @@ 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 @@ -33,11 +31,9 @@ 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 @@ -153,6 +149,11 @@ static bool get_oracle(struct client *me) return false; } +static void wakeup(struct client *c) +{ + tevent_fd_set_flags(c->fde, state_flag_map[c->state]); +} + static void service_client(struct tevent_context *ev, struct tevent_fd *fde, uint16_t flags, void *_c) { @@ -168,50 +169,36 @@ static void service_client(struct tevent_context *ev, len = read_string(c->fd, &c->question); if (len <= 0) goto fail; - if (input_finished(c->question)) { - /* 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); - } - } + if (input_finished(c->question)) + set_state(c, SENDING_OTHER_QUESTION_PREFIX); break; case SENDING_OTHER_QUESTION_PREFIX: + if (!c->subclient) + goto need_subclient; if (!send_string(c, "While the Oracle ponders," " please answer the following question:\n")) goto fail; break; case SENDING_OTHER_QUESTION: + if (!c->subclient) + goto need_subclient; if (!send_string(c, c->subclient->question)) goto fail; break; case RECEIVING_OTHER_ANSWER: + if (!c->subclient) + goto need_subclient; 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); + set_state(c, SENDING_ANSWER_PREFIX); + wakeup(c->subclient); } break; case SENDING_ANSWER_PREFIX: + if (!input_finished(c->answer)) + goto need_answer; if (!send_string(c, "The Oracle spake thus:\n")) goto fail; break; @@ -225,15 +212,43 @@ static void service_client(struct tevent_context *ev, if (c->state != FINISHED) return; - fail: talloc_free(c); + return; + +need_subclient: + if (!get_subclient(c)) { + /* We can't get one: go to sleep until someone find_oracle() */ + tevent_fd_set_flags(c->fde, 0); + } else + /* In case they are waiting... */ + wakeup(c->subclient); + return; + +need_answer: + /* If we don't have an oracle and find one, that's OK. */ + if (!c->oracle && get_oracle(c)) { + /* In case they are waiting... */ + wakeup(c->oracle); + return; + } + + /* Either our oracle is not finished, or we don't have one: sleep. */ + tevent_fd_set_flags(c->fde, 0); } static int cleanup_client(struct client *client) { unsigned int i; + /* We were an oracle? */ + if (client->subclient) + client->subclient->oracle = NULL; + + /* We had an oracle? */ + if (client->oracle) + client->oracle->subclient = NULL; + for (i = 0; i < ARRAY_SIZE(client->oserver->clients); i++) { if (client->oserver->clients[i] == client) { client->oserver->clients[i] = NULL; -- 2.39.2