Ejemplo n.º 1
0
static ngx_int_t spooler_respond_message(channel_spooler_t *self, nchan_msg_t *msg) {
  spooler_respond_data_t     srdata;
  subscriber_pool_t         *spool;
  ngx_int_t                  responded_subs = 0;
  
  srdata.min = msg->prev_id;
  srdata.max = msg->id;
  srdata.multi = msg->id.tagcount;
  srdata.overflow = NULL;
  srdata.msg = msg;
  srdata.n = 0;
  //find all spools between msg->prev_id and msg->id
  rbtree_conditional_walk(&self->spoolseed, (rbtree_walk_conditional_callback_pt )collect_spool_range_callback, &srdata);
  
  if(srdata.n == 0) {
    DBG("no spools in range %V -- ", msgid_to_str(&msg->prev_id));
    DBG(" -- %V", msgid_to_str(&msg->id));
  }
  
  while((spool = spoolcollector_unwind_nextspool(&srdata)) != NULL) {
    responded_subs += spool->sub_count;
    if(msg->id.tagcount > NCHAN_FIXED_MULTITAG_MAX) {
      assert(spool->id.tag.allocd != msg->id.tag.allocd);
    }
    spool_respond_general(spool, msg, 0, NULL, 0);
    if(msg->id.tagcount > NCHAN_FIXED_MULTITAG_MAX) {
      assert(spool->id.tag.allocd != msg->id.tag.allocd);
    }
    spool_nextmsg(spool, &msg->id);
  }
  
  spool = get_spool(self, &latest_msg_id);
  if(spool->sub_count > 0) {
#if NCHAN_BENCHMARK
    responded_subs += spool->sub_count;
#endif
    spool_respond_general(spool, msg, 0, NULL, 0);
    spool_nextmsg(spool, &msg->id);
  }

  nchan_copy_msg_id(&self->prev_msg_id, &msg->id, NULL);
  
#if NCHAN_BENCHMARK
  self->last_responded_subscriber_count = responded_subs;
#endif
  return NGX_OK;
}
Ejemplo n.º 2
0
static ngx_int_t spooler_add_subscriber(channel_spooler_t *self, subscriber_t *sub) {
  nchan_msg_id_t          *msgid = &sub->last_msg_id;
  subscriber_pool_t       *spool;
  
  if(self->want_to_stop) {
    ERR("Not accepting new subscribers right now. want to stop.");
    return NGX_ERROR;
  }
  
  spool = get_spool(self, msgid);
  
  assert(spool->id.time == msgid->time);
  
  if(spool == NULL) {
    return NGX_ERROR;
  }

  if(spool_add_subscriber(spool, sub, 1) != NGX_OK) {
    ERR("couldn't add subscriber to spool %p", spool);
    return NGX_ERROR;
  }
  
  if(self->add_handler != NULL) {
    self->add_handler(self, sub, self->add_handler_privdata);
  }
  else {
    ERR("SPOOLER %p has no add_handler, couldn't add subscriber %p", self, sub);
  }
  
  switch(spool->msg_status) {
    case MSG_FOUND:
      assert(spool->msg);
      spool_respond_general(spool, spool->msg, 0, NULL);
      break;
    
    case MSG_INVALID:
      assert(spool->msg == NULL);
      spool_fetch_msg(spool);
      break;
    
    case MSG_CHANNEL_NOTREADY:
    case MSG_PENDING:
    case MSG_EXPECTED:
      //nothing to do but wait
      break;
      
    case MSG_EXPIRED:
    case MSG_NOTFOUND:
      //shouldn't happen
      assert(0);
  }

  return NGX_OK;
}
Ejemplo n.º 3
0
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;
}
Ejemplo n.º 4
0
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;
}
Ejemplo n.º 5
0
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;
}
Ejemplo n.º 6
0
static ngx_int_t spooler_respond_generic(channel_spooler_t *self, nchan_msg_t *msg, ngx_int_t code, void *code_data, unsigned notice) {
  spooler_respond_generic_data_t  data = {self, msg, code, code_data, notice};
  rbtree_walk(&self->spoolseed, (rbtree_walk_callback_pt )spooler_respond_rbtree_node_spool, &data);
  spool_respond_general(&self->current_msg_spool, data.msg, data.code, data.code_data, notice);
  return NGX_OK;
}
Ejemplo n.º 7
0
static ngx_int_t spooler_respond_rbtree_node_spool(rbtree_seed_t *seed, subscriber_pool_t *spool, void *data) {
  spooler_respond_generic_data_t  *d = data;
  
  return spool_respond_general(spool, d->msg, d->code, d->code_data, d->notice);
}
Ejemplo n.º 8
0
static ngx_int_t spooler_add_subscriber(channel_spooler_t *self, subscriber_t *sub) {
  nchan_msg_id_t          *msgid = &sub->last_msgid;
  subscriber_pool_t       *spool;
  subscriber_type_t        subtype;
  
  if(self->want_to_stop) {
    ERR("Not accepting new subscribers right now. want to stop.");
    return NGX_ERROR;
  }
  
  //validate_spooler(self, "before add_subscriber");
  
  spool = get_spool(self, msgid);
  
  assert(spool->id.time == msgid->time);
  
  
  if(spool == NULL) {
    return NGX_ERROR;
  }

  subtype = sub->type;
  
  if(spool_add_subscriber(spool, sub, 1) != NGX_OK) {
    ERR("couldn't add subscriber to spool %p", spool);
    return NGX_ERROR;
  }
  self->handlers->add(self, sub, self->handlers_privdata);
  
  switch(spool->msg_status) {
    case MSG_FOUND:
      assert(spool->msg);
      spool_respond_general(spool, spool->msg, 0, NULL, 0);
      break;
    
    case MSG_INVALID:
      assert(spool->msg == NULL);
      spool_fetch_msg(spool);
      break;
    
    case MSG_CHANNEL_NOTREADY:
    case MSG_PENDING:
      //nothing to do
      break;
      
    case MSG_EXPECTED:
      //notify subscriber
      sub->fn->respond_status(sub, NGX_HTTP_NO_CONTENT, NULL);
      break;
      
    case MSG_EXPIRED:
    case MSG_NOTFOUND:
    case MSG_NORESPONSE:
      //shouldn't happen
      assert(0);
  }
  
  if(self->handlers->bulk_post_subscribe != NULL && subtype != INTERNAL) {
    self->handlers->bulk_post_subscribe(self, 1, self->handlers_privdata);
  }
  
  //validate_spooler(self, "after add_subscriber");
  
  return NGX_OK;
}
Ejemplo n.º 9
0
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;
}
Ejemplo n.º 10
0
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;
}