class NsServerNumConcurrentRequests(UserResourceTask):
    def __init__(self, user, node, streaminguri):
        super(NsServerNumConcurrentRequests, self).__init__(user, node)
        self.nconns = []  # Belongs exclusively to poll method
        self.thread = None
        self.httprq = get_http_request(self.node.ip, streaminguri,
                                       self.node.rest_username,
                                       self.node.rest_password)
        self.no_of_connections = AtomicInteger(0)
        self.no_of_open_connections = AtomicInteger(0)
        self.no_of_throughput_updates = AtomicInteger(0)
        self.rest = RestConnection(self.node)

    def on_throughput_increase(self, throughput):
        log.debug("Increasing throughput by {}".format(throughput -
                                                       self.throughput))

        # Record the last throughput update
        last_throughput_update = self.no_of_throughput_updates.get()

        # Update the throughput
        self.no_of_connections.set(throughput)

        # Launch the thread
        if self.thread is None:
            self.thread = Thread(target=self.poll)
            self.thread.start()

        # Block until the update has gone (TODO add a timeout)
        while self.no_of_throughput_updates.get() <= last_throughput_update:
            continue

    def on_throughput_decrease(self, throughput):
        log.debug("Decreasing throughput by {}".format(self.throughput -
                                                       throughput))

        # Record the last throughput update
        last_throughput_update = self.no_of_throughput_updates.get()

        # Update the throughput
        self.no_of_connections.set(throughput)

        if self.thread and throughput == 0:
            self.thread.join()
            self.thread = None

        # Block until the update has gone (TODO add a timeout)
        while self.no_of_throughput_updates.get() <= last_throughput_update:
            continue

    def get_throughput_success(self):
        return self.no_of_open_connections.get()

    def poll(self):
        """ Repeatedly poll each connection and attempt to keep them alive """
        no_of_conns = self.no_of_connections.get()

        while no_of_conns > 0 or len(self.nconns) > 0:
            update_nconns = no_of_conns != len(self.nconns)

            if update_nconns:
                # Add any new connections
                for i in range(no_of_conns - len(self.nconns)):
                    self.nconns.append(
                        NonBlockingConnection(self.node.ip, 8091, self.httprq))
                # Disconnect the connections that need to be closed
                for conn in self.nconns[no_of_conns:]:
                    conn.disconnect()
                # Delete the disconnected connections
                del self.nconns[no_of_conns:]

            # Poll and count open connections
            open_count = 0
            for conn in self.nconns:
                if conn.poll():
                    open_count += 1

            # Update the number of open connections
            self.no_of_open_connections.set(open_count)

            # Notify the main thread that the connections have been updated
            if update_nconns:
                self.no_of_throughput_updates.incrementAndGet()

            no_of_conns = self.no_of_connections.get()

    def error(self):
        return self.rest._http_request(self.rest.baseUrl + "/pools/default")[1]

    def expected_error(self):
        return 'Limit(s) exceeded [num_concurrent_requests]'
class AbstractTimedThroughputWorker(object):
    """ Send throughput across a time period. """
    def __init__(self, period=60, chunks=100, throughput=0):
        self.period = period  # The time period over which to produce throughput
        self.chunks = chunks
        self.throughput = AtomicInteger(throughput)
        self.throughput_success = AtomicInteger(0)
        self.resume = AtomicBoolean(True)
        self.curr_tick = AtomicInteger(0)

    def stop(self):
        self.resume.set(False)
        self.thread.join()

    def start(self):
        self.thread = Thread(target=self.loop)
        self.thread.start()

    def loop(self):
        while self.resume.get():
            self.throughput_success.set(self.tick(self.throughput.get()))

    def action(self, throughput):
        """ This method fires

        Args:
            throughput (int): The throughput in bytes

        Returns (bool): Indicating success.
        """
        raise NotImplementedError("Please implement this method")

    def next_tick(self, period):
        """ Returns the next multiple of a time period """
        curr_time = time.time()
        return curr_time + (period - curr_time % period)

    def tick(self, throughput):
        """ Fires throughput over this time period """
        # The next 60 second time period
        next_tick = self.next_tick(self.period)

        # Every mini_tick, we will fire a thoughput of this size
        throughput_per_chunk = throughput / self.chunks

        #  The size of a mini_tick (e.g. 0.6 seconds)
        mini_tick_period = self.period / float(self.chunks)

        chunks_sent, successes = 0, 0
        while time.time() < next_tick and chunks_sent < self.chunks:
            if not self.resume.get():
                break
            # Fire action and record time taken
            # time_taken, success = time_it(self.action, throughput_per_chunk)
            success = self.action(throughput_per_chunk)

            # Count successes
            if success:
                successes += 1

            chunks_sent += 1

            # The time remaining to reach the next mini tick
            time_till_next_mini_tick = max(
                0,
                self.next_tick(mini_tick_period) - time.time())

            # sleep to next mini tick to ensure actions happen evenly
            time.sleep(time_till_next_mini_tick)

        return successes * throughput_per_chunk