比特币源码解析(17)

来源:互联网 发布:知乎个人封面图片 编辑:程序博客网 时间:2024/06/05 15:08

0x01 Step 4a - Continue

CScheduler 实现

    // Start the lightweight task scheduler thread    CScheduler::Function serviceLoop = boost::bind(&CScheduler::serviceQueue, &scheduler);    threadGroup.create_thread(boost::bind(&TraceThread<CScheduler::Function>, "scheduler", serviceLoop));

首先通过bind函数将函数绑定到对象,在前面http://blog.csdn.net/pure_lady/article/details/77675915#t2已经介绍过bind的基本用法,同时也介绍了当bind的第一个参数为类中的成员函数时,后面的第一个参数必须是类的对象,这里的第一句代码就是绑定类成员函数serviceQueue到函数对象serviceLoop,然后通过线程组threadGroup创建新的线程,线程的执行函数为bind返回的函数对象,其中TraceThread的定义如下,

// src/util.h  line 299/** * .. and a wrapper that just calls func once */template <typename Callable> void TraceThread(const char* name,  Callable func){    std::string s = strprintf("bitcoin-%s", name);    RenameThread(s.c_str());    try    {        LogPrintf("%s thread start\n", name);        func();        LogPrintf("%s thread exit\n", name);    }    catch (const boost::thread_interrupted&)    {        LogPrintf("%s thread interrupt\n", name);        throw;    }    catch (const std::exception& e) {        PrintExceptionContinue(&e, name);        throw;    }    catch (...) {        PrintExceptionContinue(nullptr, name);        throw;    }}

该函数通过template <typename Callable>来定义后面传入函数的类型,所以在实际调用时传入的函数类型为CScheduer::Function,实例对象为serviceLoop,第一个参数为“scheduler” 。该函数的作用是重命名线程并将调用函数执行一次,也就是serviceLoop函数,实际上也就是CScheduler中的serviceQueue函数,再来看看这个函数是怎么实现的,

void CScheduler::serviceQueue(){    boost::unique_lock<boost::mutex> lock(newTaskMutex);    ++nThreadsServicingQueue;    // newTaskMutex is locked throughout this loop EXCEPT    // when the thread is waiting or when the user's function    // is called.    while (!shouldStop()) {        try {            if (!shouldStop() && taskQueue.empty()) {                reverse_lock<boost::unique_lock<boost::mutex> > rlock(lock);                // Use this chance to get a tiny bit more entropy                RandAddSeedSleep();            }            while (!shouldStop() && taskQueue.empty()) {                // Wait until there is something to do.                newTaskScheduled.wait(lock);            }            // Wait until either there is a new task, or until            // the time of the first item on the queue:// wait_until needs boost 1.50 or later; older versions have timed_wait:#if BOOST_VERSION < 105000            while (!shouldStop() && !taskQueue.empty() &&                   newTaskScheduled.timed_wait(lock, toPosixTime(taskQueue.begin()->first))) {                // Keep waiting until timeout            }#else            // Some boost versions have a conflicting overload of wait_until that returns void.            // Explicitly use a template here to avoid hitting that overload.            while (!shouldStop() && !taskQueue.empty()) {                boost::chrono::system_clock::time_point timeToWaitFor = taskQueue.begin()->first;                if (newTaskScheduled.wait_until<>(lock, timeToWaitFor) == boost::cv_status::timeout)                    break; // Exit loop after timeout, it means we reached the time of the event            }#endif            // If there are multiple threads, the queue can empty while we're waiting (another            // thread may service the task we were waiting on).            if (shouldStop() || taskQueue.empty())                continue;            Function f = taskQueue.begin()->second;            taskQueue.erase(taskQueue.begin());            {                // Unlock before calling f, so it can reschedule itself or another task                // without deadlocking:                reverse_lock<boost::unique_lock<boost::mutex> > rlock(lock);                f();            }        } catch (...) {            --nThreadsServicingQueue;            throw;        }    }    --nThreadsServicingQueue;    newTaskScheduled.notify_one();}

该函数首先定义了一个unique_lock来保证整个函数是线程安全的,接着是一个while循环,循环中通过shouldStop判断是否该终止循环,shouldStop的实现如下,

bool shouldStop() const { return stopRequested || (stopWhenEmpty && taskQueue.empty()); }

如果stopRequested被设置为true或者队列为空且stopWhenEmpty被设置为true,那么shouldStop就返回true表示循环该停止了,这也是多线程中简单的通过变量来控制其他线程终止的方式。回到serviceQueue函数,首先通过一个条件语句判断,如果当前没有任务,那么随机的Sleep一段时间,来增加系统的随机熵;这一步完成后,如果依然没有任务,那么就通过wait函数进行阻塞等待唤醒,这里newTaskScheduled变量类型又是我们之前介绍过的condition_variable。等到有新的任务了,也就是taskQueue不为空了,注意这里涉及到的任务包含两种类型:(1)等待delta时间后,只执行一次;(2)等待delta时间后,每隔delta时间后都执行一次。这两种类型分别通过scheduleFromNow()scheduleEvery()函数进行调用。所以每个任务都包含两个变量,实现时通过pair<time_point, Function>来进行插入,所以源码这里发现有新的任务添加后,首先获取新任务的第一个变量也就是时间,进行等待,实现时根据boost不同的版本有不同的等待方式;等待完之后,获取任务的第二个参数,也就是要执行的函数,并从任务队列中删除该任务,此时所有的共享变量(taskQueue)都已经访问完了,所以在执行调用函数之前,可以先解锁,而该函数已经从列队中删除了,所以也就只可能执行一次。

注册后台处理信号

    GetMainSignals().RegisterBackgroundSignalScheduler(scheduler);

这句代码中的GetMainSignals()返回一个CMainSignals类型的静态变量g_signals,然后调用这个类中的一个成员函数RegisterBackgroundSingalScheduler(),通过这个函数将CMainSignals类中的unique_ptr<MainSignalsInstance>指针类型的成员变量m_internals赋值为一个新建的对象,这个新建的对象类型为结构体MainSignalsInstance,这个结构体包含了许多的信号,

struct MainSignalsInstance {    boost::signals2::signal<void (const CBlockIndex *, const CBlockIndex *, bool fInitialDownload)> UpdatedBlockTip;    boost::signals2::signal<void (const CTransactionRef &)> TransactionAddedToMempool;    boost::signals2::signal<void (const std::shared_ptr<const CBlock> &, const CBlockIndex *pindex, const std::vector<CTransactionRef>&)> BlockConnected;    boost::signals2::signal<void (const std::shared_ptr<const CBlock> &)> BlockDisconnected;    boost::signals2::signal<void (const CBlockLocator &)> SetBestChain;    boost::signals2::signal<void (const uint256 &)> Inventory;    boost::signals2::signal<void (int64_t nBestBlockTime, CConnman* connman)> Broadcast;    boost::signals2::signal<void (const CBlock&, const CValidationState&)> BlockChecked;    boost::signals2::signal<void (const CBlockIndex *, const std::shared_ptr<const CBlock>&)> NewPoWValidBlock;    // We are not allowed to assume the scheduler only runs in one thread,    // but must ensure all callbacks happen in-order, so we end up creating    // our own queue here :(    SingleThreadedSchedulerClient m_schedulerClient;    explicit MainSignalsInstance(CScheduler *pscheduler) : m_schedulerClient(pscheduler) {}};

从这些信号的名称可以看出都是与节点本地区块链运行相关的操作,在后面也必然会有相应的触发,这里只是设置好相应的变量。

启动RPCServer、HTTPServer

    /* Start the RPC server already.  It will be started in "warmup" mode     * and not really process calls already (but it will signify connections     * that the server is there and will be ready later).  Warmup mode will     * be disabled when initialisation is finished.     */    if (gArgs.GetBoolArg("-server", false))    {        uiInterface.InitMessage.connect(SetRPCWarmupStatus);        if (!AppInitServers(threadGroup))            return InitError(_("Unable to start HTTP server. See debug log for details."));    }

-server:接收命令行命令和JSON-RPC命令。

如果在命令行中指定了-server命令,那么就表示要启动相关的server服务来处理client发送过来的命令,启动之前首先先给InitMessage信号添加一个新的执行函数,该函数实现如下

void SetRPCWarmupStatus(const std::string& newStatus){    LOCK(cs_rpcWarmup);    rpcWarmupStatus = newStatus;}

rpcWarmupStatus是一个静态string类型的全局变量,所以该函数的作用就是将新的参数值赋给rpcWarmupStatus变量。

AppInitServers

接下来调用AppInitServers来初始化各种服务器,实现如下

bool AppInitServers(boost::thread_group& threadGroup){    RPCServer::OnStarted(&OnRPCStarted);    RPCServer::OnStopped(&OnRPCStopped);    RPCServer::OnPreCommand(&OnRPCPreCommand);    if (!InitHTTPServer())        return false;    if (!StartRPC())        return false;    if (!StartHTTPRPC())        return false;    if (gArgs.GetBoolArg("-rest", DEFAULT_REST_ENABLE) && !StartREST())        return false;    if (!StartHTTPServer())        return false;    return true;}

首先调用RPCServer类中的三个函数分别连接三个函数到三个信号槽,而这三个函数又分别做了一些信号连接工作,实现如下,

void OnRPCStarted(){    uiInterface.NotifyBlockTip.connect(&RPCNotifyBlockChange);}void OnRPCStopped(){    uiInterface.NotifyBlockTip.disconnect(&RPCNotifyBlockChange);    RPCNotifyBlockChange(false, nullptr);    cvBlockChange.notify_all();    LogPrint(BCLog::RPC, "RPC stopped.\n");}void OnRPCPreCommand(const CRPCCommand& cmd){    // Observe safe mode    std::string strWarning = GetWarnings("rpc");    if (strWarning != "" && !gArgs.GetBoolArg("-disablesafemode", DEFAULT_DISABLE_SAFEMODE) &&        !cmd.okSafeMode)        throw JSONRPCError(RPC_FORBIDDEN_BY_SAFE_MODE, std::string("Safe mode: ") + strWarning);}

OnRPCStarted负责将RPCNotifyBlockChange连接到NotifyBlockTip信号上;而OnRPCStopped负责将连接解除,并做一些其他的清除工作;最后一个OnRPCPreCommand检查在安全模式下是否有警告消息,如果有那么就抛出相应的异常。

接下来几个函数分别初始化和启动几个不同的server:

  • InitHTTPServer():初始化http server.
  • StartRPC():启动RPC服务
  • StartHTTPRPC():启动HTTP RPC服务
  • StartREST():启动REST
  • StartHTTPServer():启动HTTP server.

将在后面章节详细介绍它们各自的具体实现方式。

原创粉丝点击