def main(self):
    # If we're currently shutting down, just die. If not, loop forever,
    # to recover from possible errors caught by the huge try statement inside this loop.
    # Count how often the except for that try was hit recently. This will be reset if
    # there was no exception for at least 5 minutes since the last one.
    tries = 0
    while not self.shutdown:
      try:
        # Record our starting timestamp, in order to back off if we repeatedly die
        starttime = time.time()
        # Exception container: If an exception occurs in the listener thread, the listener thread
        # will store it here and terminate, and the main thread will rethrow it and then restart.
        self.error = None
        self.hasheswithoutshare = 0

        # Initialize megahashes per second to zero, will be measured later.
        self.stats.mhps = 0

        # Job that the device is currently working on, or that is currently being uploaded.
        # This variable is used by BaseWorker to figure out the current work source for statistics.
        self.job = None
        # Job that was previously being procesed. Has been destroyed, but there might be some late nonces.
        self.oldjob = None

        # Open the serial port
        self.handle = serial.Serial(self.port, self.baudrate, serial.EIGHTBITS, serial.PARITY_NONE, serial.STOPBITS_ONE, 1, False, False, 5, False, None)

        # We keep control of the wakeup lock at all times unless we're sleeping
        self.wakeup.acquire()
        # Set validation success flag to false
        self.checksuccess = False
        # Start device response listener thread
        self.listenerthread = Thread(None, self._listener, self.settings.name + "_listener")
        self.listenerthread.daemon = True
        self.listenerthread.start()

        # Send validation job to device
        job = ValidationJob(self.core, unhexlify(b"00000001c3bf95208a646ee98a58cf97c3a0c4b7bf5de4c89ca04495000005200000000024d1fff8d5d73ae11140e4e48032cd88ee01d48c67147f9a09cd41fdec2e25824f5c038d1a0b350c5eb01f04"))
        self._sendjob(job)

        # If an exception occurred in the listener thread, rethrow it
        if self.error != None: raise self.error

        # Wait for the validation job to complete. The wakeup flag will be set by the listener
        # thread when the validation job completes. 60 seconds should be sufficient for devices
        # down to about 2.6MH/s, for slower devices this timeout will need to be increased.
        self.wakeup.wait(60)
        # If an exception occurred in the listener thread, rethrow it
        if self.error != None: raise self.error
        # Honor shutdown flag
        if self.shutdown: break
        # We woke up, but the validation job hasn't succeeded in the mean time.
        # This usually means that the wakeup timeout has expired.
        if not self.checksuccess: raise Exception("Timeout waiting for validation job to finish")
        # self.stats.mhps has now been populated by the listener thread
        self.core.log(self, "Running at %f MH/s\n" % self.stats.mhps, 300, "B")
        # Calculate the time that the device will need to process 2**32 nonces.
        # This is limited at 60 seconds in order to have some regular communication,
        # even with very slow devices (and e.g. detect if the device was unplugged).
        interval = min(60, 2**32 / 1000000. / self.stats.mhps)
        # Add some safety margin and take user's interval setting (if present) into account.
        self.jobinterval = min(self.settings.jobinterval, max(0.5, interval * 0.8 - 1))
        self.core.log(self, "Job interval: %f seconds\n" % self.jobinterval, 400, "B")
        # Tell the MPBM core that our hash rate has changed, so that it can adjust its work buffer.
        self.jobspersecond = 1. / self.jobinterval
        self.core.notify_speed_changed(self)

        # Main loop, continues until something goes wrong or we're shutting down.
        while not self.shutdown:

          # Fetch a job, add 2 seconds safety margin to the requested minimum expiration time.
          # Blocks until one is available. Because of this we need to release the
          # wakeup lock temporarily in order to avoid possible deadlocks.
          self.wakeup.release()
          job = self.core.get_job(self, self.jobinterval + 2)
          self.wakeup.acquire()
          
          # If a new block was found while we were fetching that job, just discard it and get a new one.
          if job.canceled:
            job.destroy()
            continue

          # If an exception occurred in the listener thread, rethrow it
          if self.error != None: raise self.error

          # Upload the job to the device
          self._sendjob(job)
          # If an exception occurred in the listener thread, rethrow it
          if self.error != None: raise self.error
          # If the job was already caught by a long poll while we were uploading it,
          # jump back to the beginning of the main loop in order to immediately fetch new work.
          # Don't check for the canceled flag before the job was accepted by the device,
          # otherwise we might get out of sync.
          if self.job.canceled: continue
          # Wait while the device is processing the job. If nonces are sent by the device, they
          # will be processed by the listener thread. If the job gets canceled, we will be woken up.
          self.wakeup.wait(self.jobinterval)
          # If an exception occurred in the listener thread, rethrow it
          if self.error != None: raise self.error

      # If something went wrong...
      except Exception as e:
        # ...complain about it!
        self.core.log(self, "%s\n" % traceback.format_exc(), 100, "rB")
        # Make sure that the listener thread realizes that something went wrong
        self.error = e
      finally:
        # We're not doing productive work any more, update stats and destroy current job
        self._jobend()
        self.stats.mhps = 0
        # Release the wake lock to allow the listener thread to move. Ignore it if that goes wrong.
        try: self.wakeup.release()
        except: pass
        # Close the serial port handle, otherwise we can't reopen it after restarting.
        # This should hopefully also make reads on that port from the listener thread fail,
        # so that the listener thread will realize that it's supposed to shut down.
        try: self.handle.close()
        except: pass
        # Wait for the listener thread to terminate.
        # If it doens't within 5 seconds, continue anyway. We can't do much about that.
        try: self.listenerthread.join(5)
        except: pass
        # Set MH/s to zero again, the listener thread might have overwritten that.
        self.stats.mhps = 0
        # If we aren't shutting down, figure out if there have been many errors recently,
        # and if yes, wait a bit longer until restarting the worker.
        if not self.shutdown:
          tries += 1
          if time.time() - starttime >= 300: tries = 0
          with self.wakeup:
            if tries > 5: self.wakeup.wait(30)
            else: self.wakeup.wait(1)
  def main(self):
    # If we're currently shutting down, just die. If not, loop forever,
    # to recover from possible errors caught by the huge try statement inside this loop.
    # Count how often the except for that try was hit recently. This will be reset if
    # there was no exception for at least 5 minutes since the last one.
    tries = 0
    while not self.shutdown:
      try:
        # Record our starting timestamp, in order to back off if we repeatedly die
        starttime = time.time()

        # Initialize megahashes per second to zero, will be measured later.
        self.stats.mhps = 0

        # Job that the device is currently working on, or that is currently being uploaded.
        # This variable is used by BaseWorker to figure out the current work source for statistics.
        self.job = None
        # Job that was previously being procesed. Has been destroyed, but there might be some late nonces.
        self.oldjob = None

        # We keep control of the wakeup lock at all times unless we're sleeping
        self.wakeup.acquire()
        # Eat up leftover wakeups
        self.wakeup.wait(0)
        # Honor shutdown flag (in case it was a real wakeup)
        if self.shutdown: break
        # Set validation success flag to false
        self.checksuccess = False
        # Set validation job second iteration flag to false
        self.seconditeration = False
        
        # Initialize hash rate tracking data
        self.lasttime = None
        self.lastnonce = None

        # Initialize malfunction tracking data
        self.recentshares = 0
        self.recentinvalid = 0

        # Configure core clock, if the bitstream supports that
        if self.firmware_rev > 0: self._set_speed(self.parent.settings.initialspeed)
        
        # Clear FPGA's nonce queue
        self.parent.clear_queue(self.fpga)

        # Send validation job to device
        job = ValidationJob(self.core, unhexlify(b"00000001c3bf95208a646ee98a58cf97c3a0c4b7bf5de4c89ca04495000005200000000024d1fff8d5d73ae11140e4e48032cd88ee01d48c67147f9a09cd41fdec2e25824f5c038d1a0b350c5eb01f04"))
        self._sendjob(job)

        # Wait for the validation job to complete. The wakeup flag will be set by the listener
        # thread when the validation job completes. 180 seconds should be sufficient for devices
        # down to about 50MH/s, for slower devices this timeout will need to be increased.
        if self.stats.speed: self.wakeup.wait((2**32 / 1000000. / self.stats.speed) * 1.1)
        else: self.wakeup.wait(180)
        # Honor shutdown flag
        if self.shutdown: break
        # We woke up, but the validation job hasn't succeeded in the mean time.
        # This usually means that the wakeup timeout has expired.
        if not self.checksuccess: raise Exception("Timeout waiting for validation job to finish")
        # self.stats.mhps has now been populated by the listener thread
        self.core.log(self, "Running at %f MH/s\n" % self.stats.mhps, 300, "B")
        self._update_job_interval()

        # Main loop, continues until something goes wrong or we're shutting down.
        while not self.shutdown:

          # Fetch a job, add 2 seconds safety margin to the requested minimum expiration time.
          # Blocks until one is available. Because of this we need to release the
          # wakeup lock temporarily in order to avoid possible deadlocks.
          self.wakeup.release()
          job = self.core.get_job(self, self.jobinterval + 2)
          self.wakeup.acquire()
          
          # If a new block was found while we were fetching that job, just discard it and get a new one.
          if job.canceled:
            job.destroy()
            continue

          # Upload the job to the device
          self._sendjob(job)
          
          # Go through the safety checks and reduce the clock if necessary
          self.safetycheck()
          
          # If the job was already caught by a long poll while we were uploading it,
          # jump back to the beginning of the main loop in order to immediately fetch new work.
          # Don't check for the canceled flag before the job was accepted by the device,
          # otherwise we might get out of sync.
          if self.job.canceled: continue
          # Wait while the device is processing the job. If nonces are sent by the device, they
          # will be processed by the listener thread. If the job gets canceled, we will be woken up.
          self.wakeup.wait(self.jobinterval)

      # If something went wrong...
      except Exception as e:
        # ...complain about it!
        self.core.log(self, "%s\n" % traceback.format_exc(), 100, "rB")
      finally:
        # We're not doing productive work any more, update stats and destroy current job
        self._jobend()
        self.stats.mhps = 0
        try: self.wakeup.release()
        except: pass
        # If we aren't shutting down, figure out if there have been many errors recently,
        # and if yes, restart the parent worker as well.
        if not self.shutdown:
          tries += 1
          if time.time() - starttime >= 300: tries = 0
          with self.wakeup:
            if tries > 5:
              self.parent.async_restart()
              return
            else: self.wakeup.wait(1)
    def main(self):
        # If we're currently shutting down, just die. If not, loop forever,
        # to recover from possible errors caught by the huge try statement inside this loop.
        # Count how often the except for that try was hit recently. This will be reset if
        # there was no exception for at least 5 minutes since the last one.
        tries = 0
        while not self.shutdown:
            try:
                # Record our starting timestamp, in order to back off if we repeatedly die
                starttime = time.time()
                self.error = None

                # Initialize megahashes per second to zero, will be measured later.
                self.stats.mhps = 0

                # Job that the device is currently working on, or that is currently being uploaded.
                # This variable is used by BaseWorker to figure out the current work source for statistics.
                self.job = None
                # Job that was previously being procesed. Has been destroyed, but there might be some late nonces.
                self.oldjob = None

                # We keep control of the wakeup lock at all times unless we're sleeping
                self.wakeup.acquire()
                # Eat up leftover wakeups
                self.wakeup.wait(0)
                # Honor shutdown flag (in case it was a real wakeup)
                if self.shutdown: break
                # Set validation success flag to false
                self.checksuccess = False

                # Initialize hash rate tracking data
                self.lasttime = None
                self.lastnonce = None

                # Initialize malfunction tracking data
                self.recentshares = 0
                self.recentinvalid = 0

                # Configure core clock, if the bitstream supports that
                self._set_speed(self.parent.settings.initialspeed)

                # Send validation job to device
                job = ValidationJob(
                    self.core,
                    unhexlify(
                        b"00000001c3bf95208a646ee98a58cf97c3a0c4b7bf5de4c89ca04495000005200000000024d1fff8d5d73ae11140e4e48032cd88ee01d48c67147f9a09cd41fdec2e25824f5c038d1a0b350c5eb01f04"
                    ))
                self._sendjob(job)

                # Wait for the validation job to complete. The wakeup flag will be
                # set by the listener thread when the validation job completes.
                self.wakeup.wait((100. / self.stats.mhps) + 1)
                # Honor shutdown flag
                if self.shutdown: break
                # We woke up, but the validation job hasn't succeeded in the mean time.
                # This usually means that the wakeup timeout has expired.
                if not self.checksuccess:
                    raise Exception(
                        "Timeout waiting for validation job to finish")

                # Main loop, continues until something goes wrong or we're shutting down.
                while not self.shutdown:

                    # Fetch a job, add 2 seconds safety margin to the requested minimum expiration time.
                    # Blocks until one is available. Because of this we need to release the
                    # wakeup lock temporarily in order to avoid possible deadlocks.
                    self.wakeup.release()
                    job = self.core.get_job(self, self.jobinterval + 2)
                    self.wakeup.acquire()
                    if self.error: raise self.error

                    # If a new block was found while we were fetching that job, just discard it and get a new one.
                    if job.canceled:
                        job.destroy()
                        continue

                    # Upload the job to the device
                    self._sendjob(job)
                    if self.error: raise self.error

                    # Go through the safety checks and reduce the clock if necessary
                    self.safetycheck()

                    # If the job was already caught by a long poll while we were uploading it,
                    # jump back to the beginning of the main loop in order to immediately fetch new work.
                    # Don't check for the canceled flag before the job was accepted by the device,
                    # otherwise we might get out of sync.
                    if self.job.canceled: continue
                    # Wait while the device is processing the job. If nonces are sent by the device, they
                    # will be processed by the listener thread. If the job gets canceled, we will be woken up.
                    self.wakeup.wait(self.jobinterval)
                    if self.error: raise self.error

            # If something went wrong...
            except Exception as e:
                # ...complain about it!
                self.core.log(self, "%s\n" % traceback.format_exc(), 100, "rB")
            finally:
                # We're not doing productive work any more, update stats and destroy current job
                self._jobend()
                self.stats.mhps = 0
                try:
                    self.wakeup.release()
                except:
                    pass
                # If we aren't shutting down, figure out if there have been many errors recently,
                # and if yes, restart the parent worker as well.
                if not self.shutdown:
                    tries += 1
                    if time.time() - starttime >= 300: tries = 0
                    with self.wakeup:
                        if tries > 5:
                            self.parent.async_restart()
                            return
                        else:
                            self.wakeup.wait(1)
    def main(self):
        # If we're currently shutting down, just die. If not, loop forever,
        # to recover from possible errors caught by the huge try statement inside this loop.
        # Count how often the except for that try was hit recently. This will be reset if
        # there was no exception for at least 5 minutes since the last one.
        tries = 0
        while not self.shutdown:
            try:
                # Record our starting timestamp, in order to back off if we repeatedly die
                starttime = time.time()
                # Exception container: If an exception occurs in the listener thread, the listener thread
                # will store it here and terminate, and the main thread will rethrow it and then restart.
                self.error = None

                # Initialize megahashes per second to zero, will be measured later.
                self.stats.mhps = 0

                # Job that the device is currently working on (found nonces are coming from this one).
                # This variable is used by BaseWorker to figure out the current work source for statistics.
                self.job = None
                # Job that is currently being uploaded to the device but not yet being processed.
                self.nextjob = None

                # Open the serial port
                self.handle = serial.Serial(self.port, self.baudrate,
                                            serial.EIGHTBITS,
                                            serial.PARITY_NONE,
                                            serial.STOPBITS_ONE, 1, False,
                                            False, 5, False, None)

                # Send enough zero bytes to make sure that the device is not expecting data any more.
                # Command zero is a ping request, which is answered by a zero byte from the device.
                # This means that superfluous zero bytes (but at least one) will just bounce back to us.
                self.handle.write(struct.pack("45B", *([0] * 45)))
                # Read the device's response.
                # There should be at least one byte, and the last byte must be zero.
                # If not, something is wrong with the device or communication channel.
                data = self.handle.read(100)
                if len(data) == 0:
                    raise Exception(
                        "Failed to sync with mining device: Device does not respond"
                    )
                if data[-1:] != b"\0":
                    raise Exception(
                        "Failed to sync with mining device: Device sends garbage"
                    )

                # We keep control of the wakeup lock at all times unless we're sleeping
                self.wakeup.acquire()
                # Set validation success flag to false
                self.checksuccess = False
                # Start device response listener thread
                self.listenerthread = Thread(None, self._listener,
                                             self.settings.name + "_listener")
                self.listenerthread.daemon = True
                self.listenerthread.start()

                # Send validation job to device
                job = ValidationJob(
                    self.core,
                    unhexlify(
                        b"00000001c3bf95208a646ee98a58cf97c3a0c4b7bf5de4c89ca04495000005200000000024d1fff8d5d73ae11140e4e48032cd88ee01d48c67147f9a09cd41fdec2e25824f5c038d1a0b350c5eb01f04"
                    ))
                self._sendjob(job)

                # Wait for validation job to be accepted by the device
                self.wakeup.wait(1)
                # If an exception occurred in the listener thread, rethrow it
                if self.error != None: raise self.error
                # Honor shutdown flag
                if self.shutdown: break
                # If the job that was enqueued above has not been moved from nextjob to job by the
                # listener thread yet, something went wrong. Throw an exception to make everything restart.
                if self.nextjob != None:
                    raise Exception("Timeout waiting for job ACK")

                # Wait for the validation job to complete. The wakeup flag will be set by the listener
                # thread when the validation job completes. 60 seconds should be sufficient for devices
                # down to about 1.3MH/s, for slower devices this timeout will need to be increased.
                self.wakeup.wait(60)
                # If an exception occurred in the listener thread, rethrow it
                if self.error != None: raise self.error
                # Honor shutdown flag
                if self.shutdown: break
                # We woke up, but the validation job hasn't succeeded in the mean time.
                # This usually means that the wakeup timeout has expired.
                if not self.checksuccess:
                    raise Exception(
                        "Timeout waiting for validation job to finish")
                # self.stats.mhps has now been populated by the listener thread
                self.core.log(
                    self.settings.name +
                    ": Running at %f MH/s\n" % self.stats.mhps, 300, "B")
                # Calculate the time that the device will need to process 2**32 nonces.
                # This is limited at 60 seconds in order to have some regular communication,
                # even with very slow devices (and e.g. detect if the device was unplugged).
                interval = min(60, 2**32 / 1000000. / self.stats.mhps)
                # Add some safety margin and take user's interval setting (if present) into account.
                self.jobinterval = min(self.settings.jobinterval,
                                       max(0.5, interval * 0.8 - 1))
                self.core.log(
                    self.settings.name +
                    ": Job interval: %f seconds\n" % self.jobinterval, 400,
                    "B")
                # Tell the MPBM core that our hash rate has changed, so that it can adjust its work buffer.
                self.jobspersecond = 1. / self.jobinterval
                self.core.notify_speed_changed(self)

                # Main loop, continues until something goes wrong or we're shutting down.
                while not self.shutdown:

                    # Fetch a job, add 2 seconds safety margin to the requested minimum expiration time.
                    # Blocks until one is available. Because of this we need to release the
                    # wakeup lock temporarily in order to avoid possible deadlocks.
                    self.wakeup.release()
                    job = self.core.get_job(self, self.jobinterval + 2)
                    self.wakeup.acquire()

                    # If a new block was found while we were fetching that job, just discard it and get a new one.
                    if job.canceled:
                        job.destroy()
                        continue

                    # If an exception occurred in the listener thread, rethrow it
                    if self.error != None: raise self.error

                    # Upload the job to the device
                    self._sendjob(job)
                    # Wait for up to one second for the device to accept it
                    self.wakeup.wait(1)
                    # Honor shutdown flag
                    if self.shutdown: break
                    # If an exception occurred in the listener thread, rethrow it
                    if self.error != None: raise self.error
                    # If the job that was send above has not been moved from nextjob to job by the listener
                    # thread yet, something went wrong. Throw an exception to make everything restart.
                    if self.nextjob != None:
                        raise Exception("Timeout waiting for job ACK")
                    # If the job was already caught by a long poll while we were uploading it,
                    # jump back to the beginning of the main loop in order to immediately fetch new work.
                    # Don't check for the canceled flag before the job was accepted by the device,
                    # otherwise we might get out of sync.
                    if self.job.canceled: continue
                    # Wait while the device is processing the job. If nonces are sent by the device, they
                    # will be processed by the listener thread. If the job gets canceled, we will be woken up.
                    self.wakeup.wait(self.jobinterval)
                    # If an exception occurred in the listener thread, rethrow it
                    if self.error != None: raise self.error

            # If something went wrong...
            except Exception as e:
                # ...complain about it!
                self.core.log(
                    self.settings.name + ": %s\n" % traceback.format_exc(),
                    100, "rB")
                # Make sure that the listener thread realizes that something went wrong
                self.error = e
            finally:
                # We're not doing productive work any more, update stats and destroy current job
                self._jobend()
                self.stats.mhps = 0
                # Release the wake lock to allow the listener thread to move. Ignore it if that goes wrong.
                try:
                    self.wakeup.release()
                except:
                    pass
                # Close the serial port handle, otherwise we can't reopen it after restarting.
                # This should hopefully also make reads on that port from the listener thread fail,
                # so that the listener thread will realize that it's supposed to shut down.
                try:
                    self.handle.close()
                except:
                    pass
                # Wait for the listener thread to terminate.
                # If it doens't within 5 seconds, continue anyway. We can't do much about that.
                try:
                    self.listenerthread.join(5)
                except:
                    pass
                # Set MH/s to zero again, the listener thread might have overwritten that.
                self.stats.mhps = 0
                # If we aren't shutting down, figure out if there have been many errors recently,
                # and if yes, wait a bit longer until restarting the worker.
                if not self.shutdown:
                    tries += 1
                    if time.time() - starttime >= 300: tries = 0
                    with self.wakeup:
                        if tries > 5: self.wakeup.wait(30)
                        else: self.wakeup.wait(1)
    def main(self):
        # If we're currently shutting down, just die. If not, loop forever,
        # to recover from possible errors caught by the huge try statement inside this loop.
        # Count how often the except for that try was hit recently. This will be reset if
        # there was no exception for at least 5 minutes since the last one.
        tries = 0
        while not self.shutdown:
            try:
                # Record our starting timestamp, in order to back off if we repeatedly die
                starttime = time.time()
                # Exception container: If an exception occurs in the listener thread, the listener thread
                # will store it here and terminate, and the main thread will rethrow it and then restart.
                self.error = None
                self.hasheswithoutshare = 0

                # Initialize megahashes per second to zero, will be measured later.
                self.stats.mhps = 0
                self.offset = 0

                # Initialize clocking tracking data
                self.speed = 0
                self.recentshares = 0
                self.recentinvalid = 0

                # Job that the device is currently working on, or that is currently being uploaded.
                # This variable is used by BaseWorker to figure out the current work source for statistics.
                self.job = None
                # Job that was previously being procesed. Has been destroyed, but there might be some late nonces.
                self.oldjob = None

                # Open the serial port
                import serial
                self.handle = serial.Serial(self.port, self.baudrate,
                                            serial.EIGHTBITS,
                                            serial.PARITY_NONE,
                                            serial.STOPBITS_ONE, 1, False,
                                            False, 5, False, None)

                # We keep control of the wakeup lock at all times unless we're sleeping
                self.wakeup.acquire()
                # Set validation success flag to false
                self.checksuccess = False
                # Start device response listener thread
                self.listenerthread = Thread(None, self._listener,
                                             self.settings.name + "_listener")
                self.listenerthread.daemon = True
                self.listenerthread.start()

                # Configure core clock
                self.initialramp = True
                self._set_speed(self.settings.initialspeed // 2.5)

                # Send validation job to device
                job = ValidationJob(
                    self.core,
                    unhexlify(
                        b"00000001c3bf95208a646ee98a58cf97c3a0c4b7bf5de4c89ca04495000005200000000024d1fff8d5d73ae11140e4e48032cd88ee01d48c67147f9a09cd41fdec2e25824f5c038d1a0b350c5eb01f04"
                    ))
                self._sendjob(job)

                # If an exception occurred in the listener thread, rethrow it
                if self.error != None: raise self.error

                # Wait for the validation job to complete. The wakeup flag will be set by the listener
                # thread when the validation job completes. 60 seconds should be sufficient for devices
                # down to about 2.6MH/s, for slower devices this timeout will need to be increased.
                self.wakeup.wait(60)
                # If an exception occurred in the listener thread, rethrow it
                if self.error != None: raise self.error
                # Honor shutdown flag
                if self.shutdown: break
                # We woke up, but the validation job hasn't succeeded in the mean time.
                # This usually means that the wakeup timeout has expired.
                if not self.checksuccess:
                    raise Exception(
                        "Timeout waiting for validation job to finish")

                # Main loop, continues until something goes wrong or we're shutting down.
                while not self.shutdown:

                    # Fetch a job, add 2 seconds safety margin to the requested minimum expiration time.
                    # Blocks until one is available. Because of this we need to release the
                    # wakeup lock temporarily in order to avoid possible deadlocks.
                    self.wakeup.release()
                    job = self.core.get_job(self, self.jobinterval + 2)
                    self.wakeup.acquire()

                    # If the job could be interpreted as a command, ignore it.
                    if job.data[68:72] == unhexlify(b"ffffffff"):
                        job.destroy()
                        continue

                    # Go through the safety checks and adjust the clock if necessary
                    self.safetycheck()

                    # If a new block was found while we were fetching this job, just discard it and get a new one.
                    if job.canceled:
                        job.destroy()
                        continue

                    # If an exception occurred in the listener thread, rethrow it
                    if self.error != None: raise self.error

                    # Upload the job to the device
                    self._sendjob(job)
                    # If an exception occurred in the listener thread, rethrow it
                    if self.error != None: raise self.error
                    # If the job was already caught by a long poll while we were uploading it,
                    # jump back to the beginning of the main loop in order to immediately fetch new work.
                    # Don't check for the canceled flag before the job was accepted by the device,
                    # otherwise we might get out of sync.
                    if self.job.canceled: continue
                    # Wait while the device is processing the job. If nonces are sent by the device, they
                    # will be processed by the listener thread. If the job gets canceled, we will be woken up.
                    self.wakeup.wait(self.jobinterval)
                    # If an exception occurred in the listener thread, rethrow it
                    if self.error != None: raise self.error

            # If something went wrong...
            except Exception as e:
                # ...complain about it!
                self.core.log(self, "%s\n" % traceback.format_exc(), 100, "rB")
                # Make sure that the listener thread realizes that something went wrong
                self.error = e
            finally:
                # We're not doing productive work any more, update stats and destroy current job
                self._jobend()
                self.stats.mhps = 0
                # Release the wake lock to allow the listener thread to move. Ignore it if that goes wrong.
                try:
                    self.wakeup.release()
                except:
                    pass
                # Close the serial port handle, otherwise we can't reopen it after restarting.
                # This should hopefully also make reads on that port from the listener thread fail,
                # so that the listener thread will realize that it's supposed to shut down.
                try:
                    self.handle.close()
                except:
                    pass
                # Wait for the listener thread to terminate.
                # If it doens't within 5 seconds, continue anyway. We can't do much about that.
                try:
                    self.listenerthread.join(5)
                except:
                    pass
                # Set MH/s to zero again, the listener thread might have overwritten that.
                self.stats.mhps = 0
                # If we aren't shutting down, figure out if there have been many errors recently,
                # and if yes, wait a bit longer until restarting the worker.
                if not self.shutdown:
                    tries += 1
                    if time.time() - starttime >= 300: tries = 0
                    with self.wakeup:
                        if tries > 5: self.wakeup.wait(30)
                        else: self.wakeup.wait(1)