|
|
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.