Add support for clock ticks
This commit is contained in:
@@ -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) {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
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() {
|
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,22 +332,21 @@ 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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,27 +2,34 @@
|
|||||||
#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) {
|
||||||
|
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:
|
public:
|
||||||
std::vector<UniqueChannel> channels_;
|
std::vector<UniqueChannel> channels_;
|
||||||
virtual void event_init() {
|
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);
|
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() {
|
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);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user