比特币源码解析(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.
将在后面章节详细介绍它们各自的具体实现方式。
- 比特币源码解析(17)
- 比特币源码解析(1)
- 比特币源码解析(2)
- 比特币源码解析(3)
- 比特币源码解析(4)
- 比特币源码解析(5)
- 比特币源码解析(6)
- 比特币源码解析(7)
- 比特币源码解析(8)
- 比特币源码解析(9)
- 比特币源码解析(11)
- 比特币源码解析(12)
- 比特币源码解析(13)
- 比特币源码解析(14)
- 比特币源码解析(15)
- 比特币源码解析(16)
- 比特币源码解析(18)
- 比特币源码解析(19)
- Java ArrayList 源码分析与提高性能替代方案
- 欢迎使用CSDN-markdown编辑器
- python+pycharm+tensorflow 环境出现模块缺失解决
- IntelliJ Idea 常用快捷键 列表(实战终极总结!!!!)
- SELU激活函数,scaled exponential linear units
- 比特币源码解析(17)
- OpenGL从1.0开始--输出图元
- GKMinmaxStrategist
- 单片机通过如何发送字符串?
- 关于单例模式
- Windows下搭建Wordpress博客网站
- [RK3288][Android6.0] 调试笔记 --- Camera动态热插拔支持
- log4j.properties配置详解与实例
- Leetcode 221 & 85