/* 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; }
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); }
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); }
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; }
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); }
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(); } }
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)); } }
/** * \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; }
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)); }
/** 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; }
/* * 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(); }
/** 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; }
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; } } }
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; }
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; }
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); } }
/** * \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; }
void HttpProxy::ProxyConnection::onWriteComplete() { TRACE("chunk write complete: %s", state_str()); backend_->setMode(Socket::Read); unref(); }
/** * \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; }
/** 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; }
/** * 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(); }
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(); }
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; } } }