// The client thread reads and decodes json packets, // then dispatches the commands that it finds static void *client_thread(void *ptr) { struct watchman_client *client = ptr; struct watchman_event_poll pfd[2]; json_t *request; json_error_t jerr; w_stm_set_nonblock(client->stm, true); w_set_thread_name("client:stm=%p", client->stm); w_stm_get_events(client->stm, &pfd[0].evt); pfd[1].evt = client->ping; while (!stopping) { // Wait for input from either the client socket or // via the ping pipe, which signals that some other // thread wants to unilaterally send data to the client ignore_result(w_poll_events(pfd, 2, 2000)); if (stopping) { break; } if (pfd[0].ready) { request = w_json_buffer_next(&client->reader, client->stm, &jerr); if (!request && errno == EAGAIN) { // That's fine } else if (!request) { // Not so cool if (client->reader.wpos == client->reader.rpos) { // If they disconnected in between PDUs, no need to log // any error goto disconected; } send_error_response(client, "invalid json at position %d: %s", jerr.position, jerr.text); w_log(W_LOG_ERR, "invalid data from client: %s\n", jerr.text); goto disconected; } else if (request) { client->pdu_type = client->reader.pdu_type; dispatch_command(client, request, CMD_DAEMON); json_decref(request); } } if (pfd[1].ready) { w_event_test_and_clear(client->ping); } /* now send our response(s) */ while (client->head) { struct watchman_client_response *resp; /* de-queue the first response */ pthread_mutex_lock(&w_client_lock); resp = client->head; if (resp) { client->head = resp->next; if (client->tail == resp) { client->tail = NULL; } } pthread_mutex_unlock(&w_client_lock); if (resp) { bool ok; w_stm_set_nonblock(client->stm, false); /* Return the data in the same format that was used to ask for it */ ok = w_ser_write_pdu(client->pdu_type, &client->writer, client->stm, resp->json); json_decref(resp->json); free(resp); w_stm_set_nonblock(client->stm, true); if (!ok) { break; } } } } disconected: pthread_mutex_lock(&w_client_lock); w_ht_del(clients, w_ht_ptr_val(client)); pthread_mutex_unlock(&w_client_lock); return NULL; }
// The client thread reads and decodes json packets, // then dispatches the commands that it finds static void *client_thread(void *ptr) { struct watchman_client *client = ptr; struct watchman_event_poll pfd[2]; struct watchman_client_response *queued_responses_to_send; json_t *request; json_error_t jerr; bool send_ok = true; w_stm_set_nonblock(client->stm, true); w_set_thread_name("client=%p:stm=%p", client, client->stm); client->client_is_owner = w_stm_peer_is_owner(client->stm); w_stm_get_events(client->stm, &pfd[0].evt); pfd[1].evt = client->ping; while (!stopping) { // Wait for input from either the client socket or // via the ping pipe, which signals that some other // thread wants to unilaterally send data to the client ignore_result(w_poll_events(pfd, 2, 2000)); if (stopping) { break; } if (pfd[0].ready) { request = w_json_buffer_next(&client->reader, client->stm, &jerr); if (!request && errno == EAGAIN) { // That's fine } else if (!request) { // Not so cool if (client->reader.wpos == client->reader.rpos) { // If they disconnected in between PDUs, no need to log // any error goto disconected; } send_error_response(client, "invalid json at position %d: %s", jerr.position, jerr.text); w_log(W_LOG_ERR, "invalid data from client: %s\n", jerr.text); goto disconected; } else if (request) { client->pdu_type = client->reader.pdu_type; dispatch_command(client, request, CMD_DAEMON); json_decref(request); } } if (pfd[1].ready) { w_event_test_and_clear(client->ping); } /* de-queue the pending responses under the lock */ pthread_mutex_lock(&w_client_lock); queued_responses_to_send = client->head; client->head = NULL; client->tail = NULL; pthread_mutex_unlock(&w_client_lock); /* now send our response(s) */ while (queued_responses_to_send) { struct watchman_client_response *response_to_send = queued_responses_to_send; if (send_ok) { w_stm_set_nonblock(client->stm, false); /* Return the data in the same format that was used to ask for it. * Don't bother sending any more messages if the client disconnects, * but still free their memory. */ send_ok = w_ser_write_pdu(client->pdu_type, &client->writer, client->stm, response_to_send->json); w_stm_set_nonblock(client->stm, true); } queued_responses_to_send = response_to_send->next; json_decref(response_to_send->json); free(response_to_send); } } disconected: w_set_thread_name("NOT_CONN:client=%p:stm=%p", client, client->stm); // Remove the client from the map before we tear it down, as this makes // it easier to flush out pending writes on windows without worrying // about w_log_to_clients contending for the write buffers pthread_mutex_lock(&w_client_lock); w_ht_del(clients, w_ht_ptr_val(client)); pthread_mutex_unlock(&w_client_lock); client_delete(client); return NULL; }