比特币源码解析(17) - 可执行程序 - Bitcoind

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u012183589/article/details/78087146

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.

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

猜你喜欢

转载自blog.csdn.net/u012183589/article/details/78087146