|
|
1.1 ! root 1: // ! 2: // nono ! 3: // Copyright (C) 2021 nono project ! 4: // Licensed under nono-license.txt ! 5: // ! 6: ! 7: // ! 8: // ホストデバイス (基本クラス) ! 9: // ! 10: ! 11: #include "hostdevice.h" ! 12: #include "scheduler.h" ! 13: #include "signalthread.h" ! 14: ! 15: // コンストラクタ ! 16: HostDevice::HostDevice(const std::string& objname_) ! 17: : inherited(objname_) ! 18: { ! 19: } ! 20: ! 21: // デストラクタ ! 22: HostDevice::~HostDevice() ! 23: { ! 24: TerminateThread(); ! 25: } ! 26: ! 27: // 初期化 ! 28: bool ! 29: HostDevice::Init() ! 30: { ! 31: int fds[2]; ! 32: int r; ! 33: ! 34: kq = kqueue(); ! 35: if (kq < 0) { ! 36: putmsg(0, "kqueue: %s", strerror(errno)); ! 37: return false; ! 38: } ! 39: ! 40: // VM スレッドからの連絡用パイプ ! 41: r = pipe(fds); ! 42: if (r < 0) { ! 43: putmsg(0, "pipe: %s", strerror(errno)); ! 44: return false; ! 45: } ! 46: rpipe = fds[0]; ! 47: wpipe = fds[1]; ! 48: ! 49: if (kevent_add(kq, rpipe, EVFILT_READ, EV_ADD, DATA_FROM_VM) < 0) { ! 50: putmsg(0, "kevent_add: %s", strerror(errno)); ! 51: return false; ! 52: } ! 53: ! 54: return true; ! 55: } ! 56: ! 57: // 設定ファイルキーのプレフィックス ("hostcomX" とか) を返す ! 58: std::string ! 59: HostDevice::GetConfigKey() const ! 60: { ! 61: return string_tolower(GetName()); ! 62: } ! 63: ! 64: // 外部からの読み込みディスクリプタを登録 ! 65: int ! 66: HostDevice::AddOuter(int fd) ! 67: { ! 68: return kevent_add(kq, fd, EVFILT_READ, EV_ADD, DATA_FROM_OUTER); ! 69: } ! 70: ! 71: // 外部からの読み込みディスクリプタを登録解除。 ! 72: // ! 73: // *BSD では kqueue(2) に登録したディスクリプタがクローズされると自動的に ! 74: // 削除されるので本来この操作は不要だが、libkqueue などユーザランドで実装 ! 75: // してある互換ライブラリでは実現方法がなく自動的に削除されない。 ! 76: // そのためディスクリプタをクローズする前にこれを呼ぶこと。 ! 77: // see https://github.com/mheily/libkqueue/blob/master/BUGS.md ! 78: int ! 79: HostDevice::DelOuter(int fd) ! 80: { ! 81: return kevent_add(kq, fd, EVFILT_READ, EV_DELETE, DATA_FROM_OUTER); ! 82: } ! 83: ! 84: // シグナルスレッドからのパイプを登録 ! 85: int ! 86: HostDevice::AddSignal(int rfd, int wfd) ! 87: { ! 88: int r; ! 89: ! 90: r = kevent_add(kq, rfd, EVFILT_READ, EV_ADD, DATA_FROM_SIGNAL); ! 91: if (r < 0) { ! 92: return r; ! 93: } ! 94: // シグナルスレッドに登録。 ! 95: gSignalThread->AddPipe(wfd); ! 96: ! 97: return 0; ! 98: } ! 99: ! 100: // Listen ソケットを登録 ! 101: int ! 102: HostDevice::AddListen(int ls, int action) ! 103: { ! 104: return kevent_add(kq, ls, EVFILT_READ, action, LISTEN_SOCKET); ! 105: } ! 106: ! 107: // コールバック先デバイスを設定。 ! 108: // ! 109: // コールバックはデバイスと関数が両方設定されている時だけ呼び出すので、 ! 110: // 設定時は、コールバック関数設定後に SetCallbackDevice() でデバイスを設定、 ! 111: // 解除時は、まず SetCallbackDevice(NULL) でデバイスを解除、 ! 112: // の順にすること。 ! 113: void ! 114: HostDevice::SetCallbackDevice(Device *dev_) ! 115: { ! 116: dev = dev_; ! 117: } ! 118: ! 119: // 受信通知コールバックを設定 ! 120: void ! 121: HostDevice::SetRxCallback(DeviceCallback_t func) ! 122: { ! 123: rx_func = func; ! 124: } ! 125: ! 126: // 着信通知コールバックを設定 ! 127: void ! 128: HostDevice::SetAcceptCallback(DeviceCallback_t func) ! 129: { ! 130: accept_func = func; ! 131: } ! 132: ! 133: // スレッド実行 ! 134: void ! 135: HostDevice::ThreadRun() ! 136: { ! 137: for (; exit_requested == false; ) { ! 138: struct kevent kev; ! 139: int r; ! 140: ! 141: putlog(3, "polling"); ! 142: ! 143: r = kevent_poll(kq, &kev, 1, NULL); ! 144: if (r < 0) { ! 145: if (errno == EINTR) { ! 146: continue; ! 147: } ! 148: // XXX どうする? ! 149: putlog(0, "ThreadRun: kevent_poll: %s", strerror(errno)); ! 150: return; ! 151: } ! 152: assert(r > 0); ! 153: int udata = EV_UDATA2INT(kev.udata); ! 154: putlog(3, "polled: udata=%d", udata); ! 155: ! 156: Dispatch(udata); ! 157: } ! 158: } ! 159: ! 160: void ! 161: HostDevice::Dispatch(int udata) ! 162: { ! 163: int n; ! 164: ! 165: if (udata == DATA_FROM_VM) { ! 166: // VM からの送信パイプに着信があった ! 167: char buf[1]; ! 168: ! 169: n = read(rpipe, buf, sizeof(buf)); ! 170: if (n < 0) { ! 171: // XXX どうする? ! 172: putlog(0, "Dispatch: read: %s", strerror(errno)); ! 173: exit_requested = true; ! 174: return; ! 175: } ! 176: if (n == 0) { ! 177: // EOF なら終了要求 ! 178: exit_requested = true; ! 179: return; ! 180: } ! 181: ! 182: // 外部に送信 (継承クラスによる) ! 183: Write(buf[0]); ! 184: ! 185: } else if (udata == DATA_FROM_OUTER) { ! 186: // 外部から着信があった ! 187: ! 188: // キューに1個以上データが投入されたら VM に通知 ! 189: n = Read(); ! 190: if (n > 0) { ! 191: if (dev && rx_func) { ! 192: (dev->*rx_func)(); ! 193: } ! 194: } ! 195: } ! 196: } ! 197: ! 198: // data (のうち下位8bit) をパイプに書き込む。(VM スレッドで呼ばれる) ! 199: bool ! 200: HostDevice::WritePipe(uint32 data) ! 201: { ! 202: uint8 buf[1]; ! 203: ! 204: buf[0] = data; ! 205: if (write(wpipe, buf, sizeof(buf)) < 1) { ! 206: return false; ! 207: } ! 208: return true; ! 209: } ! 210: ! 211: // スレッド終了指示 ! 212: void ! 213: HostDevice::Terminate() ! 214: { ! 215: // wpipe を閉じると rpipe が EOF になって最終的にスレッドが終了する。 ! 216: wpipe.Close(); ! 217: } ! 218: ! 219: // ログ表示。 ! 220: // ホストデバイスでは、仮想時間とオブジェクト名を表示する。PC は表示しない。 ! 221: void ! 222: HostDevice::putlogn(const char *fmt, ...) const ! 223: { ! 224: char buf[1024]; ! 225: va_list ap; ! 226: int len; ! 227: ! 228: uint64 vt = gScheduler->GetVirtTime(); ! 229: len = snprintf(buf, sizeof(buf), "%4d.%03d'%03d'%03d %s ", ! 230: (int)(vt / 1000 / 1000 / 1000), ! 231: (int)((vt / 1000 / 1000) % 1000), ! 232: (int)((vt / 1000) % 1000), ! 233: (int)(vt % 1000), ! 234: GetName().c_str()); ! 235: ! 236: va_start(ap, fmt); ! 237: vsnprintf(buf + len, sizeof(buf) - len, fmt, ap); ! 238: va_end(ap); ! 239: ! 240: WriteLog(buf); ! 241: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.