2727#include < fc/io/fstream.hpp>
2828#include < fc/io/json.hpp>
2929
30+ #include < appbase/application.hpp>
31+ #include < csignal>
32+ #include < cerrno>
33+ #include < cstring>
34+
3035#define VIRTUAL_SCHEDULE_LAP_LENGTH ( fc::uint128_t (uint64_t (-1 )) )
3136#define VIRTUAL_SCHEDULE_LAP_LENGTH2 ( fc::uint128_t ::max_value() )
3237
@@ -58,6 +63,7 @@ FC_REFLECT((golos::chain::db_schema), (types)(object_types)(operation_type)(cust
5863
5964namespace golos { namespace chain {
6065
66+ using std::sig_atomic_t ;
6167 using boost::container::flat_set;
6268
6369 inline u256 to256 (const fc::uint128_t &t) {
@@ -67,6 +73,98 @@ namespace golos { namespace chain {
6773 return v;
6874 }
6975
76+ class signal_guard {
77+ struct sigaction old_hup_action, old_int_action, old_term_action;
78+
79+ static volatile sig_atomic_t is_interrupted;
80+
81+ bool is_restored = true ;
82+
83+ static inline void throw_exception ();
84+
85+ public:
86+ inline signal_guard ();
87+
88+ inline ~signal_guard ();
89+
90+ void setup ();
91+
92+ void restore ();
93+
94+ static inline sig_atomic_t get_is_interrupted () noexcept ;
95+ };
96+
97+ volatile sig_atomic_t signal_guard::is_interrupted = false ;
98+
99+ inline void signal_guard::throw_exception () {
100+ FC_THROW_EXCEPTION (database_signal_exception,
101+ " Error setup signal handler: ${e}." ,
102+ (" e" , strerror (errno)));
103+ }
104+
105+ inline signal_guard::signal_guard () {
106+ setup ();
107+ }
108+
109+ inline signal_guard::~signal_guard () {
110+ try {
111+ restore ();
112+ }
113+ FC_CAPTURE_AND_LOG (())
114+ }
115+
116+ void signal_guard::setup () {
117+ if (is_restored) {
118+ struct sigaction new_action;
119+
120+ new_action.sa_handler = [](int signum) {
121+ is_interrupted = true ;
122+ };
123+ new_action.sa_flags = SA_RESTART ;
124+
125+ if (sigemptyset (&new_action.sa_mask ) == -1 ) {
126+ throw_exception ();
127+ }
128+
129+ if (sigaction (SIGHUP , &new_action, &old_hup_action) == -1 ) {
130+ throw_exception ();
131+ }
132+
133+ if (sigaction (SIGINT , &new_action, &old_int_action) == -1 ) {
134+ throw_exception ();
135+ }
136+
137+ if (sigaction (SIGTERM , &new_action, &old_term_action) == -1 ) {
138+ throw_exception ();
139+ }
140+
141+ is_restored = false ;
142+ }
143+ }
144+
145+ void signal_guard::restore () {
146+ if (!is_restored) {
147+ if (sigaction (SIGHUP , &old_hup_action, nullptr ) == -1 ) {
148+ throw_exception ();
149+ }
150+
151+ if (sigaction (SIGINT , &old_int_action, nullptr ) == -1 ) {
152+ throw_exception ();
153+ }
154+
155+ if (sigaction (SIGTERM , &old_term_action, nullptr ) == -1 ) {
156+ throw_exception ();
157+ }
158+
159+ is_interrupted = false ;
160+ is_restored = true ;
161+ }
162+ }
163+
164+ inline sig_atomic_t signal_guard::get_is_interrupted () noexcept {
165+ return is_interrupted;
166+ }
167+
70168 class database_impl {
71169 public:
72170 database_impl (database &self);
@@ -113,16 +211,24 @@ namespace golos { namespace chain {
113211
114212 _block_log.open (data_dir / " block_log" );
115213
116- auto log_head = _block_log.head ();
117-
118214 // Rewind all undo state. This should return us to the state at the last irreversible block.
119215 with_strong_write_lock ([&]() {
120216 undo_all ();
121- FC_ASSERT (revision () ==
122- head_block_num (), " Chainbase revision does not match head block num" ,
123- (" rev" , revision ())(" head_block" , head_block_num ()));
124217 });
125218
219+ if (revision () != head_block_num ()) {
220+ with_strong_read_lock ([&]() {
221+ init_hardforks (); // Writes to local state, but reads from db
222+ });
223+
224+ FC_THROW_EXCEPTION (database_revision_exception,
225+ " Chainbase revision does not match head block num, "
226+ " current revision is ${rev}, "
227+ " current head block num is ${head}" ,
228+ (" rev" , revision ())
229+ (" head" , head_block_num ()));
230+ }
231+
126232 if (head_block_num ()) {
127233 auto head_block = _block_log.read_block_by_num (head_block_num ());
128234 // This assertion should be caught and a reindex should occur
@@ -143,19 +249,16 @@ namespace golos { namespace chain {
143249 FC_CAPTURE_LOG_AND_RETHROW ((data_dir)(shared_mem_dir)(shared_file_size))
144250 }
145251
146- void database::reindex (const fc::path &data_dir, const fc::path &shared_mem_dir, uint64_t shared_file_size) {
252+ void database::reindex (const fc::path &data_dir, const fc::path &shared_mem_dir, uint32_t from_block_num, uint64_t shared_file_size) {
147253 try {
148- ilog (" Reindexing Blockchain" );
149- wipe (data_dir, shared_mem_dir, false );
150- open (data_dir, shared_mem_dir, STEEMIT_INIT_SUPPLY , shared_file_size, chainbase::database::read_write);
254+ signal_guard sg;
151255 _fork_db.reset (); // override effect of _fork_db.start_block() call in open()
152256
153257 auto start = fc::time_point::now ();
154258 GOLOS_ASSERT (_block_log.head (), block_log_exception, " No blocks in block log. Cannot reindex an empty chain." );
155259
156260 ilog (" Replaying blocks..." );
157261
158-
159262 uint64_t skip_flags =
160263 skip_block_size_check |
161264 skip_witness_signature |
@@ -170,34 +273,57 @@ namespace golos { namespace chain {
170273 skip_block_log;
171274
172275 with_strong_write_lock ([&]() {
173- auto itr = _block_log. read_block ( 0 ) ;
276+ auto cur_block_num = from_block_num ;
174277 auto last_block_num = _block_log.head ()->block_num ();
278+ auto last_block_pos = _block_log.get_block_pos (last_block_num);
279+ int last_reindex_percent = 0 ;
175280
176281 set_reserved_memory (1024 *1024 *1024 ); // protect from memory fragmentations ...
177- while (itr.first .block_num () != last_block_num) {
282+ while (cur_block_num < last_block_num) {
283+ if (signal_guard::get_is_interrupted ()) {
284+ return ;
285+ }
286+
178287 auto end = fc::time_point::now ();
179- auto cur_block_num = itr.first .block_num ();
180- if (cur_block_num % 100000 == 0 ) {
288+ auto cur_block_pos = _block_log.get_block_pos (cur_block_num);
289+ auto cur_block = *_block_log.read_block_by_num (cur_block_num);
290+
291+ auto reindex_percent = cur_block_pos * 100 / last_block_pos;
292+ if (reindex_percent - last_reindex_percent >= 1 ) {
181293 std::cerr
182- << " " << double (cur_block_num * 100 ) / last_block_num << " % "
294+ << " " << reindex_percent << " % "
183295 << cur_block_num << " of " << last_block_num
184296 << " (" << (free_memory () / (1024 * 1024 )) << " M free"
185297 << " , elapsed " << double ((end - start).count ()) / 1000000.0 << " sec)\n " ;
298+
299+ last_reindex_percent = reindex_percent;
300+ }
301+
302+ apply_block (cur_block, skip_flags);
303+
304+ if (cur_block_num % 1000 == 0 ) {
305+ set_revision (head_block_num ());
186306 }
187- apply_block (itr. first , skip_flags);
188- check_free_memory (true , itr. first . block_num () );
189- itr = _block_log. read_block (itr. second ) ;
307+
308+ check_free_memory (true , cur_block_num );
309+ cur_block_num++ ;
190310 }
191311
192- apply_block (itr.first , skip_flags);
312+ auto cur_block = *_block_log.read_block_by_num (cur_block_num);
313+ apply_block (cur_block, skip_flags);
193314 set_reserved_memory (0 );
194315 set_revision (head_block_num ());
195316 });
196317
318+ if (signal_guard::get_is_interrupted ()) {
319+ sg.restore ();
320+
321+ appbase::app ().quit ();
322+ }
323+
197324 if (_block_log.head ()->block_num ()) {
198325 _fork_db.start_block (*_block_log.head ());
199326 }
200-
201327 auto end = fc::time_point::now ();
202328 ilog (" Done reindexing, elapsed time: ${t} sec" , (" t" ,
203329 double ((end - start).count ()) / 1000000.0 ));
@@ -3166,6 +3292,10 @@ namespace golos { namespace chain {
31663292 _next_flush_block = 0 ;
31673293 }
31683294
3295+ const block_log &database::get_block_log () const {
3296+ return _block_log;
3297+ }
3298+
31693299// ////////////////// private methods ////////////////////
31703300
31713301 void database::apply_block (const signed_block &next_block, uint32_t skip) {
0 commit comments