[C++] 357 ChatGPTアプリの製作 その42 “libcurlによるHTTP通信”の中断 curl_easy_cleanup 

[Mac M2 Pro 12CPU, Sonoma 14.3.1, clang++ 15.0.0, FLTK 1.3.8]

中断ボタンでlibcurlによるHTTP通信をabortしようとしましたが、かなり手こずりました。

C++では難しいためSwiftへの移植も考えたものの、相当な時間を掛けることになるのでもう一粘りしたところ、あっさり解決しました。

CURL* curl = curl_easy_init();のcurlをグローバル変数にして、中断コールバック関数内でcurl_easy_cleanup(curl);とすれば中断できるようになりました。

シングルスレッドであるFLTKにて送信ボタン押下状態で中断ボタンを押せるようにするにはstd::threadを用い送信コールバック関数(HTTP通信)を別スレッド化する必要があります。

最初は別スレッド化したHTTP通信のプロセスID(PID)をpgrepコマンドで探し、killコマンドで中断しようとしました。しかしstd::threadでは同じプロセスIDをメインスレッドと共用しているため、その方法は使えませんでした。

昨年2023/3/2にChatGPTアプリ開発に着手してから、ずっと抱えていた懸案をようやく解決できました。

libcurlでHTTP通信が容易に中断できない問題はStackOverFlowサイトでも暗礁に乗り上げていたので意外な結末でした。ChatGPTに聞いてもサンプルコードが少ないためか、珍しく迷回答頻発でした。

#include <thread>
#include <future>
#include <curl/curl.h>

extern CURL* curl;

void sendCBWrapper(Fl_Widget* w, void* v) {
    // promiseとfutureを作成
    std::promise<void> p;
    std::future<void> f = p.get_future();
    
    // sendCBを新しいスレッドで実行し、promiseを渡す
    std::thread([w, v, p = std::move(p)]() mutable {
        sendCB(w, v, std::move(p));
    }).detach();
    
    // 別のスレッドでfutureの結果を待機
    std::thread([f = std::move(f)]() mutable {
        f.wait(); // sendCBの終了を待機
        std::system("afplay /System/Library/Sounds/Submarine.aiff"); // システム音を鳴らす
        cout << "sendCB終了" << endl;
    }).detach();
}

void abortCB(Fl_Widget*, void*){
    if (curl){
        curl_easy_cleanup(curl);
        statusBox -> changeColor(YELLOW);
    } else {
        cout << "送信していません" << endl;
    }
}