Ejemplo n.º 1
0
Archivo: rf233.c Proyecto: Idolf/tock
/* switch the radio on */
int on(void) {
  /* Check whether radio is in sleep */
  if (sleep_on) {
    /* Wake the radio. It'll move to TRX_OFF state */
    gpio_clear(SLP_PIN);
    delay_ms(1);
    printf("RF233: Wake from sleep\n");
    sleep_on = 0;
  }
  uint8_t state_now = rf233_status();
  if (state_now != STATE_PLL_ON &&
      state_now != STATE_TRX_OFF &&
      state_now != STATE_TX_ARET_ON) {
    PRINTF("RF233: Failed to turn radio on, state is %s.\n", state_str(state_now));
    return -1;
  }

  PRINTF("RF233: State is %s, transitioning to PLL_ON.\n", state_str(rf233_status()));
  radio_pll = false;
  RF233_COMMAND(TRXCMD_PLL_ON);
  wait_for(&radio_pll);
  delay_ms(1);
  PRINTF("RF233: State is %s, transitioning to RX_ON.\n", state_str(rf233_status()));
  /* go to RX_ON state */
  RF233_COMMAND(TRXCMD_RX_ON);
  radio_is_on = 1;
  PRINTF("RF233: Radio is on, state is %s.\n", state_str(rf233_status()));
  return 0;
}
Ejemplo n.º 2
0
static void state(struct mwSession *s, enum mwSessionState state,
		  gpointer info) {

  struct mwSessionHandler *sh;

  g_return_if_fail(s != NULL);
  g_return_if_fail(s->handler != NULL);

  if(mwSession_isState(s, state)) return;

  s->state = state;
  s->state_info = info;

  switch(state) {
  case mwSession_STOPPING:
  case mwSession_STOPPED:
    g_message("session state: %s (0x%08x)", state_str(state),
	      GPOINTER_TO_UINT(info));
    break;

  case mwSession_LOGIN_REDIR:
    g_message("session state: %s (%s)", state_str(state),
	      (char *)info);
    break;

  default:
    g_message("session state: %s", state_str(state));
  }

  sh = s->handler;
  if(sh && sh->on_stateChange)
    sh->on_stateChange(s, state, info);
}
Ejemplo n.º 3
0
static void state(struct mwSession *s, enum mwSessionState state,
		  gpointer info) {

  struct mwSessionHandler *sh;

  g_return_if_fail(s != NULL);
  g_return_if_fail(s->handler != NULL);

  if(mwSession_isState(s, state)) return;

  s->state = state;
  s->state_info = info;

  switch(state) {
  case mwSession_STOPPING:
  case mwSession_STOPPED:
    g_message("session state: %s (0x%08x)", state_str(state),
	      GPOINTER_TO_UINT(info));
    break;

  case mwSession_LOGIN_REDIR:
	/// Miranda NG adaptation - start - https://developer.pidgin.im/ticket/7563#comment:4
    //g_message("session state: %s (%s)", state_str(state), (char *)info);
    g_message("session state: %s (%s)", state_str(state), NSTR((char *)info));
	/// Miranda NG adaptation - end
    break;

  default:
    g_message("session state: %s", state_str(state));
  }

  sh = s->handler;
  if(sh && sh->on_stateChange)
    sh->on_stateChange(s, state, info);
}
Ejemplo n.º 4
0
int fsm_transition(fsm_t *fsmp, event_t event) {
  int err = 0;
  printf("%s x %s -> %s\n", state_str(fsmp->state), event_str(event),
         state_str(transition_table[fsmp->state][event]));
  fsmp->state = transition_table[fsmp->state][event];
  return err;
}
Ejemplo n.º 5
0
void mwSession_free(struct mwSession *s) {
  struct mwSessionHandler *h;

  g_return_if_fail(s != NULL);

  if(! mwSession_isStopped(s)) {
    g_debug("session is not stopped (state: %s), proceeding with free",
	    state_str(s->state));
  }

  h = s->handler;
  if(h && h->clear) h->clear(s);
  s->handler = NULL;

  session_buf_free(s);

  mwChannelSet_free(s->channels);
  g_hash_table_destroy(s->services);
  g_hash_table_destroy(s->ciphers);
  g_hash_table_destroy(s->attributes);

  mwLoginInfo_clear(&s->login);
  mwUserStatus_clear(&s->status);
  mwPrivacyInfo_clear(&s->privacy);

  g_free(s);
}
Ejemplo n.º 6
0
void HttpProxy::ProxyConnection::writeSome()
{
	TRACE("writeSome() - %s (%d)", state_str(), request_->contentAvailable());

	ssize_t rv = backend_->write(writeBuffer_.data() + writeOffset_, writeBuffer_.size() - writeOffset_);

	if (rv > 0) {
		TRACE("write request: %ld (of %ld) bytes", rv, writeBuffer_.size() - writeOffset_);

		writeOffset_ += rv;
		writeProgress_ += rv;

		if (writeOffset_ == writeBuffer_.size()) {
			TRACE("writeOffset == writeBuffser.size (%ld) p:%ld, ca: %d, clr:%ld", writeOffset_,
				writeProgress_, request_->contentAvailable(), request_->connection.contentLength());

			writeOffset_ = 0;
			writeBuffer_.clear();
			backend_->setMode(Socket::Read);
		}
	} else {
		TRACE("write request failed(%ld): %s", rv, strerror(errno));
		close();
	}
}
Ejemplo n.º 7
0
static void state(struct mwChannel *chan, enum mwChannelState state,
		  guint32 err_code) {

  g_return_if_fail(chan != NULL);

  if(chan->state == state) return;

  chan->state = state;

  if(err_code) {
    g_message("channel 0x%08x state: %s (0x%08x)",
	      chan->id, state_str(state), err_code);
  } else {
    g_message("channel 0x%08x state: %s", chan->id, state_str(state));
  }
}
Ejemplo n.º 8
0
Archivo: rf233.c Proyecto: Idolf/tock
/**
 * \brief      Transmit a frame already put in the radio with 'prepare'
 * \param payload_len    Length of the frame to send
 * \return     Returns success/fail, refer to radio.h for explanation
 */
int rf233_transmit() {
  static uint8_t status_now;

  status_now = rf233_status();
  PRINTF("RF233: attempting transmit, in state %s\n", state_str(status_now));

  if (status_now == STATE_BUSY_RX_AACK ||
      status_now == STATE_BUSY_TX_ARET) {
    PRINTF("RF233: collision, was in state %s\n", state_str(status_now));
    /* NOTE: to avoid loops */
    return RADIO_TX_ERR;;
  }
  
  if (status_now != STATE_PLL_ON) {
    trx_reg_write(RF233_REG_TRX_STATE, STATE_PLL_ON);
    do { // I think this code is broken, does nothing -pal
      status_now = trx_bit_read(RF233_REG_TRX_STATUS, 0x1F, 0);
    } while (status_now == 0x1f);
  }
  
  if (rf233_status() != STATE_PLL_ON) {
    /* failed moving into PLL_ON state, gracefully try to recover */
    PRINTF("RF233: failed going to STATE_PLL_ON\n");
    RF233_COMMAND(TRXCMD_PLL_ON);   /* try again */
    static uint8_t state;
    state = rf233_status();
    if(state != STATE_PLL_ON) {
      PRINTF("RF233: graceful recovery (in tx) failed, giving up. State: 0x%02X\n", rf233_status());
      return RADIO_TX_ERR;
    }
  }
  
  /* perform transmission */
  flag_transmit = 1;
  radio_tx = false;
  RF233_COMMAND(TRXCMD_TX_ARET_ON);
  RF233_COMMAND(TRXCMD_TX_START);

  PRINTF("RF233:: Issued TX_START, wait for completion interrupt.\n");
  wait_for(&radio_tx);
  PRINTF("RF233: tx ok\n\n");
  
  return RADIO_TX_OK;
}
Ejemplo n.º 9
0
void sip_trans::dump() const
{
    DBG("type=%s (0x%x); msg=%p; to_tag=%.*s;"
	" reply_status=%i; state=%s (%i); retr_buf=%p; timers [%s,%s,%s]\n",
	type_str(),type,msg,to_tag.len,to_tag.s,
	reply_status,state_str(),state,retr_buf,
	timers[0]==NULL?"none":timer_name(timers[0]->type),
	timers[1]==NULL?"none":timer_name(timers[1]->type),
	timers[2]==NULL?"none":timer_name(timers[2]->type));
}
Ejemplo n.º 10
0
/** processes a (partial) request from buffer's given \p offset of \p count bytes.
 */
bool HttpConnection::process()
{
	TRACE(2, "process: offset=%lu, size=%lu (before processing) %s, %s", inputOffset_, input_.size(), state_str(), status_str());

	while (state() != MESSAGE_BEGIN || status() == ReadingRequest || status() == KeepAliveRead) {
		BufferRef chunk(input_.ref(inputOffset_));
		if (chunk.empty())
			break;

		// ensure status is up-to-date, in case we came from keep-alive-read
		if (status_ == KeepAliveRead) {
			TRACE(1, "process: status=keep-alive-read, resetting to reading-request");
			setStatus(ReadingRequest);
			if (request_->isFinished()) {
				TRACE(1, "process: finalizing request");
				request_->finalize();
			}
		}

		TRACE(1, "process: (size: %lu, isHandlingRequest:%d, state:%s status:%s", chunk.size(), isHandlingRequest(), state_str(), status_str());
		//TRACE(1, "%s", input_.ref(input_.size() - rv).str().c_str());

		size_t rv = HttpMessageProcessor::process(chunk, &inputOffset_);
		TRACE(1, "process: done process()ing; fd=%d, request=%p state:%s status:%s, rv:%d", socket_->handle(), request_, state_str(), status_str(), rv);

		if (isAborted()) {
			TRACE(1, "abort detected");
			return false;
		}

		if (state() == SYNTAX_ERROR) {
			TRACE(1, "syntax error detected");
			if (!request_->isFinished()) {
				abort(HttpStatus::BadRequest);
			}
			TRACE(1, "syntax error detected: leaving process()");
			return false;
		}

		if (rv < chunk.size()) {
			request_->log(Severity::debug1, "parser aborted early.");
			return false;
		}
	}

	TRACE(1, "process: offset=%lu, bs=%lu, state=%s (after processing) io.timer:%d",
			inputOffset_, input_.size(), state_str(), socket_->timerActive());

	return true;
}
Ejemplo n.º 11
0
/*
 * audit_changestate()
 *
 *	Audit a 'changestate' door command.
 */
static void
audit_changestate(ucred_t *ucred, char *auth, char *path, char *connection,
                  int new_state, int old_state, int result)
{
    adt_session_data_t	*session;
    adt_event_data_t	*event;
    int			pass_fail, fail_reason;

    if (audit_session(ucred, &session) != 0)
        return;

    if ((event = adt_alloc_event(session, ADT_hotplug_state)) == NULL) {
        (void) adt_end_session(session);
        return;
    }

    if (result == 0) {
        pass_fail = ADT_SUCCESS;
        fail_reason = ADT_SUCCESS;
    } else {
        pass_fail = ADT_FAILURE;
        fail_reason = result;
    }

    event->adt_hotplug_state.auth_used = auth;
    event->adt_hotplug_state.device_path = path;
    event->adt_hotplug_state.connection = connection;
    event->adt_hotplug_state.new_state = state_str(new_state);
    event->adt_hotplug_state.old_state = state_str(old_state);

    /* Put the event */
    if (adt_put_event(event, pass_fail, fail_reason) != 0)
        log_err("Cannot put audit event.\n");

    adt_free_event(event);
    (void) adt_end_session(session);
}
void AIStateMachine::idle(state_type current_run_state)
{
  DoutEntering(dc::statemachine, "AIStateMachine::idle(" << state_str(current_run_state) << ") [" << (void*)this << "]");
  llassert(is_main_thread());
  llassert(!mIdle);
  mSetStateLock.lock();
  // Only go idle if the run state is (still) what we expect it to be.
  // Otherwise assume that another thread called set_state() and continue running.
  if (current_run_state == mRunState)
  {
	mIdle = true;
	mSleep = 0;
  }
  mSetStateLock.unlock();
}
Ejemplo n.º 13
0
/** processes a (partial) request from buffer's given \p offset of \p count bytes.
 */
bool HttpConnection::process()
{
	TRACE("process: offset=%ld, size=%ld (before processing)", inputOffset_, input_.size());

	while (state() != MESSAGE_BEGIN || status() == ReadingRequest || status() == KeepAliveRead) {
		BufferRef chunk(input_.ref(inputOffset_));
		if (chunk.empty())
			break;

		// ensure status is up-to-date, in case we came from keep-alive-read
		if (status_ == KeepAliveRead) {
			TRACE("process: status=keep-alive-read, resetting to reading-request");
			status_ = ReadingRequest;
			if (request_->isFinished()) {
				TRACE("process: finalizing request");
				request_->finalize();
			}
		}

		TRACE("process: (size: %ld, isHandlingRequest:%d, state:%s status:%s", chunk.size(), (flags_ & IsHandlingRequest) != 0, state_str(), status_str());
		//TRACE("%s", input_.ref(input_.size() - rv).str().c_str());

		HttpMessageProcessor::process(chunk, &inputOffset_);
		TRACE("process: done process()ing; fd=%d, request=%p state:%s status:%s", socket_->handle(), request_, state_str(), status_str());

		if (isAborted())
			return false;

		if (state() == SYNTAX_ERROR) {
			if (!request_->isFinished()) {
				setShouldKeepAlive(false);
				request_->status = HttpError::BadRequest;
				request_->finish();
			}
			return false;
		}
	}

	TRACE("process: offset=%ld, bs=%ld, state=%s (after processing) io.timer:%d",
			inputOffset_, input_.size(), state_str(), socket_->timerActive());

	return true;
}
Ejemplo n.º 14
0
void HttpProxy::ProxyConnection::readSome()
{
	TRACE("readSome() - %s", state_str());

	std::size_t lower_bound = readBuffer_.size();

	if (lower_bound == readBuffer_.capacity())
		readBuffer_.setCapacity(lower_bound + 4096);

	ssize_t rv = backend_->read(readBuffer_);

	if (rv > 0) {
		TRACE("read response: %ld bytes", rv);
		std::size_t np = process(readBuffer_.ref(lower_bound, rv));
		(void) np;
		TRACE("readSome(): process(): %ld / %ld", np, rv);

		if (processingDone_ || state() == SYNTAX_ERROR) {
			close();
		} else {
			TRACE("resume with io:%d, state:%s", backend_->mode(), backend_->state_str());
			backend_->setMode(Socket::Read);
		}
	} else if (rv == 0) {
		TRACE("http server connection closed");
		close();
	} else {
		TRACE("read response failed(%ld): %s", rv, strerror(errno));

		switch (errno) {
#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
		case EWOULDBLOCK:
#endif
		case EAGAIN:
		case EINTR:
			backend_->setMode(Socket::Read);
			break;
		default:
			close();
			break;
		}
	}
}
Ejemplo n.º 15
0
Archivo: rf233.c Proyecto: Idolf/tock
int rf233_prepare(const void *payload, unsigned short payload_len) {
  int i;
  uint8_t templen;
  uint8_t radio_status;
  uint8_t data[130];

  /* Add length of the FCS (2 bytes) */
  templen = payload_len + 2; 
  data[0] = templen;
  for (i = 0; i < templen; i++) {
    data[i + 1] = ((uint8_t*)payload)[i];
  }
  data[3] = (uint8_t)(counter & 0xff);
  counter++;

#if DEBUG_PRINTDATA
  PRINTF("RF233 prepare (%u/%u): 0x", payload_len, templen);
  for(i = 0; i < templen; i++) {
    PRINTF("%02x", *(uint8_t *)(payload + i));
  }
  PRINTF("\n");
#endif  /* DEBUG_PRINTDATA */
   
  PRINTF("RF233: prepare %u\n", payload_len);
  if(payload_len > MAX_PACKET_LEN) {
    PRINTF("RF233: error, frame too large to tx\n");
    return RADIO_TX_ERR;
  }

  /* check that the FIFO is clear to access */
  radio_status = rf233_status();
  if (radio_status == STATE_BUSY_RX_AACK ||
      radio_status == STATE_BUSY_RX || 
      radio_status == STATE_BUSY_TX_ARET) {
    PRINTF("RF233: TRX buffer unavailable: prep when state %s\n", state_str(radio_status));
    return RADIO_TX_ERR;
  }

  /* Write packet to TX FIFO. */
  PRINTF("RF233: sqno: %02x len = %u\n", counter, payload_len);
  trx_frame_write((uint8_t *)data, templen+1);
  return RADIO_TX_OK;
}
Ejemplo n.º 16
0
AIStateMachine::state_type AIStateMachine::begin_loop(base_state_type base_state)
{
  DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::begin_loop(" << state_str(base_state) << ") [" << (void*)this << "]");

  sub_state_type_wat sub_state_w(mSubState);
  // Honor a subsequent call to idle() (only necessary in bs_multiplex, but it doesn't hurt to reset this flag in other states too).
  sub_state_w->skip_idle = false;
  // Mark that we're about to honor all previous run requests.
  sub_state_w->need_run = false;
  // Honor previous calls to advance_state() (once run_state is initialized).
  if (base_state == bs_multiplex && sub_state_w->advance_state > sub_state_w->run_state)
  {
	Dout(dc::statemachine(mSMDebug), "Copying advance_state to run_state, because it is larger [" << state_str_impl(sub_state_w->advance_state) << " > " << state_str_impl(sub_state_w->run_state) << "]");
	sub_state_w->run_state = sub_state_w->advance_state;
  }
#ifdef SHOW_ASSERT
  else
  {
	// If advance_state wasn't honored then it isn't a reason to run.
	// We're running anyway, but that should be because set_state() was called.
	mDebugAdvanceStatePending = false;
  }
#endif
  sub_state_w->advance_state = 0;

#ifdef SHOW_ASSERT
  // Mark that we're running the loop.
  mThreadId.reset();
  // This point marks handling cont().
  mDebugShouldRun |= mDebugContPending;
  mDebugContPending = false;
  // This point also marks handling advance_state().
  mDebugShouldRun |= mDebugAdvanceStatePending;
  mDebugAdvanceStatePending = false;
#endif

  // Make a copy of the state that we're about to run.
  return sub_state_w->run_state;
}
Ejemplo n.º 17
0
void mwChannel_recvAccept(struct mwChannel *chan,
			  struct mwMsgChannelAccept *msg) {

  struct mwService *srvc;

  g_return_if_fail(chan != NULL);
  g_return_if_fail(msg != NULL);
  g_return_if_fail(chan->id == msg->head.channel);

  if(mwChannel_isIncoming(chan)) {
    g_warning("channel 0x%08x not an outgoing channel", chan->id);
    mwChannel_destroy(chan, ERR_REQUEST_INVALID, NULL);
    return;
  }

  if(chan->state != mwChannel_WAIT) {
    g_warning("channel 0x%08x state not WAIT: %s",
	      chan->id, state_str(chan->state));
    mwChannel_destroy(chan, ERR_REQUEST_INVALID, NULL);
    return;
  }

  mwLoginInfo_clone(&chan->user, &msg->acceptor);

  srvc = mwSession_getService(chan->session, chan->service);
  if(! srvc) {
    g_warning("no service: 0x%08x", chan->service);
    mwChannel_destroy(chan, ERR_SERVICE_NO_SUPPORT, NULL);
    return;
  }

  chan->policy = msg->encrypt.mode;
  g_message("channel accepted with encrypt policy 0x%04x", chan->policy);

  if(! msg->encrypt.mode || ! msg->encrypt.item) {
    /* no mode or no item means no encryption */
    mwChannel_selectCipherInstance(chan, NULL);

  } else {
    guint16 cid = msg->encrypt.item->id;
    struct mwCipherInstance *ci = get_supported(chan, cid);

    if(! ci) {
      g_warning("not an offered cipher: 0x%04x", cid);
      mwChannel_destroy(chan, ERR_REQUEST_INVALID, NULL);
      return;
    }

    mwCipherInstance_accepted(ci, msg->encrypt.item);
    mwChannel_selectCipherInstance(chan, ci);
  }

  /* mark it as open for the service */
  state(chan, mwChannel_OPEN, 0);

  /* let the service know */
  mwService_recvAccept(srvc, chan, msg);

  /* flush it if the service didn't just immediately close it */
  if(mwChannel_isState(chan, mwChannel_OPEN)) {
    channel_open(chan);
  }
}
Ejemplo n.º 18
0
Archivo: rf233.c Proyecto: Idolf/tock
/**
 * \brief      switch the radio on to listen (rx) mode 
 * \retval 0   Success
 */
int rf233_on(void) {
  PRINTF("RF233: turning on from state %s\n  - sleeping radio will be POWER_ON since it doesn't respond to SPI and 0x0 is POWER_ON", state_str(rf233_status()));
  on();
  return 0;
}
Ejemplo n.º 19
0
void HttpProxy::ProxyConnection::onWriteComplete()
{
	TRACE("chunk write complete: %s", state_str());
	backend_->setMode(Socket::Read);
	unref();
}
Ejemplo n.º 20
0
Archivo: rf233.c Proyecto: Idolf/tock
/**
 * \brief      switch the radio off 
 * \retval 0   Success
 */
int rf233_off(void) {
  PRINTF("RF233: turning off from state %s\n", state_str(rf233_status()));
  off();
  return 0;
}
Ejemplo n.º 21
0
/** processes a message-chunk.
 *
 * \param chunk   the chunk of bytes to process
 *
 * \return        number of bytes actually parsed and processed
 */
std::size_t HttpMessageProcessor::process(const BufferRef& chunk, size_t* out_nparsed)
{
	/*
	 * CR               = 0x0D
	 * LF               = 0x0A
	 * SP               = 0x20
	 * HT               = 0x09
	 *
	 * CRLF             = CR LF
	 * LWS              = [CRLF] 1*( SP | HT )
	 *
	 * HTTP-message     = Request | Response
	 *
	 * generic-message  = start-line
	 *                    *(message-header CRLF)
	 *                    CRLF
	 *                    [ message-body ]
	 *                 
	 * start-line       = Request-Line | Status-Line
	 *
	 * Request-Line     = Method SP Request-URI SP HTTP-Version CRLF
	 *
	 * Method           = "OPTIONS" | "GET" | "HEAD"
	 *                  | "POST"    | "PUT" | "DELETE"
	 *                  | "TRACE"   | "CONNECT"
	 *                  | extension-method
	 *
	 * Request-URI      = "*" | absoluteURI | abs_path | authority
	 *
	 * extension-method = token
	 *
	 * Status-Line      = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
	 *
	 * HTTP-Version     = "HTTP" "/" 1*DIGIT "." 1*DIGIT
	 * Status-Code      = 3*DIGIT
	 * Reason-Phrase    = *<TEXT, excluding CR, LF>
	 *
	 * absoluteURI      = "http://" [user ':'******'@'] hostname [abs_path] [qury]
	 * abs_path         = "/" *CHAR
	 * authority        = ...
	 * token            = 1*<any CHAR except CTLs or seperators>
	 * separator        = "(" | ")" | "<" | ">" | "@"
	 *                  | "," | ";" | ":" | "\" | <">
	 *                  | "/" | "[" | "]" | "?" | "="
	 *                  | "{" | "}" | SP | HT
	 *
	 * message-header   = field-name ":" [ field-value ]
	 * field-name       = token
	 * field-value      = *( field-content | LWS )
	 * field-content    = <the OCTETs making up the field-value
	 *                    and consisting of either *TEXT or combinations
	 *                    of token, separators, and quoted-string>
	 *
	 * message-body     = entity-body
	 *                  | <entity-body encoded as per Transfer-Encoding>
	 */

	const char* i = chunk.cbegin();
	const char* e = chunk.cend();

	const size_t initialOutOffset = out_nparsed ? *out_nparsed : 0;
	size_t result = initialOutOffset;
	size_t* nparsed = out_nparsed ? out_nparsed : &result;

	//TRACE(2, "process(curState:%s): size: %ld: '%s'", state_str(), chunk.size(), chunk.str().c_str());
	TRACE(2, "process(curState:%s): size: %ld", state_str(), chunk.size());

#if 0
	switch (state_) {
		case CONTENT: // fixed size content
			if (!passContent(chunk, nparsed))
				goto done;

			i += *nparsed;
			break;
		case CONTENT_ENDLESS: // endless-sized content (until stream end)
		{
			*nparsed += chunk.size();
			bool rv = filters_.empty()
				? onMessageContent(chunk)
				: onMessageContent(filters_.process(chunk));

			goto done;
		}
		default:
			break;
	}
#endif

	while (i != e) {
#if !defined(XZERO_NDEBUG)
		if (std::isprint(*i)) {
			TRACE(3, "parse: %4ld, 0x%02X (%c),  %s", *nparsed, *i, *i, state_str());
		} else {
			TRACE(3, "parse: %4ld, 0x%02X,     %s", *nparsed, *i, state_str());
		}
#endif

		switch (state_) {
			case MESSAGE_BEGIN:
				contentLength_ = -1;
				switch (mode_) {
					case REQUEST:
						state_ = REQUEST_LINE_BEGIN;
						versionMajor_ = 0;
						versionMinor_ = 0;
						break;
					case RESPONSE:
						state_ = STATUS_LINE_BEGIN;
						code_ = 0;
						versionMajor_ = 0;
						versionMinor_ = 0;
						break;
					case MESSAGE:
						state_ = HEADER_NAME_BEGIN;

						// an internet message has no special top-line,
						// so we just invoke the callback right away
						if (!onMessageBegin())
							goto done;

						break;
				}
				break;
			case REQUEST_LINE_BEGIN:
				if (isToken(*i)) {
					state_ = REQUEST_METHOD;
					method_ = chunk.ref(*nparsed - initialOutOffset, 1);

					++*nparsed;
					++i;
				} else {
					state_ = SYNTAX_ERROR;
				}
				break;
			case REQUEST_METHOD:
				if (*i == SP) {
					state_ = REQUEST_ENTITY_BEGIN;
					++*nparsed;
					++i;
				} else if (!isToken(*i)) {
					state_ = SYNTAX_ERROR;
				} else {
					method_.shr();
					++*nparsed;
					++i;
				}
				break;
			case REQUEST_ENTITY_BEGIN:
				if (std::isprint(*i)) {
					entity_ = chunk.ref(*nparsed - initialOutOffset, 1);
					state_ = REQUEST_ENTITY;

					++*nparsed;
					++i;
				}
				else {
					state_ = SYNTAX_ERROR;
				}
				break;
			case REQUEST_ENTITY:
				if (*i == SP) {
					state_ = REQUEST_PROTOCOL_BEGIN;
					++*nparsed;
					++i;
				} else if (std::isprint(*i)) {
					entity_.shr();
					++*nparsed;
					++i;
				} else
					state_ = SYNTAX_ERROR;
				break;
			case REQUEST_PROTOCOL_BEGIN:
				if (*i != 'H') {
					state_ = SYNTAX_ERROR;
				} else {
					state_ = REQUEST_PROTOCOL_T1;
					++*nparsed;
					++i;
				}
				break;
			case REQUEST_PROTOCOL_T1:
				if (*i != 'T') {
					state_ = SYNTAX_ERROR;
				} else {
					state_ = REQUEST_PROTOCOL_T2;
					++*nparsed;
					++i;
				}
				break;
			case REQUEST_PROTOCOL_T2:
				if (*i != 'T') {
					state_ = SYNTAX_ERROR;
				} else {
					state_ = REQUEST_PROTOCOL_P;
					++*nparsed;
					++i;
				}
				break;
			case REQUEST_PROTOCOL_P:
				if (*i != 'P') {
					state_ = SYNTAX_ERROR;
				} else {
					state_ = REQUEST_PROTOCOL_SLASH;
					++*nparsed;
					++i;
				}
				break;
			case REQUEST_PROTOCOL_SLASH:
				if (*i != '/') {
					state_ = SYNTAX_ERROR;
				} else {
					state_ = REQUEST_PROTOCOL_VERSION_MAJOR;
					++*nparsed;
					++i;
				}
				break;
			case REQUEST_PROTOCOL_VERSION_MAJOR:
				if (*i == '.') {
					state_ = REQUEST_PROTOCOL_VERSION_MINOR;
					++*nparsed;
					++i;
				}
				else if (!std::isdigit(*i)) {
					state_ = SYNTAX_ERROR;
				} else {
					versionMajor_ = versionMajor_ * 10 + *i - '0';
					++*nparsed;
					++i;
				}
				break;
			case REQUEST_PROTOCOL_VERSION_MINOR:
				if (*i == CR) {
					state_ = REQUEST_LINE_LF;
					++*nparsed;
					++i;
				}
#if defined(X0_HTTP_SUPPORT_SHORT_LF)
				else if (*i == LF) {
					state_ = HEADER_NAME_BEGIN;
					++*nparsed;
					++i;

					TRACE(2, "request-line: method=%s, entity=%s, vmaj=%d, vmin=%d",
							method_.str().c_str(), entity_.str().c_str(), versionMajor_, versionMinor_);

					if (!onMessageBegin(method_, entity_, versionMajor_, versionMinor_)) {
						goto done;
					}
				}
#endif
				else if (!std::isdigit(*i)) {
					state_ = SYNTAX_ERROR;
				} else {
					versionMinor_ = versionMinor_ * 10 + *i - '0';
					++*nparsed;
					++i;
				}
				break;
			case REQUEST_LINE_LF:
				if (*i == LF) {
					state_ = HEADER_NAME_BEGIN;
					++*nparsed;
					++i;

					TRACE(2, "request-line: method=%s, entity=%s, vmaj=%d, vmin=%d",
							method_.str().c_str(), entity_.str().c_str(), versionMajor_, versionMinor_);

					if (!onMessageBegin(method_, entity_, versionMajor_, versionMinor_)) {
						goto done;
					}
				}
				else
					state_ = SYNTAX_ERROR;
				break;
			case STATUS_LINE_BEGIN:
			case STATUS_PROTOCOL_BEGIN:
				if (*i != 'H') {
					state_ = SYNTAX_ERROR;
				} else {
					state_ = STATUS_PROTOCOL_T1;
					++*nparsed;
					++i;
				}
				break;
			case STATUS_PROTOCOL_T1:
				if (*i != 'T') {
					state_ = SYNTAX_ERROR;
				} else {
					state_ = STATUS_PROTOCOL_T2;
					++*nparsed;
					++i;
				}
				break;
			case STATUS_PROTOCOL_T2:
				if (*i != 'T') {
					state_ = SYNTAX_ERROR;
				} else {
					state_ = STATUS_PROTOCOL_P;
					++*nparsed;
					++i;
				}
				break;
			case STATUS_PROTOCOL_P:
				if (*i != 'P') {
					state_ = SYNTAX_ERROR;
				} else {
					state_ = STATUS_PROTOCOL_SLASH;
					++*nparsed;
					++i;
				}
				break;
			case STATUS_PROTOCOL_SLASH:
				if (*i != '/') {
					state_ = SYNTAX_ERROR;
				} else {
					state_ = STATUS_PROTOCOL_VERSION_MAJOR;
					++*nparsed;
					++i;
				}
				break;
			case STATUS_PROTOCOL_VERSION_MAJOR:
				if (*i == '.') {
					state_ = STATUS_PROTOCOL_VERSION_MINOR;
					++*nparsed;
					++i;
				} else if (!std::isdigit(*i)) {
					state_ = SYNTAX_ERROR;
				} else {
					versionMajor_ = versionMajor_ * 10 + *i - '0';
					++*nparsed;
					++i;
				}
				break;
			case STATUS_PROTOCOL_VERSION_MINOR:
				if (*i == SP) {
					state_ = STATUS_CODE_BEGIN;
					++*nparsed;
					++i;
				} else if (!std::isdigit(*i)) {
					state_ = SYNTAX_ERROR;
				} else {
					versionMinor_ = versionMinor_ * 10 + *i - '0';
					++*nparsed;
					++i;
				}
				break;
			case STATUS_CODE_BEGIN:
				if (!std::isdigit(*i)) {
					code_ = SYNTAX_ERROR;
					break;
				}
				state_ = STATUS_CODE;
				/* fall through */
			case STATUS_CODE:
				if (std::isdigit(*i)) {
					code_ = code_ * 10 + *i - '0';
					++*nparsed;
					++i;
				} else if (*i == SP) {
					state_ = STATUS_MESSAGE_BEGIN;
					++*nparsed;
					++i;
				} else if (*i == CR) { // no Status-Message passed
					state_ = STATUS_MESSAGE_LF;
					++*nparsed;
					++i;
				} else {
					state_ = SYNTAX_ERROR;
				}
				break;
			case STATUS_MESSAGE_BEGIN:
				if (isText(*i)) {
					state_ = STATUS_MESSAGE;
					message_ = chunk.ref(*nparsed - initialOutOffset, 1);
					++*nparsed;
					++i;
				}
				else
					state_ = SYNTAX_ERROR;
				break;
			case STATUS_MESSAGE:
				if (isText(*i) && *i != CR && *i != LF) {
					message_.shr();
					++*nparsed;
					++i;
				} else if (*i == CR) {
					state_ = STATUS_MESSAGE_LF;
					++*nparsed;
					++i;
				} else {
					state_ = SYNTAX_ERROR;
				}
				break;
			case STATUS_MESSAGE_LF:
				if (*i == LF) {
					state_ = HEADER_NAME_BEGIN;
					++*nparsed;
					++i;

					//TRACE(2, "status-line: HTTP/%d.%d, code=%d, message=%s", versionMajor_, versionMinor_, code_, message_.str().c_str());
					if (!onMessageBegin(versionMajor_, versionMinor_, code_, message_)) {
						goto done;
					}
				} else
					state_ = SYNTAX_ERROR;
				break;
			case HEADER_NAME_BEGIN:
				if (isToken(*i)) {
					name_ = chunk.ref(*nparsed - initialOutOffset, 1);
					state_ = HEADER_NAME;
					++*nparsed;
					++i;
				} else if (*i == CR) {
					state_ = HEADER_END_LF;
					++*nparsed;
					++i;
				}
#if defined(X0_HTTP_SUPPORT_SHORT_LF)
				else if (*i == LF)
					state_ = HEADER_END_LF;
#endif
				else
					state_ = SYNTAX_ERROR;
				break;
			case HEADER_NAME:
				if (isToken(*i)) {
					name_.shr();
					++*nparsed;
					++i;
				} else if (*i == ':') {
					state_ = LWS_BEGIN;
					lwsNext_ = HEADER_VALUE_BEGIN;
					lwsNull_ = HEADER_VALUE_END; // only (CR LF) parsed, assume empty value & go on with next header
					++*nparsed;
					++i;
				} else if (*i == CR) {
					state_ = LWS_LF;
					lwsNext_ = HEADER_COLON;
					lwsNull_ = SYNTAX_ERROR;
					++*nparsed;
					++i;
				} else
					state_ = SYNTAX_ERROR;
				break;
			case HEADER_COLON:
				if (*i == ':') {
					state_ = LWS_BEGIN;
					lwsNext_ = HEADER_VALUE_BEGIN;
					lwsNull_ = HEADER_VALUE_END;
					++*nparsed;
					++i;
				} else
					state_ = SYNTAX_ERROR;
				break;
			case LWS_BEGIN:
				if (*i == CR) {
					state_ = LWS_LF;
					++*nparsed;
					++i;
#if defined(X0_HTTP_SUPPORT_SHORT_LF)
				} else if (*i == LF) {
					state_ = LWS_SP_HT_BEGIN;
					++*nparsed;
					++i;
#endif
				} else if (*i == SP || *i == HT) {
					state_ = LWS_SP_HT;
					++*nparsed;
					++i;
				} else if (std::isprint(*i)) {
					state_ = lwsNext_;
				} else
					state_ = SYNTAX_ERROR;
				break;
			case LWS_LF:
				if (*i == LF) {
					state_ = LWS_SP_HT_BEGIN;
					++*nparsed;
					++i;
				} else
					state_ = SYNTAX_ERROR;
				break;
			case LWS_SP_HT_BEGIN:
				if (*i == SP || *i == HT) {
					if (!value_.empty())
						value_.shr(3); // CR LF (SP | HT)

					state_ = LWS_SP_HT;
					++*nparsed;
					++i;
				} else {
					// only (CF LF) parsed so far and no 1*(SP | HT) found.
					state_ = lwsNull_;
					// XXX no nparsed/i-update
				}
				break;
			case LWS_SP_HT:
				if (*i == SP || *i == HT) {
					if (!value_.empty())
						value_.shr();

					++*nparsed;
					++i;
				} else
					state_ = lwsNext_;
				break;
			case HEADER_VALUE_BEGIN:
				if (isText(*i)) {
					value_ = chunk.ref(*nparsed - initialOutOffset, 1);
					++*nparsed;
					++i;
					state_ = HEADER_VALUE;
				} else if (*i == CR) {
					state_ = HEADER_VALUE_LF;
					++*nparsed;
					++i;
#if defined(X0_HTTP_SUPPORT_SHORT_LF)
				} else if (*i == LF) {
					state_ = HEADER_VALUE_END;
					++*nparsed;
					++i;
#endif
				} else {
					state_ = SYNTAX_ERROR;
				}
				break;
			case HEADER_VALUE:
				if (*i == CR) {
					state_ = LWS_LF;
					lwsNext_ = HEADER_VALUE;
					lwsNull_ = HEADER_VALUE_END;
					++*nparsed;
					++i;
				}
#if defined(X0_HTTP_SUPPORT_SHORT_LF)
				else if (*i == LF) {
					state_ = LWS_SP_HT_BEGIN;
					lwsNext_ = HEADER_VALUE;
					lwsNull_ = HEADER_VALUE_END;
					++*nparsed;
					++i;
				}
#endif
				else if (isText(*i)) {
					value_.shr();
					++*nparsed;
					++i;
				} else
					state_ = SYNTAX_ERROR;
				break;
			case HEADER_VALUE_LF:
				if (*i == LF) {
					state_ = HEADER_VALUE_END;
					++*nparsed;
					++i;
				} else {
					state_ = SYNTAX_ERROR;
				}
				break;
			case HEADER_VALUE_END: {
				TRACE(2, "header: name='%s', value='%s'", name_.str().c_str(), value_.str().c_str());

				if (iequals(name_, "Content-Length")) {
					contentLength_ = value_.toInt();
					TRACE(2, "set content length to: %ld", contentLength_);
				} else if (iequals(name_, "Transfer-Encoding")) {
					if (iequals(value_, "chunked")) {
						chunked_ = true;
					}
				}

				bool rv = onMessageHeader(name_, value_);
				name_.clear();
				value_.clear();

				// continue with the next header
				state_ = HEADER_NAME_BEGIN;

				if (!rv) {
					goto done;
				}
				break;
			}
			case HEADER_END_LF:
				if (*i == LF) {
					if (isContentExpected())
						state_ = CONTENT_BEGIN;
					else
						state_ = MESSAGE_BEGIN;

					++*nparsed;
					++i;

					if (!onMessageHeaderEnd()) {
						TRACE(2, "messageHeaderEnd returned false. returning `Aborted`-state");
						goto done;
					}

					if (!isContentExpected() && !onMessageEnd()) {
						goto done;
					}
				} else {
					state_ = SYNTAX_ERROR;
				}
				break;
			case CONTENT_BEGIN:
				if (chunked_)
					state_ = CONTENT_CHUNK_SIZE_BEGIN;
				else if (contentLength_ >= 0)
					state_ = CONTENT;
				else
					state_ = CONTENT_ENDLESS;
				break;
			case CONTENT_ENDLESS: {
				// body w/o content-length (allowed in simple MESSAGE types only)
				BufferRef c(chunk.ref(*nparsed - initialOutOffset));

				//TRACE(2, "prepared content-chunk (%ld bytes): %s", c.size(), c.str().c_str());

				*nparsed += c.size();
				i += c.size();

				bool rv = filters_.empty()
					? onMessageContent(c)
					: onMessageContent(filters_.process(c).ref());

				if (!rv)
					goto done;

				break;
			}
			case CONTENT: {
				// fixed size content length
				std::size_t offset = *nparsed - initialOutOffset;
				std::size_t chunkSize = std::min(static_cast<size_t>(contentLength_), chunk.size() - offset);

				contentLength_ -= chunkSize;
				*nparsed += chunkSize;
				i += chunkSize;

				bool rv = filters_.empty()
					? onMessageContent(chunk.ref(offset, chunkSize))
					: onMessageContent(filters_.process(chunk.ref(offset, chunkSize)).ref());

				if (contentLength_ == 0)
					state_ = MESSAGE_BEGIN;

				if (!rv)
					goto done;

				if (state_ == MESSAGE_BEGIN && !onMessageEnd())
					goto done;

				break;
			}
			case CONTENT_CHUNK_SIZE_BEGIN:
				if (!std::isxdigit(*i)) {
					state_ = SYNTAX_ERROR;
					break;
				}
				state_ = CONTENT_CHUNK_SIZE;
				contentLength_ = 0;
				/* fall through */
			case CONTENT_CHUNK_SIZE:
				if (*i == CR) {
					state_ = CONTENT_CHUNK_LF1;
					++*nparsed;
					++i;
				} else if (*i >= '0' && *i <= '9') {
					contentLength_ = contentLength_ * 16 + *i - '0';
					++*nparsed;
					++i;
				} else if (*i >= 'a' && *i <= 'f') {
					contentLength_ = contentLength_ * 16 + 10 + *i - 'a';
					++*nparsed;
					++i;
				} else if (*i >= 'A' && *i <= 'F') {
					contentLength_ = contentLength_ * 16 + 10 + *i - 'A';
					++*nparsed;
					++i;
				} else {
					state_ = SYNTAX_ERROR;
				}
				break;
			case CONTENT_CHUNK_LF1:
				if (*i != LF) {
					state_ = SYNTAX_ERROR;
				} else {
					//TRACE(2, "content_length: %ld", contentLength_);
					if (contentLength_ != 0)
						state_ = CONTENT_CHUNK_BODY;
					else
						state_ = CONTENT_CHUNK_CR3;

					++*nparsed;
					++i;
				}
				break;
			case CONTENT_CHUNK_BODY:
				if (contentLength_) {
					std::size_t offset = *nparsed - initialOutOffset;
					std::size_t chunkSize = std::min(static_cast<size_t>(contentLength_), chunk.size() - offset);
					contentLength_ -= chunkSize;
					*nparsed += chunkSize;
					i += chunkSize;

					bool rv = filters_.empty()
						? onMessageContent(chunk.ref(offset, chunkSize))
						: onMessageContent(filters_.process(chunk.ref(offset, chunkSize)).ref());

					if (!rv) {
						goto done;
					}
				} else if (*i == CR) {
					state_ = CONTENT_CHUNK_LF2;
					++*nparsed;
					++i;
				}
				break;
			case CONTENT_CHUNK_LF2:
				if (*i != LF) {
					state_ = SYNTAX_ERROR;
				} else {
					state_ = CONTENT_CHUNK_SIZE;
					++*nparsed;
					++i;
				}
				break;
			case CONTENT_CHUNK_CR3:
				if (*i != CR) {
					state_ = SYNTAX_ERROR;
				} else {
					state_ = CONTENT_CHUNK_LF3;
					++*nparsed;
					++i;
				}
				break;
			case CONTENT_CHUNK_LF3:
				if (*i != LF) {
					state_ = SYNTAX_ERROR;
				} else {
					++*nparsed;
					++i;

					if (!onMessageEnd())
						goto done;

					state_ = MESSAGE_BEGIN;
				}
				break;
			case SYNTAX_ERROR: {
#if !defined(XZERO_NDEBUG)
				TRACE(1, "parse: syntax error");
				if (std::isprint(*i)) {
					TRACE(1, "parse: syntax error at nparsed: %ld, character: '%c'", *nparsed, *i);
				} else {
					TRACE(1, "parse: syntax error at nparsed: %ld, character: 0x%02X", *nparsed, *i);
				}
				chunk.dump("request chunk (at syntax error)");
#endif
				goto done;
			}
			default:
#if !defined(XZERO_NDEBUG)
				TRACE(1, "parse: unknown state %i", state_);
				if (std::isprint(*i)) {
					TRACE(1, "parse: internal error at nparsed: %ld, character: '%c'", *nparsed, *i);
				} else {
					TRACE(1, "parse: internal error at nparsed: %ld, character: 0x%02X", *nparsed, *i);
				}
				Buffer::dump(chunk.data(), chunk.size(), "request chunk (at unknown state)");
#endif
				goto done;
		}
	}
	// we've reached the end of the chunk

	if (state_ == CONTENT_BEGIN) {
		// we've just parsed all headers but no body yet.

		if (contentLength_ < 0 && !chunked_ && mode_ != MESSAGE) {
			// and there's no body to come

			if (!onMessageEnd())
				goto done;

			// subsequent calls to process() parse next request(s).
			state_ = MESSAGE_BEGIN;
		}
	}

done:
	return *nparsed - initialOutOffset;
}
Ejemplo n.º 22
0
/**
 * Frees up any resources and resets state of this connection.
 *
 * This method is invoked only after the connection has been closed and
 * this resource may now either be physically destructed or put into
 * a list of free connection objects for later use of newly incoming
 * connections (to avoid too many memory allocations).
 */
void HttpConnection::clear()
{
	TRACE(1, "clear(): refCount: %zu, conn.status: %s, parser.state: %s", refCount_, status_str(), state_str());
	//TRACE(1, "Stack Trace:\n%s", StackTrace().c_str());

	HttpMessageProcessor::reset();

	if (request_) {
		request_->clear();
	}

	clearCustomData();

	worker_->server_.onConnectionClose(this);
	delete socket_;
	socket_ = nullptr;
	requestCount_ = 0;

	inputOffset_ = 0;
	input_.clear();
}
Ejemplo n.º 23
0
void AIStateMachine::multiplex(event_type event)
{
  // If this fails then you are using a pointer to a state machine instead of an LLPointer.
  llassert(event == initial_run || getNumRefs() > 0);

  DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::multiplex(" << event_str(event) << ") [" << (void*)this << "]");

  base_state_type state;
  state_type run_state;

  // Critical area of mState.
  {
	multiplex_state_type_rat state_r(mState);

	// If another thread is already running multiplex() then it will pick up
	// our need to run (by us having set need_run), so there is no need to run
	// ourselves.
	llassert(!mMultiplexMutex.isSelfLocked());		// We may never enter recursively!
	if (!mMultiplexMutex.tryLock())
	{
	  Dout(dc::statemachine(mSMDebug), "Leaving because it is already being run [" << (void*)this << "]");
	  return;
	}

	//===========================================
	// Start of critical area of mMultiplexMutex.

	// If another thread already called begin_loop() since we needed a run,
	// then we must not schedule a run because that could lead to running
	// the same state twice. Note that if need_run was reset in the mean
	// time and then set again, then it can't hurt to schedule a run since
	// we should indeed run, again.
	if (event == schedule_run && !sub_state_type_rat(mSubState)->need_run)
	{
	  Dout(dc::statemachine(mSMDebug), "Leaving because it was already being run [" << (void*)this << "]");
	  return;
	}

	// We're at the beginning of multiplex, about to actually run it.
	// Make a copy of the states.
	run_state = begin_loop((state = state_r->base_state));
  }
  // End of critical area of mState.

  bool keep_looping;
  bool destruct = false;
  do
  {

	if (event == normal_run)
	{
#ifdef CWDEBUG
	  if (state == bs_multiplex)
		Dout(dc::statemachine(mSMDebug), "Running state bs_multiplex / " << state_str_impl(run_state) << " [" << (void*)this << "]");
	  else
		Dout(dc::statemachine(mSMDebug), "Running state " << state_str(state) << " [" << (void*)this << "]");
#endif

#ifdef SHOW_ASSERT
	  // This debug code checks that each state machine steps precisely through each of it's states correctly.
	  if (state != bs_reset)
	  {
		switch(mDebugLastState)
		{
		  case bs_reset:
			llassert(state == bs_initialize || state == bs_killed);
			break;
		  case bs_initialize:
			llassert(state == bs_multiplex || state == bs_abort);
			break;
		  case bs_multiplex:
			llassert(state == bs_multiplex || state == bs_finish || state == bs_abort);
			break;
		  case bs_abort:
			llassert(state == bs_finish);
			break;
		  case bs_finish:
			llassert(state == bs_callback);
			break;
		  case bs_callback:
			llassert(state == bs_killed || state == bs_reset);
			break;
		  case bs_killed:
			llassert(state == bs_killed);
			break;
		}
	  }
	  // More sanity checks.
	  if (state == bs_multiplex)
	  {
		// set_state is only called from multiplex_impl and therefore synced with mMultiplexMutex.
		mDebugShouldRun |= mDebugSetStatePending;
		// Should we run at all?
		llassert(mDebugShouldRun);
	  }
	  // Any previous reason to run is voided by actually running.
	  mDebugShouldRun = false;
#endif

	  mRunMutex.lock();
	  // Now we are actually running a single state.
	  // If abort() was called at any moment before, we execute that state instead.
	  bool const late_abort = (state == bs_multiplex || state == bs_initialize) && sub_state_type_rat(mSubState)->aborted;
	  if (LL_UNLIKELY(late_abort))
	  {
		// abort() was called from a child state machine, from another thread, while we were already scheduled to run normally from an engine.
		// What we want to do here is pretend we detected the abort at the end of the *previous* run.
		// If the state is bs_multiplex then the previous state was either bs_initialize or bs_multiplex,
		// both of which would have switched to bs_abort: we set the state to bs_abort instead and just
		// continue this run.
		// However, if the state is bs_initialize we can't switch to bs_killed because that state isn't
		// handled in the switch below; it's only handled when exiting multiplex() directly after it is set.
		// Therefore, in that case we have to set the state BACK to bs_reset and run it again. This duplicated
		// run of bs_reset is not a problem because it happens to be a NoOp.
		state = (state == bs_initialize) ? bs_reset : bs_abort;
#ifdef CWDEBUG
		Dout(dc::statemachine(mSMDebug), "Late abort detected! Running state " << state_str(state) << " instead [" << (void*)this << "]");
#endif
	  }
#ifdef SHOW_ASSERT
	  mDebugLastState = state;
	  // Make sure we only call ref() once and in balance with unref().
	  if (state == bs_initialize)
	  {
		// This -- and call to ref() (and the test when we're about to call unref()) -- is all done in the critical area of mMultiplexMutex.
		llassert(!mDebugRefCalled);
		mDebugRefCalled = true;
	  }
#endif
	  switch(state)
	  {
		case bs_reset:
		  // We're just being kick started to get into the right thread
		  // (possibly for the second time when a late abort was detected, but that's ok: we do nothing here).
		  break;
		case bs_initialize:
		  ref();
		  initialize_impl();
		  break;
		case bs_multiplex:
		  llassert(!mDebugAborted);
		  multiplex_impl(run_state);
		  break;
		case bs_abort:
		  abort_impl();
		  break;
		case bs_finish:
		  sub_state_type_wat(mSubState)->reset = false;		// By default, halt state machines when finished.
		  finish_impl();									// Call run() from finish_impl() or the call back to restart from the beginning.
		  break;
		case bs_callback:
		  callback();
		  break;
		case bs_killed:
		  mRunMutex.unlock();
		  // bs_killed is handled when it is set. So, this must be a re-entry.
		  // We can only get here when being called by an engine that we were added to before we were killed.
		  // This should already be have been set to NULL to indicate that we want to be removed from that engine.
		  llassert(!multiplex_state_type_rat(mState)->current_engine);
		  // Do not call unref() twice.
		  return;
	  }
	  mRunMutex.unlock();
	}

	{
	  multiplex_state_type_wat state_w(mState);

	  //=================================
	  // Start of critical area of mState

	  // Unless the state is bs_multiplex or bs_killed, the state machine needs to keep calling multiplex().
	  bool need_new_run = true;
	  if (event == normal_run || event == insert_abort)
	  {
		sub_state_type_rat sub_state_r(mSubState);

		if (event == normal_run)
		{
		  // Switch base state as function of sub state.
		  switch(state)
		  {
			case bs_reset:
			  if (sub_state_r->aborted)
			  {
				// We have been aborted before we could even initialize, no de-initialization is possible.
				state_w->base_state = bs_killed;
				// Stop running.
				need_new_run = false;
			  }
			  else
			  {
				// run() was called: call initialize_impl() next.
				state_w->base_state = bs_initialize;
			  }
			  break;
			case bs_initialize:
			  if (sub_state_r->aborted)
			  {
				// initialize_impl() called abort.
				state_w->base_state = bs_abort;
			  }
			  else
			  {
				// Start actually running.
				state_w->base_state = bs_multiplex;
				// If the state is bs_multiplex we only need to run again when need_run was set again in the meantime or when this state machine isn't idle.
				need_new_run = sub_state_r->need_run || !sub_state_r->idle;
			  }
			  break;
			case bs_multiplex:
			  if (sub_state_r->aborted)
			  {
				// abort() was called.
				state_w->base_state = bs_abort;
			  }
			  else if (sub_state_r->finished)
			  {
				// finish() was called.
				state_w->base_state = bs_finish;
			  }
			  else
			  {
				// Continue in bs_multiplex.
				// If the state is bs_multiplex we only need to run again when need_run was set again in the meantime or when this state machine isn't idle.
				need_new_run = sub_state_r->need_run || !sub_state_r->idle;
				// If this fails then the run state didn't change and neither idle() nor yield() was called.
				llassert_always(!(need_new_run && !sub_state_r->skip_idle && !mYieldEngine && sub_state_r->run_state == run_state));
			  }
			  break;
			case bs_abort:
			  // After calling abort_impl(), call finish_impl().
			  state_w->base_state = bs_finish;
			  break;
			case bs_finish:
			  // After finish_impl(), call the call back function.
			  state_w->base_state = bs_callback;
			  break;
			case bs_callback:
			  if (sub_state_r->reset)
			  {
				// run() was called (not followed by kill()).
				state_w->base_state = bs_reset;
			  }
			  else
			  {
				// After the call back, we're done.
				state_w->base_state = bs_killed;
				// Call unref().
				destruct = true;
				// Stop running.
				need_new_run = false;
			  }
			  break;
			default: // bs_killed
			  // We never get here.
			  break;
		  }
		}
		else // event == insert_abort
		{
		  // We have been aborted, but we're idle. If we'd just schedule a new run below, it would re-run
		  // the last state before the abort is handled. What we really need is to pick up as if the abort
		  // was handled directly after returning from the last run. If we're not running anymore, then
		  // do nothing as the state machine already ran and things should be processed normally
		  // (in that case this is just a normal schedule which can't harm because we're can't accidently
		  // re-run an old run_state).
		  if (state_w->base_state == bs_multiplex)		// Still running?
		  {
			// See the switch above for case bs_multiplex.
			llassert(sub_state_r->aborted);
			// abort() was called.
			state_w->base_state = bs_abort;
		  }
		}

#ifdef CWDEBUG
		if (state != state_w->base_state)
		  Dout(dc::statemachine(mSMDebug), "Base state changed from " << state_str(state) << " to " << state_str(state_w->base_state) <<
			  "; need_new_run = " << (need_new_run ? "true" : "false") << " [" << (void*)this << "]");
#endif
	  }

	  // Figure out in which engine we should run.
	  AIEngine* engine = mYieldEngine ? mYieldEngine : (state_w->current_engine ? state_w->current_engine : mDefaultEngine);
	  // And the current engine we're running in.
	  AIEngine* current_engine = (event == normal_run) ? state_w->current_engine : NULL;

	  // Immediately run again if yield() wasn't called and it's OK to run in this thread.
	  // Note that when it's OK to run in any engine (mDefaultEngine is NULL) then the last
	  // compare is also true when current_engine == NULL.
	  keep_looping = need_new_run && !mYieldEngine && engine == current_engine;
	  mYieldEngine = NULL;

	  if (keep_looping)
	  {
		// Start a new loop.
		run_state = begin_loop((state = state_w->base_state));
		event = normal_run;
	  }
	  else
	  {
		if (need_new_run)
		{
		  // Add us to an engine if necessary.
		  if (engine != state_w->current_engine)
		  {
			// engine can't be NULL here: it can only be NULL if mDefaultEngine is NULL.
			engine->add(this);
			// Mark that we're added to this engine, and at the same time, that we're not added to the previous one.
			state_w->current_engine = engine;
		  }
#ifdef SHOW_ASSERT
		  // We are leaving the loop, but we're not idle. The statemachine should re-enter the loop again.
		  mDebugShouldRun = true;
#endif
		}
		else
		{
		  // Remove this state machine from any engine,
		  // causing the engine to remove us.
		  state_w->current_engine = NULL;
		}

#ifdef SHOW_ASSERT
		// Mark that we stop running the loop.
		mThreadId.clear();

		if (destruct)
		{
		  // We're about to call unref(). Make sure we call that in balance with ref()!
		  llassert(mDebugRefCalled);
		  mDebugRefCalled  = false;
		}
#endif

		// End of critical area of mMultiplexMutex.
		//=========================================

		// Release the lock on mMultiplexMutex *first*, before releasing the lock on mState,
		// to avoid to ever call the tryLock() and fail, while this thread isn't still
		// BEFORE the critical area of mState!

		mMultiplexMutex.unlock();
	  }

	  // Now it is safe to leave the critical area of mState as the tryLock won't fail anymore.
	  // (Or, if we didn't release mMultiplexMutex because keep_looping is true, then this
	  // end of the critical area of mState is equivalent to the first critical area in this
	  // function.

	  // End of critical area of mState.
	  //================================
	}

  }
  while (keep_looping);

  if (destruct)
  {
	unref();
  }
}
void AIStateMachine::set_state(state_type state)
{
  DoutEntering(dc::statemachine, "AIStateMachine::set_state(" << state_str(state) << ") [" << (void*)this << "]");

  // Stop race condition of multiple threads calling cont() or set_state() here.
  mSetStateLock.lock();

  // Do not call set_state() unless running.
  llassert(mState == bs_run || !is_main_thread());

  // If this function is called from another thread than the main thread, then we have to ignore
  // it if we're not idle and the state is less than the current state. The main thread must
  // be able to change the state to anything (also smaller values). Note that that only can work
  // if the main thread itself at all times cancels thread callbacks that call set_state()
  // before calling idle() again!
  //
  // Thus: main thead calls idle(), and tells one or more threads to do callbacks on events,
  // which (might) call set_state(). If the main thread calls set_state first (currently only
  // possible as a result of the use of a timer) it will set mIdle to false (here) then cancel
  // the call backs from the other threads and only then call idle() again.
  // Thus if you want other threads get here while mIdle is false to be ignored then the
  // main thread should use a large value for the new run state.
  //
  // If a non-main thread calls set_state first, then the state is changed but the main thread
  // can still override it if it calls set_state before handling the new state; in the latter
  // case it would still be as if the call from the non-main thread was ignored.
  //
  // Concurrent calls from non-main threads however, always result in the largest state
  // to prevail.

  // If the state machine is already running, and we are not the main-thread and the new
  // state is less than the current state, ignore it.
  // Also, if abort() or finish() was called, then we should just ignore it.
  if (mState != bs_run ||
	  (!mIdle && state <= mRunState && !AIThreadID::in_main_thread()))
  {
#ifdef SHOW_ASSERT
	// It's a bit weird if the same thread does two calls on a row where the second call
	// has a smaller value: warn about that.
	if (mState == bs_run && mContThread.equals_current_thread())
	{
	  llwarns << "Ignoring call to set_state(" << state_str(state) <<
		  ") by non-main thread before main-thread could react on previous call, "
		  "because new state is smaller than old state (" << state_str(mRunState) << ")." << llendl;
	}
#endif
	mSetStateLock.unlock();
	return;		// Ignore.
  }

  // Do not call idle() when set_state is called from another thread; use idle(state_type) instead.
  llassert(!mCalledThreadUnsafeIdle || is_main_thread());

  // Change mRunState to the requested value.
  if (mRunState != state)
  {
	mRunState = state;
	Dout(dc::statemachine, "mRunState set to " << state_str(mRunState));
  }

  // Continue the state machine if appropriate.
  if (mIdle)
	locked_cont();				// This unlocks mSetStateLock.
  else
	mSetStateLock.unlock();

  // If we get here then mIdle is false, so only mRunState can still be changed but we won't
  // call locked_cont() anymore. When the main thread finally picks up on the state change,
  // it will cancel any possible callbacks from other threads and process the largest state
  // that this function was called with in the meantime.
}
void AIStateMachine::multiplex(U64 current_time)
{
  // Return immediately when this state machine is sleeping.
  // A negative value of mSleep means we're counting frames,
  // a positive value means we're waiting till a certain
  // amount of time has passed.
  if (mSleep != 0)
  {
	if (mSleep < 0)
	{
	  if (++mSleep)
		return;
	}
	else
	{
	  if (current_time < (U64)mSleep)
		return;
	  mSleep = 0;
	}
  }

  DoutEntering(dc::statemachine, "AIStateMachine::multiplex() [" << (void*)this << "] [with state: " << state_str(mState == bs_run ? mRunState : mState) << "]");
  llassert(mState == bs_initialize || mState == bs_run);

  // Real state machine starts here.
  if (mState == bs_initialize)
  {
	mAborted = false;
	mState = bs_run;
	initialize_impl();
	if (mAborted || mState != bs_run)
	  return;
  }
  multiplex_impl();
}
Ejemplo n.º 26
0
void NetMessageReader::parse()
{
	while (!isEndOfBuffer()) {
		if (std::isprint(currentChar())) {
			TRACE("parse: '%c' (%d) %s", currentChar(), static_cast<int>(state()), state_str());
		} else {
			TRACE("parse: 0x%02X (%d) %s", currentChar(), static_cast<int>(state()), state_str());
		}

		switch (state()) {
			case MESSAGE_BEGIN:
				// Syntetic state. Go straight to TYPE.
			case MESSAGE_TYPE:
				switch (currentChar()) {
					case '+':
					case '-':
					case ':':
						currentContext_->type = static_cast<NetMessage::Type>(currentChar());
						setState(MESSAGE_LINE_BEGIN);
						nextChar();
						break;
					case '$':
						currentContext_->type = NetMessage::String;
						setState(BULK_BEGIN);
						break;
					case '*':
						currentContext_->type = NetMessage::Array;
						setState(MESSAGE_NUM_ARGS);
						nextChar();
						break;
					default:
						currentContext_->type = NetMessage::Nil;
						setState(SYNTAX_ERROR);
						return;
				}
				break;
			case MESSAGE_LINE_BEGIN:
				if (currentChar() == '\r') {
					setState(SYNTAX_ERROR);
					return;
				}
				setState(MESSAGE_LINE_OR_CR);
				begin_ = pos_;
				nextChar();
				break;
			case MESSAGE_LINE_OR_CR:
				if (currentChar() == '\n') {
					setState(SYNTAX_ERROR);
					return;
				}

				if (currentChar() == '\r')
					setState(MESSAGE_LINE_LF);

				nextChar();
				break;
			case MESSAGE_LINE_LF: {
				if (currentChar() != '\n') {
					setState(SYNTAX_ERROR);
					return;
				}
				BufferRef value = buffer_->ref(begin_, pos_ - begin_ - 1);
				switch (currentContext_->type) {
					case NetMessage::Status:
						currentContext_->message = NetMessage::createStatus(value);
						break;
					case NetMessage::Error:
						currentContext_->message = NetMessage::createError(value);
						break;
					case NetMessage::String:
						currentContext_->message = NetMessage::createString(value);
						break;
					case NetMessage::Number:
						currentContext_->message = NetMessage::createNumber(value.toInt());
						break;
					default:
						currentContext_->message = NetMessage::createNil();
						break;
				}
				setState(MESSAGE_END);
				nextChar();
				popContext();
				break;
			}
			case MESSAGE_NUM_ARGS: {
				switch (currentChar()) {
					case '0':
					case '1':
					case '2':
					case '3':
					case '4':
					case '5':
					case '6':
					case '7':
					case '8':
					case '9':
						currentContext_->number *= 10;
						currentContext_->number += currentChar() - '0';
						setState(MESSAGE_NUM_ARGS_OR_CR);
						nextChar();
						break;
					default:
						setState(SYNTAX_ERROR);
						return;
				}
				break;
			}
			case MESSAGE_NUM_ARGS_OR_CR:
				switch (currentChar()) {
					case '0':
					case '1':
					case '2':
					case '3':
					case '4':
					case '5':
					case '6':
					case '7':
					case '8':
					case '9':
						currentContext_->number *= 10;
						currentContext_->number += currentChar() - '0';
						nextChar();
						break;
					case '\r':
						setState(MESSAGE_LF);
						currentContext_->message = NetMessage::createArray(currentContext_->number);
						nextChar();
						break;
					default:
						setState(SYNTAX_ERROR);
						return;
				}
				break;
			case MESSAGE_LF:
				if (currentChar() != '\n') {
					setState(SYNTAX_ERROR);
					return;
				}

				nextChar();

				if (currentContext_->type == NetMessage::Array) {
					setState(BULK_BEGIN);
					pushContext();
				} else {
					setState(MESSAGE_END);
					popContext();
				}
				break;
			case BULK_BEGIN:
				if (currentChar() != '$') {
					setState(SYNTAX_ERROR);
					return;
				}
				setState(BULK_SIZE);
				nextChar();
				break;
			case BULK_SIZE:
				switch (currentChar()) {
					case '0':
					case '1':
					case '2':
					case '3':
					case '4':
					case '5':
					case '6':
					case '7':
					case '8':
					case '9':
						argSize_ *= 10;
						argSize_ += currentChar() - '0';
						setState(BULK_SIZE_OR_CR);
						nextChar();
						break;
					default:
						setState(SYNTAX_ERROR);
						return;
				}
				break;
			case BULK_SIZE_OR_CR:
				switch (currentChar()) {
					case '0':
					case '1':
					case '2':
					case '3':
					case '4':
					case '5':
					case '6':
					case '7':
					case '8':
					case '9':
						argSize_ *= 10;
						argSize_ += currentChar() - '0';
						nextChar();
						break;
					case '\r':
						setState(BULK_SIZE_LF);
						nextChar();
						break;
					default:
						setState(SYNTAX_ERROR);
						return;
				}
				break;
			case BULK_SIZE_LF:
				if (currentChar() != '\n') {
					setState(SYNTAX_ERROR);
					return;
				}
				nextChar();
				setState(BULK_BODY_OR_CR);
				begin_ = pos_;
				break;
			case BULK_BODY_OR_CR:
				if (argSize_ > 0) {
					argSize_ -= nextChar(argSize_);
				} else if (currentChar() == '\r') {
					BufferRef value = buffer_->ref(begin_, pos_ - begin_);
					currentContext_->message = NetMessage::createString(value);
					nextChar();
					setState(BULK_BODY_LF);
				} else {
					setState(SYNTAX_ERROR);
					return;
				}
				break;
			case BULK_BODY_LF:
				if (currentChar() != '\n') {
					setState(SYNTAX_ERROR);
					return;
				}
				nextChar();

				setState(MESSAGE_END);
				popContext();

				break;
			case MESSAGE_END:
				// if we reach here, then only because
				// there's garbage at the end of our message.
				break;
			case SYNTAX_ERROR:
				fprintf(stderr, "NetMessageSocket message syntax error at offset %zi\n", pos_);
				break;
			default:
				break;
		}
	}
}