From bf3dd49cc8a388a7b8db61c0002cc06286ac6087 Mon Sep 17 00:00:00 2001 From: Josh Yelon Date: Tue, 12 Oct 2021 13:54:08 -0400 Subject: [PATCH] Add support for clock ticks --- luprex/core/cpp/drivenengine.cpp | 6 +- luprex/core/cpp/drivenengine.hpp | 10 ++- luprex/core/cpp/driver-mingw.cpp | 55 +++++++++------ luprex/core/cpp/main.cpp | 112 ++++++++++++++++++++++++++----- 4 files changed, 140 insertions(+), 43 deletions(-) diff --git a/luprex/core/cpp/drivenengine.cpp b/luprex/core/cpp/drivenengine.cpp index 1d5a672d..f9d4a95e 100644 --- a/luprex/core/cpp/drivenengine.cpp +++ b/luprex/core/cpp/drivenengine.cpp @@ -186,8 +186,10 @@ void DrivenEngine::drv_recv_incoming(int chid, int nbytes, const char *bytes) { } } -void DrivenEngine::drv_notify_close(int chid) { - get_chid(chid)->closed_ = true; +void DrivenEngine::drv_notify_close(int chid, const std::string &err) { + Channel *ch = get_chid(chid); + ch->closed_ = true; + ch->error_ = err; } int DrivenEngine::drv_notify_accept(int port) { diff --git a/luprex/core/cpp/drivenengine.hpp b/luprex/core/cpp/drivenengine.hpp index 3592b012..061049f8 100644 --- a/luprex/core/cpp/drivenengine.hpp +++ b/luprex/core/cpp/drivenengine.hpp @@ -123,6 +123,13 @@ public: // bool closed() const { return closed_; } + // Get the channel's error message. + // + // If this is an empty string, there is no error. If this is set, + // then the channel is also closed. + // + std::string error() const { return error_; } + // True if the channel is in readline mode. // // Stdio always starts with this enabled, other channels always start @@ -161,6 +168,7 @@ private: std::unique_ptr sb_out_; int port_; bool closed_; + std::string error_; std::string target_; char readline_buf_[READLINE_MAX]; int readline_len_; @@ -313,7 +321,7 @@ public: // delete it. Closing a channel prevents it from showing up in // 'drv_list_channels'. // - void drv_notify_close(int chid); + void drv_notify_close(int chid, const std::string &err); // Notify the DrivenEngine that somebody connected to an incoming port. // This will cause the DrivenEngine to allocate a new channel and put the diff --git a/luprex/core/cpp/driver-mingw.cpp b/luprex/core/cpp/driver-mingw.cpp index 6bd61394..8b1e44ce 100644 --- a/luprex/core/cpp/driver-mingw.cpp +++ b/luprex/core/cpp/driver-mingw.cpp @@ -8,6 +8,7 @@ #include #include #include +#include @@ -19,9 +20,16 @@ public: DrivenEngine *driven_; SOCKET socket_[MAX_CHAN]; bool connected_[MAX_CHAN]; - bool engine_wakeup_; + bool short_sleep_; std::map listen_sockets_; std::unique_ptr chbuf; + int64_t basetime_; + + int64_t get_now() { + FILETIME ft_now; + GetSystemTimeAsFileTime(&ft_now); + return (LONGLONG)ft_now.dwLowDateTime + ((LONGLONG)(ft_now.dwHighDateTime) << 32LL); + } static PADDRINFOA find_good_addr(PADDRINFOA addrinfo) { for (PADDRINFOA addr = addrinfo; addr != nullptr; addr = addr->ai_next) { @@ -56,7 +64,7 @@ public: goto error; } if (status != 0) { - err = "DNS resolution hard failure"; + err = "DNS resolution malfunction"; goto error; } goodaddr = find_good_addr(addrs); @@ -72,7 +80,7 @@ public: int errcode = WSAGetLastError(); if (errcode != WSAEWOULDBLOCK) { std::ostringstream oss; - oss << "Error " << errcode; + oss << "connect error " << errcode; err = oss.str(); goto error; } @@ -111,7 +119,7 @@ public: socket_[i] = INVALID_SOCKET; connected_[i] = false; } - engine_wakeup_ = false; + short_sleep_ = false; chbuf.reset(new char[65536]); } @@ -132,7 +140,7 @@ public: void handle_lua_source() { if (driven_->drv_get_rescan_lua_source()) { driven_->drv_set_lua_source(util::read_lua_source("lua")); - engine_wakeup_ = true; + short_sleep_ = true; } } @@ -156,8 +164,8 @@ public: std::string err; SOCKET sock = open_connection(driven_->drv_get_target(chid), err); if (sock == INVALID_SOCKET) { - driven_->drv_notify_close(chid); - engine_wakeup_ = true; + driven_->drv_notify_close(chid, err); + short_sleep_ = true; } else { socket_[chid] = sock; connected_[chid] = false; @@ -200,21 +208,25 @@ public: char c = key.uChar.AsciiChar; ascii[nascii++] = c; } - driven_->drv_recv_incoming(0, nascii, ascii); + if (nascii > 0) { + driven_->drv_recv_incoming(0, nascii, ascii); + short_sleep_ = true; + } } - engine_wakeup_ = true; } void handle_clock() { + int now = get_now() - basetime_; + driven_->drv_set_clock(double(now) / 10000000.0); } - void close_socket(int chid) { + void close_socket(int chid, const std::string err) { assert(socket_[chid] != INVALID_SOCKET); assert(closesocket(socket_[chid]) == 0); - driven_->drv_notify_close(chid); + driven_->drv_notify_close(chid, err); socket_[chid] = INVALID_SOCKET; connected_[chid] = false; - engine_wakeup_ = true; + short_sleep_ = true; } bool calc_select_sets(fd_set &rfds, fd_set &wfds, fd_set &efds) const { @@ -245,7 +257,7 @@ public: int chid = driven_->drv_notify_accept(port); socket_[chid] = chsock; connected_[chid] = true; - engine_wakeup_ = true; + short_sleep_ = true; continue; } int errcode = WSAGetLastError(); @@ -293,7 +305,7 @@ public: if (nbytes > 0) { int wbytes = send(sock, bytes, nbytes, 0); if (wbytes == SOCKET_ERROR) { - close_socket(chid); + close_socket(chid, "send failure"); continue; } else { driven_->drv_sent_outgoing(chid, wbytes); @@ -303,11 +315,11 @@ public: if (FD_ISSET(sock, &rfds)) { int nrecv = recv(sock, chbuf.get(), 65536, 0); if ((nrecv == SOCKET_ERROR) || (nrecv == 0)) { - close_socket(chid); + close_socket(chid, "recv failure"); continue; } else { driven_->drv_recv_incoming(chid, nrecv, chbuf.get()); - engine_wakeup_ = true; + short_sleep_ = true; } } } @@ -320,22 +332,21 @@ public: assert(hconsole != INVALID_HANDLE_VALUE); init(de); DrivenEngine::set(de); + basetime_ = get_now(); driven_->drv_set_lua_source(util::read_lua_source("lua")); driven_->drv_invoke_event_init(); handle_listen_ports(); while (!de->drv_get_stop_driver()) { - engine_wakeup_ = false; + short_sleep_ = false; handle_lua_source(); handle_new_closed_sockets(); handle_new_outgoing_sockets(); - handle_console_output(); handle_console_input(); - int mstimeout = engine_wakeup_ ? 0 : 100; + handle_console_output(); + int mstimeout = short_sleep_ ? 0 : 100; handle_socket_input_output(mstimeout); handle_clock(); - if (engine_wakeup_) { - de->drv_invoke_event_update(); - } + de->drv_invoke_event_update(); } DrivenEngine::set(nullptr); } diff --git a/luprex/core/cpp/main.cpp b/luprex/core/cpp/main.cpp index efeec0af..9532a649 100644 --- a/luprex/core/cpp/main.cpp +++ b/luprex/core/cpp/main.cpp @@ -2,27 +2,34 @@ #include "textgame.hpp" #include "driver.hpp" #include "drivenengine.hpp" +#include +#include -class TNTest : public DrivenEngine { +void write_closed_message(Channel *ch, StreamBuffer *out) { + std::ostringstream oss; + oss << "Chan " << ch->chid() << " closed [" << ch->error() << "]\n"; + out->write_bytes(oss.str()); +} + +void dump_lines(StreamBuffer *in, StreamBuffer *out, int chid) { + while (true) { + std::string l = in->readline(); + if (l == "") break; + std::ostringstream oss; + oss << "Chan " << chid << ": " << l; + out->write_bytes(oss.str()); + } +} + +// This test allows input on stdin or on port 8085. +// You can type lines and see them echoed. +class TNTest1 : public DrivenEngine { public: std::vector channels_; virtual void event_init() { - // UniqueChannel ch = new_outgoing_channel("stanford.edu:80"); - // ch->out()->write_bytes("GET /index.html HTTP/1.1\n\n"); - // channels_.emplace_back(std::move(ch)); listen_port(8085); } - void dump_lines(StreamBuffer *in, StreamBuffer *out, int chid) { - while (true) { - std::string l = in->readline(); - if (l == "") break; - std::ostringstream oss; - oss << "Chan " << chid << ": " << l; - out->write_bytes(oss.str()); - } - } - virtual void event_update() { while (true) { UniqueChannel ch = new_incoming_channel(); @@ -37,9 +44,7 @@ public: for (UniqueChannel &ch : channels_) { dump_lines(ch->in(), stdioch->out(), ch->chid()); if (ch->closed()) { - std::ostringstream oss; - oss << "Chan " << ch->chid() << " closed.\n"; - stdioch->out()->write_bytes(oss.str()); + write_closed_message(ch.get(), stdioch->out()); } else { keep.emplace_back(std::move(ch)); } @@ -48,9 +53,80 @@ public: } }; +// This test connects to a public webserver and prints +// the output from the server. +class TNTest2 : public DrivenEngine { +public: + std::vector channels_; + virtual void event_init() { + UniqueChannel ch = new_outgoing_channel("stanford.edu:80"); + ch->out()->write_bytes("GET http://stanford.edu/index.html HTTP/1.1\n\n"); + channels_.emplace_back(std::move(ch)); + } + + virtual void event_update() { + Channel *stdioch = get_stdio_channel(); + dump_lines(stdioch->in(), stdioch->out(), 0); + std::vector keep; + for (UniqueChannel &ch : channels_) { + dump_lines(ch->in(), stdioch->out(), ch->chid()); + if (ch->closed()) { + write_closed_message(ch.get(), stdioch->out()); + } else { + keep.emplace_back(std::move(ch)); + } + } + channels_ = std::move(keep); + } +}; + +// This test produces a DNS resolution failure. +class TNTest3 : public DrivenEngine { +public: + std::vector channels_; + virtual void event_init() { + UniqueChannel ch = new_outgoing_channel("akjsdkajshdakjshd.alk:80"); + ch->out()->write_bytes("GET http://stanford.edu/index.html HTTP/1.1\n\n"); + channels_.emplace_back(std::move(ch)); + } + + virtual void event_update() { + Channel *stdioch = get_stdio_channel(); + dump_lines(stdioch->in(), stdioch->out(), 0); + std::vector keep; + for (UniqueChannel &ch : channels_) { + dump_lines(ch->in(), stdioch->out(), ch->chid()); + if (ch->closed()) { + write_closed_message(ch.get(), stdioch->out()); + } else { + keep.emplace_back(std::move(ch)); + } + } + channels_ = std::move(keep); + } +}; + +// This test just prints the time. +class TNTest4 : public DrivenEngine { +public: + int count; + virtual void event_init() { + count = 0; + } + + virtual void event_update() { + std::cerr << std::fixed << std::setprecision(2) << get_clock() << " "; + count++; + if (count == 10) { + std::cerr << std::endl; + count = 0; + } + } +}; + int main(int argc, char **argv) { //TextGame tg; - TNTest tg; + TNTest4 tg; driver_drive(&tg); }