//temporary cheat static ngx_int_t ngx_http_push_store_publish(ngx_http_push_channel_t *channel, ngx_http_push_msg_t *msg, ngx_int_t status_code, const ngx_str_t *status_line, ngx_log_t *log) { //subscribers are queued up in a local pool. Queue heads, however, are located //in shared memory, identified by pid. ngx_shmtx_lock(&ngx_http_push_shpool->mutex); ngx_http_push_pid_queue_t *sentinel = &channel->workers_with_subscribers; ngx_http_push_pid_queue_t *cur = sentinel; ngx_int_t received; received = channel->subscribers > 0 ? NGX_HTTP_PUSH_MESSAGE_RECEIVED : NGX_HTTP_PUSH_MESSAGE_QUEUED; if(msg!=NULL && received==NGX_HTTP_PUSH_MESSAGE_RECEIVED) { //just for now ngx_http_push_store_reserve_message_locked(channel, msg); } while((cur=(ngx_http_push_pid_queue_t *)ngx_queue_next(&cur->queue))!=sentinel) { pid_t worker_pid = cur->pid; ngx_int_t worker_slot = cur->slot; ngx_http_push_subscriber_t *subscriber_sentinel= cur->subscriber_sentinel; /* each time all of a worker's subscribers are removed, so is the sentinel. this is done to make garbage collection easier. Assuming we want to avoid placing the sentinel in shared memory (for now -- it's a little tricky to debug), the owner of the worker pool must be the one to free said sentinel. But channels may be deleted by different worker processes, and it seems unwieldy (for now) to do IPC just to delete one stinkin' sentinel. Hence a new sentinel is used every time the subscriber queue is emptied. */ cur->subscriber_sentinel = NULL; //think about it it terms of garbage collection. it'll make sense. sort of. ngx_shmtx_unlock(&ngx_http_push_shpool->mutex); if(subscriber_sentinel != NULL) { if(worker_pid == ngx_pid) { //my subscribers ngx_http_push_respond_to_subscribers(channel, subscriber_sentinel, msg, status_code, status_line); } else { //some other worker's subscribers //interprocess communication breakdown if(ngx_http_push_send_worker_message(channel, subscriber_sentinel, worker_pid, worker_slot, msg, status_code, log) != NGX_ERROR) { ngx_http_push_alert_worker(worker_pid, worker_slot, log); } else { ngx_log_error(NGX_LOG_ERR, log, 0, "push module: error communicating with some other worker process"); } } } ngx_shmtx_lock(&ngx_http_push_shpool->mutex); } ngx_shmtx_unlock(&ngx_http_push_shpool->mutex); return received; }
static ngx_int_t ngx_http_push_broadcast_locked(ngx_http_push_channel_t *channel, ngx_http_push_msg_t *msg, ngx_int_t status_code, const ngx_str_t *status_line, ngx_log_t *log, ngx_slab_pool_t *shpool) { //subscribers are queued up in a local pool. Queue heads, however, are located //in shared memory, identified by pid. ngx_http_push_pid_queue_t *sentinel = &channel->workers_with_subscribers; ngx_http_push_pid_queue_t *cur = sentinel; ngx_int_t received = NGX_HTTP_PUSH_MESSAGE_QUEUED; while((cur=(ngx_http_push_pid_queue_t *)ngx_queue_next(&cur->queue))!=sentinel) { pid_t worker_pid = cur->pid; ngx_int_t worker_slot = cur->slot; ngx_http_push_subscriber_t *subscriber_sentinel= cur->subscriber_sentinel; received = NGX_HTTP_PUSH_MESSAGE_RECEIVED; if(msg!=NULL) { //we need a refcount because channel messages MAY be dequed before they are used up. It thus falls on the IPC stuff to free it. msg->refcount++; } ngx_shmtx_unlock(&shpool->mutex); if(worker_pid == ngx_pid) { //my subscribers ngx_http_push_respond_to_subscribers(channel, subscriber_sentinel, msg, status_code, status_line); } else { //some other worker's subscribers //interprocess communication breakdown if(ngx_http_push_send_worker_message(channel, subscriber_sentinel, worker_pid, worker_slot, msg, status_code, log) != NGX_ERROR) { ngx_http_push_alert_worker(worker_pid, worker_slot, log); } else { ngx_log_error(NGX_LOG_ERR, log, 0, "push module: error communicating with some other worker process"); } } ngx_shmtx_lock(&shpool->mutex); cur->subscriber_sentinel=NULL; //no messages here, no sir. } return received; }