ngx_int_t websocket_subscriber_destroy(subscriber_t *sub) { full_subscriber_t *fsub = (full_subscriber_t *)sub; nchan_request_ctx_t *ctx; if(!fsub->awaiting_destruction) { ctx = ngx_http_get_module_ctx(fsub->sub.request, nchan_module); ctx->sub = NULL; } if(fsub->upstream_stuff && fsub->upstream_stuff->psr_data.tmp_pool) { ngx_destroy_pool(fsub->upstream_stuff->psr_data.tmp_pool); } if(sub->reserved > 0) { DBG("%p not ready to destroy (reserved for %i) for req %p", sub, sub->reserved, fsub->sub.request); fsub->awaiting_destruction = 1; } else { DBG("%p destroy for req %p", sub, fsub->sub.request); nchan_free_msg_id(&fsub->sub.last_msgid); #if NCHAN_SUBSCRIBER_LEAK_DEBUG subscriber_debug_remove(&fsub->sub); #endif nchan_free_msg_id(&sub->last_msgid); //debug ngx_memset(fsub, 0x13, sizeof(*fsub)); ngx_free(fsub); } return NGX_OK; }
ngx_int_t stop_spooler(channel_spooler_t *spl, uint8_t dequeue_subscribers) { ngx_rbtree_node_t *cur, *sentinel; spooler_event_ll_t *ecur, *ecur_next; subscriber_pool_t *spool; rbtree_seed_t *seed = &spl->spoolseed; ngx_rbtree_t *tree = &seed->tree; ngx_int_t n=0; sentinel = tree->sentinel; fetchmsg_data_t *dcur; #if NCHAN_RBTREE_DBG ngx_int_t active_before = seed->active_nodes, allocd_before = seed->active_nodes; #endif if(spl->running) { for(ecur = spl->spooler_dependent_events; ecur != NULL; ecur = ecur_next) { ecur_next = ecur->next; if(ecur->cancel) { ecur->cancel(ecur->ev.data); } ngx_event_del_timer(&ecur->ev); ngx_free(ecur); } for(cur = tree->root; cur != NULL && cur != sentinel; cur = tree->root) { spool = (subscriber_pool_t *)rbtree_data_from_node(cur); if(dequeue_subscribers) { destroy_spool(spool); } else { remove_spool(spool); rbtree_destroy_node(seed, cur); } n++; } for(dcur = spl->fetchmsg_cb_data_list; dcur != NULL; dcur = dcur->next) { dcur->spooler = NULL; } DBG("stopped %i spools in SPOOLER %p", n, *spl); } else { DBG("SPOOLER %p not running", *spl); } #if NCHAN_RBTREE_DBG assert(active_before - n == 0); assert(allocd_before - n == 0); assert(seed->active_nodes == 0); assert(seed->allocd_nodes == 0); #endif nchan_free_msg_id(&spl->prev_msg_id); spl->running = 0; return NGX_OK; }
ngx_int_t msg_release(nchan_msg_t *msg, char *lbl) { nchan_msg_t *parent = msg->parent; if(parent) { assert(msg->storage != NCHAN_MSG_SHARED); #if NCHAN_MSG_RESERVE_DEBUG nchan_msg_release_debug(msg, lbl); #endif msg->refcount--; assert(msg->refcount >= 0); if(msg->refcount == 0) { switch(msg->storage) { case NCHAN_MSG_POOL: //free the id, the rest of the msg will be cleaned with the pool nchan_free_msg_id(&msg->id); break; case NCHAN_MSG_HEAP: nchan_free_msg_id(&msg->id); ngx_free(msg); break; default: break; //do nothing for NCHAN_MSG_STACK. NCHAN_MSG_SHARED should never be seen here. } } return msg_release(parent, lbl); } assert(!parent); #if NCHAN_MSG_RESERVE_DEBUG nchan_msg_release_debug(msg, lbl); #endif assert(msg->refcount > 0); ngx_atomic_fetch_add((ngx_atomic_uint_t *)&msg->refcount, -1); //DBG("msg %p released (%i) %s", msg, msg->refcount, lbl); return NGX_OK; }
static ngx_int_t remove_spool(subscriber_pool_t *spool) { channel_spooler_t *spl = spool->spooler; ngx_rbtree_node_t *node = rbtree_node_from_data(spool); DBG("remove spool node %p", node); assert(spool->spooler->running); nchan_free_msg_id(&spool->id); rbtree_remove_node(&spl->spoolseed, rbtree_node_from_data(spool)); //assert((node = rbtree_find_node(&spl->spoolseed, &spool->id)) == NULL); //double-check that it's gone return NGX_OK; }
ngx_int_t longpoll_subscriber_destroy(subscriber_t *sub) { full_subscriber_t *fsub = (full_subscriber_t *)sub; if(sub->reserved > 0) { DBG("%p not ready to destroy (reserved for %i) for req %p", sub, sub->reserved, fsub->sub.request); fsub->data.awaiting_destruction = 1; } else { DBG("%p destroy for req %p", sub, fsub->sub.request); nchan_free_msg_id(&fsub->sub.last_msgid); #if NCHAN_SUBSCRIBER_LEAK_DEBUG subscriber_debug_remove(sub); ngx_free(sub->lbl); ngx_memset(fsub, 0xB9, sizeof(*fsub)); //debug #endif ngx_free(fsub); } 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 void spool_fetch_msg_noresponse_retry_cancel(void *pd) { nomsg_retry_data_t *d = pd; nchan_free_msg_id(&d->msg_id); ngx_free(d); }