Annotation of nono/host/logger.cpp, revision 1.1.1.2

1.1       root        1: //
                      2: // nono
                      3: // Copyright (C) 2025 nono project
                      4: // Licensed under nono-license.txt
                      5: //
                      6: 
                      7: //
                      8: // ログ
                      9: //
                     10: 
1.1.1.2 ! root       11: // Logger はモニタを持つため Object から派生しているが、(Object のもう一つの
        !            12: // 機能である) ログ出力は出来ない。これは自明。
        !            13: 
1.1       root       14: #include "logger.h"
                     15: #include "monitor.h"
                     16: #include "mythread.h"
                     17: #include "sjis.h"
                     18: #include "textscreen.h"
                     19: 
                     20: // コンストラクタ
                     21: Logger::Logger()
                     22:        : inherited(OBJ_LOGGER)
                     23: {
1.1.1.2 ! root       24:        ClearAlias();
        !            25: 
1.1       root       26:        monitor = gMonitorManager->Regist(ID_MONITOR_LOG, this);
                     27:        monitor->func = ToMonitorCallback(&Logger::MonitorUpdate);
                     28:        monitor->SetSize(80, 40);       // 初期サイズは適当
                     29: }
                     30: 
                     31: // デストラクタ
                     32: Logger::~Logger()
                     33: {
                     34:        TerminateThread();
                     35: }
                     36: 
                     37: // UTF-8 -> Shift_JIS 変換関数を登録する。GUI が登録する。
                     38: void
                     39: Logger::SetConverter(std::string (*conv)(const std::string&))
                     40: {
                     41:        utf8_to_sjis_converter = conv;
                     42: }
                     43: 
                     44: // スレッドの開始。
                     45: bool
                     46: Logger::StartThread()
                     47: {
                     48:        if (ResizeCol(monitor->GetCol()) == false) {
                     49:                return false;
                     50:        }
                     51: 
1.1.1.2 ! root       52:        // スレッド起動。
1.1       root       53:        std::lock_guard<std::mutex> lock(thread_starter);
                     54:        try {
1.1.1.2 ! root       55:                thread.reset(new std::thread(&Logger::OnStart, this));
1.1       root       56:        } catch (...) { }
                     57:        if ((bool)thread == false) {
                     58:                warnx("Failed to initialize thread at %s", __method__);
                     59:                return false;
                     60:        }
                     61:        return true;
                     62: }
                     63: 
1.1.1.2 ! root       64: // 開始されたスレッドでのエントリポイント。
        !            65: void
        !            66: Logger::OnStart()
        !            67: {
        !            68:        // このスレッドにスレッド名を設定。
        !            69:        static const char name[] = "Logger";
        !            70:        PTHREAD_SETNAME(name);
        !            71: 
        !            72:        auto thman = gMainApp.GetThreadManager();
        !            73:        thman->RegistThread(this, name);
        !            74: 
        !            75:        // 実行開始。
        !            76:        std::lock_guard<std::mutex> lock_sub(this->thread_starter);
        !            77:        ThreadRun();
        !            78: }
        !            79: 
1.1       root       80: // スレッドを終了させる
                     81: void
                     82: Logger::TerminateThread()
                     83: {
                     84:        if ((bool)thread) {
1.1.1.2 ! root       85:                auto thman = gMainApp.GetThreadManager();
        !            86:                thman->UnregistThread(this);
        !            87: 
1.1       root       88:                Terminate();
                     89: 
                     90:                if (thread->joinable()) {
                     91:                        thread->join();
                     92:                }
                     93: 
                     94:                thread.reset();
                     95:        }
                     96: }
                     97: 
                     98: // スレッドの終了を指示
                     99: void
                    100: Logger::Terminate()
                    101: {
                    102:        std::lock_guard<std::mutex> lock(queue_mtx);
                    103:        request |= REQ_TERMINATE;
                    104:        cv.notify_one();
                    105: }
                    106: 
                    107: // ログ書き込み。
                    108: // 他スレッドから呼ばれる。
                    109: void
                    110: Logger::Write(const char *str)
                    111: {
                    112:        std::lock_guard<std::mutex> lock(queue_mtx);
                    113:        queue.emplace_back(str);
                    114:        request |= REQ_QUEUE;
                    115:        cv.notify_one();
                    116: }
                    117: 
                    118: void
                    119: Logger::ThreadRun()
                    120: {
                    121:        SetThreadAffinityHint(AffinityClass::Light);
                    122: 
                    123:        for (;;) {
                    124:                std::string utf8str;
                    125: 
                    126:                // 何か起きるまで待つ。
                    127:                {
                    128:                        std::unique_lock<std::mutex> lock(queue_mtx);
                    129:                        cv.wait(lock, [&]{ return (request != 0); });
                    130: 
                    131:                        if (__predict_false((request & REQ_TERMINATE))) {
                    132:                                // 終了するので、他の条件は無視
                    133:                                break;
                    134:                        }
                    135:                        // ここは (request == REQ_QUEUE) が成立しているはず。
                    136:                        if (__predict_false(queue.empty())) {
                    137:                                request = 0;
                    138:                                continue;
                    139:                        } else {
                    140:                                utf8str = queue.front();
                    141:                                queue.pop_front();
                    142:                                if (queue.empty()) {
                    143:                                        request = 0;
                    144:                                }
                    145:                        }
                    146:                }
                    147: 
                    148:                if (__predict_true(utf8str.empty() == false)) {
                    149:                        DoLog(utf8str);
                    150:                }
                    151:        }
                    152: }
                    153: 
                    154: // 着信処理。
                    155: void
                    156: Logger::DoLog(const std::string& utf8str)
                    157: {
                    158:        // 必要なら標準出力にも出力
                    159:        if (use_stdout) {
                    160:                puts(utf8str.c_str());
                    161:        }
                    162: 
                    163:        // コンバータがなければここで終了 (CLI)。
                    164:        // GUI でも万が一 dispbuf がなければ、これ以上出来ることはない。
                    165:        if (__predict_false(utf8_to_sjis_converter == NULL)) {
                    166:                return;
                    167:        }
                    168:        if (__predict_false(dispbuf.empty())) {
                    169:                return;
                    170:        }
                    171: 
                    172:        // Shift_JIS に変換。
                    173:        std::string sjis_str = utf8_to_sjis_converter(utf8str);
                    174: 
                    175:        // 入力の1文字列が改行を含んでいれば行を分解。
                    176:        std::vector<std::string> lines = string_split(sjis_str, '\n');
                    177: 
                    178:        {
                    179:                std::lock_guard<std::mutex> lock(backend_mtx);
                    180: 
                    181:                // 1行ずつバックログに追加。
                    182:                for (const auto& str : lines) {
                    183:                        AddLog(str);
                    184:                }
                    185:        }
                    186: }
                    187: 
                    188: // バックログに str を追加。
                    189: // str は Shift_JIS で改行を含まないこと。
                    190: void
                    191: Logger::AddLog(const std::string& str)
                    192: {
                    193:        // バックログに追加。埋まっていれば古いのを追い出す。
                    194:        if (__predict_true(logs.size() >= maxlines)) {
                    195:                logs.pop_front();
                    196:        }
                    197:        logs.emplace_back(str);
                    198: 
                    199:        // ディスプレイバッファにレンダリング。
                    200:        AddDisplay(str);
                    201: }
                    202: 
                    203: // ディスプレイバッファにレンダリングする。
                    204: // TAB 文字は展開されず 0x09 のグリフが表示される。
                    205: // backend_mtx 内で呼ぶこと。
                    206: void
                    207: Logger::AddDisplay(const std::string& str)
                    208: {
                    209:        assert(dispbuf.empty() == false);
                    210: 
                    211:        uint8 *d = &dispbuf[current * col];
                    212:        uint x = 0;
                    213:        for (const char *s = str.c_str(); ; ) {
                    214:                uint8 c = s[x];
                    215:                if (__predict_false(c == '\0')) {
                    216:                        // 改行へ。
                    217:                } else if (SJIS::IsHankaku(c) || s[x + 1] == '\0') {
                    218:                        // 半角。(半角でない判定でも次が終端文字なら半角扱い)
                    219:                        x++;
                    220:                        if (__predict_true(x < col)) {
                    221:                                continue;
                    222:                        }
                    223:                } else if (x < col - 1) {
                    224:                        x += 2;
                    225:                        if (__predict_true(x < col)) {
                    226:                                continue;
                    227:                        }
                    228:                } else {
                    229:                        // 2バイト文字がはみ出すなら改行。
                    230:                }
                    231: 
                    232:                // s から d に x 文字出力して、改行。
                    233:                memcpy(d, s, x);
                    234:                if (x < col) {
                    235:                        // 余った右側は空白で埋める。
                    236:                        memset(d + x, ' ', col - x);
                    237:                }
                    238:                s += x;
                    239:                x = 0;
                    240: 
                    241:                // 行送り。
                    242:                current = (current + 1) % maxlines;
                    243:                d = &dispbuf[current * col];
                    244:                if (__predict_false(displines < maxlines)) {
                    245:                        displines++;
                    246:                }
                    247: 
                    248:                // 行を出力したあとで、改めて s が終端なら終了。
                    249:                if (*s == '\0') {
                    250:                        break;
                    251:                }
                    252:        }
                    253: }
                    254: 
                    255: // ログの表示桁数を変更(再設定)する。
                    256: // 0 なら表示用バッファなし。
                    257: bool
                    258: Logger::ResizeCol(int newcol)
                    259: {
                    260:        std::lock_guard<std::mutex> lock(backend_mtx);
                    261: 
                    262:        size_t buflen = maxlines * newcol;
                    263:        try {
                    264:                dispbuf.resize(buflen);
                    265:        } catch (...) {
                    266:                // もう出来ることはない…。
                    267:                warnx("%s: Failed to allocate %zu bytes", __method__, buflen);
                    268:                dispbuf.resize(0);
                    269:                return false;
                    270:        }
                    271: 
                    272:        col = newcol;
                    273: 
                    274:        if (col != 0) {
                    275:                // 今あるバッファを全部書き直す。
                    276:                current = 0;
                    277:                displines = 0;
                    278:                for (const auto& str : logs) {
                    279:                        AddDisplay(str);
                    280:                }
                    281:        }
                    282: 
                    283:        return true;
                    284: }
                    285: 
                    286: // screen.userdata はログの最新から何行手前を screen の底の行にコピーするか
                    287: // を示す。
                    288: // userdata = 0 なら screen の底が最新行。
                    289: // userdata = 1 なら1行古いほうに遡った状態、となる。
                    290: // screen.userdata + screen の行数が maxlines を超えてはいけない。
                    291: void
                    292: Logger::MonitorUpdate(Monitor *, TextScreen& screen)
                    293: {
                    294:        assertmsg(screen.userdata + screen.GetRow() <= maxlines,
                    295:                "userdata=%" PRIu64 " row=%d maxlines=%d",
                    296:                screen.userdata, screen.GetRow(), maxlines);
                    297:        assertmsg(screen.GetCol() == col,
                    298:                "GetCol=%d col=%d", screen.GetCol(), col);
                    299: 
                    300:        // ここのモニタは常に Ring モードで書き込む。
                    301:        // 本当はこれを呼び出す側が一度初期化しておくだけでいいのだが
                    302:        // ここでやるほうが簡単なので。
                    303:        screen.Mode = TextScreen::Ring;
                    304: 
                    305:        std::lock_guard<std::mutex> lock(backend_mtx);
                    306: 
                    307:        // 万が一 dispbuf が空なら出来ることはない。
                    308:        if (__predict_false(dispbuf.empty())) {
                    309:                return;
                    310:        }
                    311: 
                    312:        // nlines はコピーする行数。
                    313:        int nlines = std::min(displines, screen.GetRow());
                    314:        if (nlines < screen.GetRow()) {
                    315:                // ログが画面高さ分埋まってなければ、画面をクリア。
                    316:                screen.Clear();
                    317:        } else {
                    318:                // 全文字上書きするのでクリア不要。
                    319:                screen.Locate(0, 0);
                    320:        }
                    321: 
                    322:        // dispy は dispbuf のコピー開始位置 (行単位)。
                    323:        int dispy = current - (screen.userdata + nlines);
                    324:        dispy = (dispy + maxlines) % maxlines;
                    325: 
                    326:        // 1行ずつコピー。
                    327:        for (uint y = 0; y < nlines; y++) {
                    328:                uint8 *dp = &dispbuf[dispy * col];
                    329:                for (uint x = 0; x < col; x++) {
                    330:                        screen.Putc(*dp++);
                    331:                }
                    332:                dispy++;
                    333:                if (__predict_false(dispy >= maxlines)) {
                    334:                        dispy = 0;
                    335:                }
                    336:        }
                    337: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.