Add support for clock ticks

This commit is contained in:
2021-10-12 13:54:08 -04:00
parent 995219d965
commit bf3dd49cc8
4 changed files with 140 additions and 43 deletions

View File

@@ -186,8 +186,10 @@ void DrivenEngine::drv_recv_incoming(int chid, int nbytes, const char *bytes) {
} }
} }
void DrivenEngine::drv_notify_close(int chid) { void DrivenEngine::drv_notify_close(int chid, const std::string &err) {
get_chid(chid)->closed_ = true; Channel *ch = get_chid(chid);
ch->closed_ = true;
ch->error_ = err;
} }
int DrivenEngine::drv_notify_accept(int port) { int DrivenEngine::drv_notify_accept(int port) {

View File

@@ -123,6 +123,13 @@ public:
// //
bool closed() const { return closed_; } 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. // True if the channel is in readline mode.
// //
// Stdio always starts with this enabled, other channels always start // Stdio always starts with this enabled, other channels always start
@@ -161,6 +168,7 @@ private:
std::unique_ptr<StreamBuffer> sb_out_; std::unique_ptr<StreamBuffer> sb_out_;
int port_; int port_;
bool closed_; bool closed_;
std::string error_;
std::string target_; std::string target_;
char readline_buf_[READLINE_MAX]; char readline_buf_[READLINE_MAX];
int readline_len_; int readline_len_;
@@ -313,7 +321,7 @@ public:
// delete it. Closing a channel prevents it from showing up in // delete it. Closing a channel prevents it from showing up in
// 'drv_list_channels'. // '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. // Notify the DrivenEngine that somebody connected to an incoming port.
// This will cause the DrivenEngine to allocate a new channel and put the // This will cause the DrivenEngine to allocate a new channel and put the

View File

@@ -8,6 +8,7 @@
#include <ws2tcpip.h> #include <ws2tcpip.h>
#include <winsock2.h> #include <winsock2.h>
#include <synchapi.h> #include <synchapi.h>
#include <sysinfoapi.h>
@@ -19,9 +20,16 @@ public:
DrivenEngine *driven_; DrivenEngine *driven_;
SOCKET socket_[MAX_CHAN]; SOCKET socket_[MAX_CHAN];
bool connected_[MAX_CHAN]; bool connected_[MAX_CHAN];
bool engine_wakeup_; bool short_sleep_;
std::map<int, SOCKET> listen_sockets_; std::map<int, SOCKET> listen_sockets_;
std::unique_ptr<char> chbuf; std::unique_ptr<char> 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) { static PADDRINFOA find_good_addr(PADDRINFOA addrinfo) {
for (PADDRINFOA addr = addrinfo; addr != nullptr; addr = addr->ai_next) { for (PADDRINFOA addr = addrinfo; addr != nullptr; addr = addr->ai_next) {
@@ -56,7 +64,7 @@ public:
goto error; goto error;
} }
if (status != 0) { if (status != 0) {
err = "DNS resolution hard failure"; err = "DNS resolution malfunction";
goto error; goto error;
} }
goodaddr = find_good_addr(addrs); goodaddr = find_good_addr(addrs);
@@ -72,7 +80,7 @@ public:
int errcode = WSAGetLastError(); int errcode = WSAGetLastError();
if (errcode != WSAEWOULDBLOCK) { if (errcode != WSAEWOULDBLOCK) {
std::ostringstream oss; std::ostringstream oss;
oss << "Error " << errcode; oss << "connect error " << errcode;
err = oss.str(); err = oss.str();
goto error; goto error;
} }
@@ -111,7 +119,7 @@ public:
socket_[i] = INVALID_SOCKET; socket_[i] = INVALID_SOCKET;
connected_[i] = false; connected_[i] = false;
} }
engine_wakeup_ = false; short_sleep_ = false;
chbuf.reset(new char[65536]); chbuf.reset(new char[65536]);
} }
@@ -132,7 +140,7 @@ public:
void handle_lua_source() { void handle_lua_source() {
if (driven_->drv_get_rescan_lua_source()) { if (driven_->drv_get_rescan_lua_source()) {
driven_->drv_set_lua_source(util::read_lua_source("lua")); driven_->drv_set_lua_source(util::read_lua_source("lua"));
engine_wakeup_ = true; short_sleep_ = true;
} }
} }
@@ -156,8 +164,8 @@ public:
std::string err; std::string err;
SOCKET sock = open_connection(driven_->drv_get_target(chid), err); SOCKET sock = open_connection(driven_->drv_get_target(chid), err);
if (sock == INVALID_SOCKET) { if (sock == INVALID_SOCKET) {
driven_->drv_notify_close(chid); driven_->drv_notify_close(chid, err);
engine_wakeup_ = true; short_sleep_ = true;
} else { } else {
socket_[chid] = sock; socket_[chid] = sock;
connected_[chid] = false; connected_[chid] = false;
@@ -200,21 +208,25 @@ public:
char c = key.uChar.AsciiChar; char c = key.uChar.AsciiChar;
ascii[nascii++] = c; ascii[nascii++] = c;
} }
if (nascii > 0) {
driven_->drv_recv_incoming(0, nascii, ascii); driven_->drv_recv_incoming(0, nascii, ascii);
short_sleep_ = true;
}
} }
engine_wakeup_ = true;
} }
void handle_clock() { 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(socket_[chid] != INVALID_SOCKET);
assert(closesocket(socket_[chid]) == 0); assert(closesocket(socket_[chid]) == 0);
driven_->drv_notify_close(chid); driven_->drv_notify_close(chid, err);
socket_[chid] = INVALID_SOCKET; socket_[chid] = INVALID_SOCKET;
connected_[chid] = false; connected_[chid] = false;
engine_wakeup_ = true; short_sleep_ = true;
} }
bool calc_select_sets(fd_set &rfds, fd_set &wfds, fd_set &efds) const { 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); int chid = driven_->drv_notify_accept(port);
socket_[chid] = chsock; socket_[chid] = chsock;
connected_[chid] = true; connected_[chid] = true;
engine_wakeup_ = true; short_sleep_ = true;
continue; continue;
} }
int errcode = WSAGetLastError(); int errcode = WSAGetLastError();
@@ -293,7 +305,7 @@ public:
if (nbytes > 0) { if (nbytes > 0) {
int wbytes = send(sock, bytes, nbytes, 0); int wbytes = send(sock, bytes, nbytes, 0);
if (wbytes == SOCKET_ERROR) { if (wbytes == SOCKET_ERROR) {
close_socket(chid); close_socket(chid, "send failure");
continue; continue;
} else { } else {
driven_->drv_sent_outgoing(chid, wbytes); driven_->drv_sent_outgoing(chid, wbytes);
@@ -303,11 +315,11 @@ public:
if (FD_ISSET(sock, &rfds)) { if (FD_ISSET(sock, &rfds)) {
int nrecv = recv(sock, chbuf.get(), 65536, 0); int nrecv = recv(sock, chbuf.get(), 65536, 0);
if ((nrecv == SOCKET_ERROR) || (nrecv == 0)) { if ((nrecv == SOCKET_ERROR) || (nrecv == 0)) {
close_socket(chid); close_socket(chid, "recv failure");
continue; continue;
} else { } else {
driven_->drv_recv_incoming(chid, nrecv, chbuf.get()); driven_->drv_recv_incoming(chid, nrecv, chbuf.get());
engine_wakeup_ = true; short_sleep_ = true;
} }
} }
} }
@@ -320,23 +332,22 @@ public:
assert(hconsole != INVALID_HANDLE_VALUE); assert(hconsole != INVALID_HANDLE_VALUE);
init(de); init(de);
DrivenEngine::set(de); DrivenEngine::set(de);
basetime_ = get_now();
driven_->drv_set_lua_source(util::read_lua_source("lua")); driven_->drv_set_lua_source(util::read_lua_source("lua"));
driven_->drv_invoke_event_init(); driven_->drv_invoke_event_init();
handle_listen_ports(); handle_listen_ports();
while (!de->drv_get_stop_driver()) { while (!de->drv_get_stop_driver()) {
engine_wakeup_ = false; short_sleep_ = false;
handle_lua_source(); handle_lua_source();
handle_new_closed_sockets(); handle_new_closed_sockets();
handle_new_outgoing_sockets(); handle_new_outgoing_sockets();
handle_console_output();
handle_console_input(); handle_console_input();
int mstimeout = engine_wakeup_ ? 0 : 100; handle_console_output();
int mstimeout = short_sleep_ ? 0 : 100;
handle_socket_input_output(mstimeout); handle_socket_input_output(mstimeout);
handle_clock(); handle_clock();
if (engine_wakeup_) {
de->drv_invoke_event_update(); de->drv_invoke_event_update();
} }
}
DrivenEngine::set(nullptr); DrivenEngine::set(nullptr);
} }
}; };

View File

@@ -2,15 +2,13 @@
#include "textgame.hpp" #include "textgame.hpp"
#include "driver.hpp" #include "driver.hpp"
#include "drivenengine.hpp" #include "drivenengine.hpp"
#include <iostream>
#include <iomanip>
class TNTest : public DrivenEngine { void write_closed_message(Channel *ch, StreamBuffer *out) {
public: std::ostringstream oss;
std::vector<UniqueChannel> channels_; oss << "Chan " << ch->chid() << " closed [" << ch->error() << "]\n";
virtual void event_init() { out->write_bytes(oss.str());
// 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) { void dump_lines(StreamBuffer *in, StreamBuffer *out, int chid) {
@@ -23,6 +21,15 @@ public:
} }
} }
// 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<UniqueChannel> channels_;
virtual void event_init() {
listen_port(8085);
}
virtual void event_update() { virtual void event_update() {
while (true) { while (true) {
UniqueChannel ch = new_incoming_channel(); UniqueChannel ch = new_incoming_channel();
@@ -37,9 +44,7 @@ public:
for (UniqueChannel &ch : channels_) { for (UniqueChannel &ch : channels_) {
dump_lines(ch->in(), stdioch->out(), ch->chid()); dump_lines(ch->in(), stdioch->out(), ch->chid());
if (ch->closed()) { if (ch->closed()) {
std::ostringstream oss; write_closed_message(ch.get(), stdioch->out());
oss << "Chan " << ch->chid() << " closed.\n";
stdioch->out()->write_bytes(oss.str());
} else { } else {
keep.emplace_back(std::move(ch)); 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<UniqueChannel> 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<UniqueChannel> 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<UniqueChannel> 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<UniqueChannel> 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) int main(int argc, char **argv)
{ {
//TextGame tg; //TextGame tg;
TNTest tg; TNTest4 tg;
driver_drive(&tg); driver_drive(&tg);
} }