|
|
nono 1.7.0
//
// nono
// Copyright (C) 2021 nono project
// Licensed under nono-license.txt
//
//
// ホストデバイス (基本クラス)
//
#include "hostdevice.h"
#include "config.h"
#include "scheduler.h"
// コンストラクタ
HostDevice::HostDevice(Device *parent_, uint objid_,
const std::string& portname_)
: inherited(objid_)
{
parent = parent_;
SetPortName(portname_);
}
// デストラクタ
HostDevice::~HostDevice()
{
TerminateThread();
}
// 動的コンストラクションその2
bool
HostDevice::Create2()
{
int fds[2];
int r;
kq = kqueue();
if (kq < 0) {
putmsg(0, "kqueue: %s", strerror(errno));
return false;
}
// VM スレッドからの連絡用パイプ
r = pipe(fds);
if (r < 0) {
putmsg(0, "pipe: %s", strerror(errno));
return false;
}
rpipe = fds[0];
wpipe = fds[1];
if (kevent_add(kq, rpipe, EVFILT_READ, EV_ADD, DATA_FROM_VM) < 0) {
putmsg(0, "kevent_add: %s", strerror(errno));
return false;
}
return true;
}
// 設定ファイルキーのプレフィックス ("hostcomX" とか) を返す
std::string
HostDevice::GetConfigKey() const
{
return string_tolower(GetName());
}
// 外部からの読み込みディスクリプタを登録
int
HostDevice::AddOuter(int fd)
{
return kevent_add(kq, fd, EVFILT_READ, EV_ADD, DATA_FROM_OUTER);
}
// 外部からの読み込みディスクリプタを登録解除。
//
// *BSD では kqueue(2) に登録したディスクリプタがクローズされると自動的に
// 削除されるので本来この操作は不要だが、libkqueue などユーザランドで実装
// してある互換ライブラリでは実現方法がなく自動的に削除されない。
// そのためディスクリプタをクローズする前にこれを呼ぶこと。
// see https://github.com/mheily/libkqueue/blob/master/BUGS.md
int
HostDevice::DelOuter(int fd)
{
return kevent_add(kq, fd, EVFILT_READ, EV_DELETE, DATA_FROM_OUTER);
}
// Listen ソケットを登録
int
HostDevice::AddListen(int ls, int action)
{
return kevent_add(kq, ls, EVFILT_READ, action, LISTEN_SOCKET);
}
// Listen ソケットを登録解除。
int
HostDevice::DelListen(int ls)
{
return kevent_add(kq, ls, EVFILT_READ, EV_DELETE, LISTEN_SOCKET);
}
// 受信通知コールバックを解除
void
HostDevice::ResetRxCallback()
{
rx_func = NULL;
}
// 受信通知コールバックを設定
void
HostDevice::SetRxCallback(DeviceCallback_t func, uint32 arg)
{
rx_func = func;
rx_arg = arg;
}
// 着信通知コールバックを設定
void
HostDevice::SetAcceptCallback(DeviceCallback_t func)
{
accept_func = func;
}
// スレッド実行
void
HostDevice::ThreadRun()
{
SetThreadAffinityHint(AffinityClass::Light);
for (; exit_requested == false; ) {
struct kevent kev;
int r;
putlog(3, "polling");
r = kevent_poll(kq, &kev, 1, NULL);
if (r < 0) {
if (errno == EINTR) {
continue;
}
// XXX どうする?
putlog(0, "ThreadRun: kevent_poll: %s", strerror(errno));
return;
}
assert(r > 0);
int udata = EV_UDATA2INT(kev.udata);
putlog(3, "polled: udata=%d", udata);
Dispatch(udata);
}
}
void
HostDevice::Dispatch(int udata)
{
int n;
if (udata == DATA_FROM_VM) {
// VM からの送信パイプに着信があった
char buf[1];
n = read(rpipe, buf, sizeof(buf));
if (n < 0) {
// XXX どうする?
putlog(0, "Dispatch: read: %s", strerror(errno));
exit_requested = true;
return;
}
if (n == 0) {
// EOF なら終了要求
exit_requested = true;
return;
}
if (__predict_false(buf[0] != PIPE_TX)) {
SelectDriver(false);
} else {
// 外部に送信 (継承クラスによる)
Write();
}
} else if (udata == DATA_FROM_OUTER) {
// 外部から着信があった
// キューに1個以上データが投入されたら VM に通知
n = Read();
if (n > 0) {
if (rx_func) {
(parent->*rx_func)(rx_arg);
}
}
}
}
// data (のうち下位8bit) をパイプに書き込む。(VM スレッドで呼ばれる)
bool
HostDevice::WritePipe(uint32 data)
{
uint8 buf[1];
buf[0] = data;
if (write(wpipe, buf, sizeof(buf)) < 1) {
return false;
}
return true;
}
// スレッド終了指示
void
HostDevice::Terminate()
{
// wpipe を閉じると rpipe が EOF になって最終的にスレッドが終了する。
wpipe.Close();
}
// ログ表示。
// ホストデバイスでは、仮想時間とオブジェクト名を表示する。PC は表示しない。
void
HostDevice::putlogn(const char *fmt, ...) const
{
char buf[1024];
va_list ap;
int len;
uint64 vt = scheduler->GetVirtTime();
len = snprintf(buf, sizeof(buf), "%16s %s ",
SecToStr(vt).c_str(),
GetName().c_str());
va_start(ap, fmt);
vsnprintf(buf + len, sizeof(buf) - len, fmt, ap);
va_end(ap);
WriteLog(buf);
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.