static ngx_int_t spool_nextmsg(subscriber_pool_t *spool, nchan_msg_id_t *new_last_id) { subscriber_pool_t *newspool; channel_spooler_t *spl = spool->spooler; DBG("spool nextmsg %p (%i:%i) newid %i:%i", spool, spool->id.time, spool->id.tag, new_last_id->time, new_last_id->tag); assert(spool->id.time != new_last_id->time || spool->id.tag != new_last_id->tag); if((newspool = find_spool(spl, new_last_id)) != NULL) { assert(spool != newspool); spool_transfer_subscribers(spool, newspool, 0); destroy_spool(spool); } else { ngx_rbtree_node_t *node; node = rbtree_node_from_data(spool); rbtree_remove_node(&spl->spoolseed, node); spool->id = *new_last_id; rbtree_insert_node(&spl->spoolseed, node); spool->msg_status = MSG_INVALID; spool->msg = NULL; newspool = spool; /* newspool = get_spool(spl, new_last_id); assert(spool != newspool); spool_transfer_subscribers(spool, newspool, 0); destroy_spool(spool); */ } if(newspool->sub_count > 0 && newspool->msg_status == MSG_INVALID) { spool_fetch_msg(newspool); } return NGX_OK; }
static ngx_int_t spooler_respond_status(channel_spooler_t *self, nchan_msg_id_t *id, ngx_int_t status_code, ngx_str_t *status_line) { subscriber_pool_t *spool = find_spool(self, id); //validate_spooler(self, "before respond_status"); if(spool) { if(status_code == NGX_HTTP_NO_CONTENT) { spool->msg_status = MSG_EXPECTED; } spool_respond_general(spool, NULL, status_code, status_line, 0); destroy_spool(spool); } //validate_spooler(self, "after respond_status"); return NGX_OK; }
static ngx_int_t spooler_respond_message(channel_spooler_t *self, nchan_msg_t *msg) { static nchan_msg_id_t any_msg = {0,0}; int i, max=2; subscriber_pool_t *spools[] = { find_spool(self, &msg->prev_id), find_spool(self, &any_msg) }; //DBG("%p respond msg %i:%i", self, msg->id.time, msg->id.tag); if(msg->prev_id.time == 0 && msg->prev_id.tag == 0) { max = 1; } for(i=0; i < max; i++) { //respond to prev_id spool and the {0,0} spool, as it's meant to accept any message; if(spools[i]) { spool_respond_general(spools[i], msg, 0, NULL); spool_nextmsg(spools[i], &msg->id); } } self->prev_msg_id.time = msg->id.time; self->prev_msg_id.tag = msg->id.tag; return NGX_OK; }
static ngx_int_t spool_fetch_msg_callback(nchan_msg_status_t findmsg_status, nchan_msg_t *msg, fetchmsg_data_t *data) { nchan_msg_id_t anymsg = {0,0}; subscriber_pool_t *spool, *nuspool; if((spool = find_spool(data->spooler, &data->msgid)) == NULL) { ERR("spool for msgid %i:%i not found. discarding getmsg callback response.", data->msgid.time, data->msgid.tag); ngx_free(data); return NGX_ERROR; } ngx_free(data); spool->msg_status = findmsg_status; switch(findmsg_status) { case MSG_FOUND: DBG("fetchmsg callback for spool %p msg FOUND %p %i:%i", spool, msg, msg->id.time, msg->id.tag); assert(msg != NULL); spool->msg = msg; spool_respond_general(spool, spool->msg, 0, NULL); spool_nextmsg(spool, &msg->id); break; case MSG_EXPECTED: // ♫ It's gonna be the future soon ♫ DBG("fetchmsg callback for spool %p msg EXPECTED", spool); assert(msg == NULL); spool->msg = NULL; break; case MSG_NOTFOUND: case MSG_EXPIRED: //is this right? //TODO: maybe message-expired notification //spool_respond_general(spool, NULL, NGX_HTTP_NO_CONTENT, NULL); nuspool = get_spool(spool->spooler, &anymsg); assert(spool != nuspool); spool_transfer_subscribers(spool, nuspool, 1); destroy_spool(spool); break; default: assert(0); break; } return NGX_OK; }
static ngx_int_t spool_fetch_msg_callback(nchan_msg_status_t findmsg_status, nchan_msg_t *msg, fetchmsg_data_t *data) { nchan_msg_status_t prev_status; subscriber_pool_t *spool, *nuspool; channel_spooler_t *spl = data->spooler; int free_msg_id = 1; if(spl && data == spl->fetchmsg_cb_data_list) { spl->fetchmsg_cb_data_list = data->next; } if(data->next) { data->next->prev = data->prev; } if(data->prev) { data->prev->next = data->next; } if(spl == NULL) { //channel already deleted nchan_free_msg_id(&data->msgid); ngx_free(data); return NGX_OK; } if(spl->handlers->get_message_finish) { spl->handlers->get_message_finish(spl, spl->handlers_privdata); } if((spool = find_spool(spl, &data->msgid)) == NULL) { DBG("spool for msgid %V not found. discarding getmsg callback response.", msgid_to_str(&data->msgid)); nchan_free_msg_id(&data->msgid); ngx_free(data); return NGX_ERROR; } prev_status = spool->msg_status; switch(findmsg_status) { case MSG_FOUND: spool->msg_status = findmsg_status; DBG("fetchmsg callback for spool %p msg FOUND %p %V", spool, msg, msgid_to_str(&msg->id)); assert(msg != NULL); spool->msg = msg; spool_respond_general(spool, spool->msg, 0, NULL, 0); spool_nextmsg(spool, &msg->id); break; case MSG_EXPECTED: // ♫ It's gonna be the future soon ♫ if(spool->id.time == NCHAN_NTH_MSGID_TIME) { //wait for message in the NEWEST_ID spool nchan_msg_id_t newest_id = NCHAN_NEWEST_MSGID; spool_nextmsg(spool, &newest_id); } else { spool->msg_status = findmsg_status; DBG("fetchmsg callback for spool %p msg EXPECTED", spool); spool_respond_general(spool, NULL, NGX_HTTP_NO_CONTENT, NULL, 0); assert(msg == NULL); spool->msg = NULL; } break; case MSG_NORESPONSE: if(prev_status == MSG_PENDING) { spool->msg_status = MSG_INVALID; if(spool->sub_count > 0) { nomsg_retry_data_t *retry_data = ngx_alloc(sizeof(*retry_data), ngx_cycle->log); retry_data->spooler = spl; free_msg_id = 0; retry_data->msg_id = data->msgid; spooler_add_timer(spl, NCHAN_MSG_NORESPONSE_RETRY_TIME, spool_fetch_msg_noresponse_retry_callback, spool_fetch_msg_noresponse_retry_cancel, retry_data); } } break; case MSG_NOTFOUND: if(spl->fetching_strategy == FETCH_IGNORE_MSG_NOTFOUND) { spool->msg_status = prev_status; break; } case MSG_EXPIRED: //is this right? //TODO: maybe message-expired notification spool->msg_status = findmsg_status; spool_respond_general(spool, NULL, NGX_HTTP_NO_CONTENT, NULL, 0); nuspool = get_spool(spool->spooler, &oldest_msg_id); if(spool != nuspool) { spool_transfer_subscribers(spool, nuspool, 1); destroy_spool(spool); } else { ERR("Unexpected spool == nuspool during spool fetch_msg_callback. This is weird, please report this to the developers. findmsg_status: %i", findmsg_status); assert(0); } break; case MSG_PENDING: ERR("spool %p set status to MSG_PENDING", spool); break; default: assert(0); break; } if(free_msg_id) { nchan_free_msg_id(&data->msgid); } ngx_free(data); return NGX_OK; }
static ngx_int_t spool_nextmsg(subscriber_pool_t *spool, nchan_msg_id_t *new_last_id) { subscriber_pool_t *newspool; channel_spooler_t *spl = spool->spooler; ngx_int_t immortal_spool = spool->id.time == NCHAN_NEWEST_MSGID_TIME; int16_t largetags[NCHAN_MULTITAG_MAX]; nchan_msg_id_t new_id = NCHAN_ZERO_MSGID; nchan_copy_msg_id(&new_id, &spool->id, largetags); nchan_update_multi_msgid(&new_id, new_last_id, largetags); //ERR("spool %p nextmsg (%V) --", spool, msgid_to_str(&spool->id)); //ERR(" -- update with (%V) --", msgid_to_str(new_last_id)); //ERR(" -- newid %V", msgid_to_str(&new_id)); if(msg_ids_equal(&spool->id, &new_id)) { ERR("nextmsg id same as curmsg (%V)", msgid_to_str(&spool->id)); assert(0); } else { newspool = !immortal_spool ? find_spool(spl, &new_id) : get_spool(spl, &new_id); if(newspool != NULL) { assert(spool != newspool); spool_transfer_subscribers(spool, newspool, 0); if(!immortal_spool) destroy_spool(spool); } else { ngx_rbtree_node_t *node; assert(!immortal_spool); node = rbtree_node_from_data(spool); rbtree_remove_node(&spl->spoolseed, node); nchan_copy_msg_id(&spool->id, &new_id, NULL); rbtree_insert_node(&spl->spoolseed, node); spool->msg_status = MSG_INVALID; spool->msg = NULL; newspool = spool; /* newspool = get_spool(spl, &new_id); assert(spool != newspool); spool_transfer_subscribers(spool, newspool, 0); destroy_spool(spool); */ } if(newspool->non_internal_sub_count > 0 && spl->handlers->bulk_post_subscribe != NULL) { spl->handlers->bulk_post_subscribe(spl, newspool->non_internal_sub_count, spl->handlers_privdata); } if(newspool->sub_count > 0) { switch(newspool->msg_status) { case MSG_CHANNEL_NOTREADY: newspool->msg_status = MSG_INVALID; case MSG_INVALID: spool_fetch_msg(newspool); break; case MSG_EXPECTED: spool_respond_general(newspool, NULL, NGX_HTTP_NO_CONTENT, NULL, 0); break; default: break; } } } return NGX_OK; }