瀏覽代碼

properly stop miner in stop_mining(), use condition variable instead of busy waiting

Malte Kraus 8 年之前
父節點
當前提交
5c4afaa0ea
共有 1 個文件被更改,包括 36 次插入15 次删除
  1. 36 15
      src/mining.py

+ 36 - 15
src/mining.py

@@ -1,6 +1,6 @@
 """ Functionality for mining new blocks. """
 
-from threading import Thread
+from threading import Thread, Condition
 from time import sleep
 from typing import Optional
 
@@ -30,37 +30,58 @@ class Miner:
     def __init__(self, proto, reward_pubkey):
         self.proto = proto
         self.chainbuilder = ChainBuilder(proto)
-        self.chainbuilder.chain_change_handlers.append(self.start_mining)
+        self.chainbuilder.chain_change_handlers.append(self._chain_changed)
         self._cur_miner = None
         self.reward_pubkey = reward_pubkey
+        self._stopped = False
+        self._miner_cond = Condition()
         Thread(target=self._miner_thread, daemon=True).start()
 
     def _miner_thread(self):
+        def wait_for_miner():
+            with self._miner_cond:
+                while self._cur_miner is None:
+                    if self._stopped:
+                        return None
+                    self._miner_cond.wait()
+                return self._cur_miner
+
         while True:
-            miner = self._cur_miner
+            miner = wait_for_miner()
             if miner is None:
-                # TODO: condition variable
-                _yield()
-            else:
-                block = miner.run()
-                self._cur_miner = None
-                if block is not None:
-                    self.proto.broadcast_primary_block(block)
+                return
+            block = miner.run()
+            with self._miner_cond:
+                if self._cur_miner == miner:
+                    self._cur_miner = None
+            if block is not None:
+                self.proto.broadcast_primary_block(block)
 
     def start_mining(self):
         """ Start mining on a new block. """
-        self.stop_mining()
-
         chain = self.chainbuilder.primary_block_chain
         transactions = self.chainbuilder.unconfirmed_transactions.values()
         block = mining_strategy.create_block(chain, transactions, self.reward_pubkey)
-        self._cur_miner = ProofOfWork(block)
+        with self._miner_cond:
+            self._stop_mining_for_now()
+            self._cur_miner = ProofOfWork(block)
+            self._miner_cond.notify()
 
-    def stop_mining(self):
-        """ Stop all mining. """
+    def _chain_changed(self):
+        if not self._stopped:
+            self.start_mining()
+
+    def _stop_mining_for_now(self):
         if self._cur_miner:
             self._cur_miner.abort()
+
+    def stop_mining(self):
+        """ Stop all mining. """
+        self._stopped = True
+        with self._miner_cond:
+            self._stop_mining_for_now()
             self._cur_miner = None
+            self._miner_cond.notify()
 
 from .protocol import Protocol
 from .chainbuilder import ChainBuilder