Module utils.buffer
Expand source code
#!/usr/bin/env python3
import threading
import time
class Buffer:
"""Thread safe ring - buffer of commands"""
def __init__(self, max_capacity=100):
self.max_capacity = max_capacity
self.items = [None] * self.max_capacity # List with reserved memory to eliminate problems with memory resizing
self.front_index = 0 # Read position for popFront
self.back_index = 0 # Write position for pushBack
self.length = 0 # Length of buffer in elements
self.lock = threading.Lock() # Be default lock is not acquired
self.item_is_ready = threading.Semaphore(value=0) # Semaphore to signal when there are items in the buffer
def __len__(self):
"""Total number of items in the thread-safe container."""
self.lock.acquire()
L = self.length
self.lock.release()
return L
def isFull(self):
"""Check that buffer is full"""
return len(self) == self.max_capacity
def isEmpty(self):
"""Predict which allow to check is container empty."""
return len(self) == 0
def pushBack(self, item):
"""
Push item into the back of a container, with blocking.
Args:
item (object): item which is inserted into container
"""
while True:
self.lock.acquire()
if self.length == self.max_capacity:
self.lock.release()
time.sleep(0.0001)
continue
else:
self.items[self.back_index] = item
self.back_index = (self.back_index + 1) % self.max_capacity
self.length += 1
self.lock.release()
self.item_is_ready.release()
break
return self
def waitForItem(self):
"""Wait for item available for popping from container via popFront(), blocking."""
self.item_is_ready.acquire()
return self
def popFront(self):
"""
Get item from the front of a container, no blocking.
Method does not perform checking is any element in the container is available.
Please use waitForItem() or len() if you're not sure.
Returns:
object: item from a container
"""
self.lock.acquire()
return_item = self.items[self.front_index]
self.front_index = (self.front_index + 1) % self.max_capacity
self.length -= 1
self.lock.release()
return return_item
def front(self):
"""Get item from the front of a container, not blocking."""
self.lock.acquire()
return_item = self.items[self.front_index]
self.lock.release()
return return_item
def get(self, index):
"""
Get item from the front of a container, not blocking
Method does not perform checking is index within need range accessible range.
Returns:
object: item from a container
"""
self.lock.acquire()
index = index + self.front_index
index = index % self.max_capacity
return_item = self.items[index]
self.lock.release()
return return_item
def __getitem__(self, index):
return self.get(index)
# ======================================================================================================================
# Unittests for launch please use: "pytest -v buffer.py"
# https://docs.pytest.org/en/stable/getting-started.html
def test_cmd_buffer_push_pop():
b = Buffer()
b.pushBack(10).pushBack(20)
assert len(b) == 2
assert b.popFront() == 10
assert b.popFront() == 20
assert len(b) == 0
assert b.isEmpty()
def test_cmd_buffer_waiting():
b = Buffer()
assert b.isEmpty()
b.pushBack(10)
b.pushBack(20)
b.pushBack(30)
assert not b.isEmpty()
b.waitForItem()
assert b.front() == 10
assert b.front() == 10
assert b.popFront() == 10
assert b.front() == 20
assert b.popFront() == 20
def test_cmd_buffer_indexing():
b = Buffer()
b.pushBack(10).pushBack(20).pushBack(30)
assert b[0] == 10
assert b[1] == 20
assert b[2] == 30
b = Buffer()
b.pushBack(50).pushBack(10).pushBack(20).pushBack(30)
assert len(b) == 4
assert 50 == b.popFront()
assert b[0] == 10
assert b[1] == 20
assert b[2] == 30
c = Buffer(max_capacity=3)
c.pushBack(50).pushBack(10).pushBack(20)
c.popFront()
c.popFront()
c.pushBack(21).pushBack(22)
assert c[0] == 20
assert c[1] == 21
assert c[2] == 22
assert len(c) == 3
def test_buffer_waiting():
class TestThread(threading.Thread):
def __init__(self, buffer):
threading.Thread.__init__(self)
self.buffer = buffer
def run(self):
time.sleep(1.0)
out.pushBack("Action-2")
self.buffer.popFront()
b = Buffer(3)
out = Buffer(3)
b.pushBack(1)
b.pushBack(2)
assert not b.isFull()
b.pushBack(3)
assert b.isFull()
th = TestThread(b)
th.start()
out.pushBack("Action-1")
b.pushBack(4)
out.pushBack("Action-3")
assert out[0] == "Action-1"
assert out[1] == "Action-2"
assert out[2] == "Action-3"
# ======================================================================================================================
Functions
def test_buffer_waiting()
-
Expand source code
def test_buffer_waiting(): class TestThread(threading.Thread): def __init__(self, buffer): threading.Thread.__init__(self) self.buffer = buffer def run(self): time.sleep(1.0) out.pushBack("Action-2") self.buffer.popFront() b = Buffer(3) out = Buffer(3) b.pushBack(1) b.pushBack(2) assert not b.isFull() b.pushBack(3) assert b.isFull() th = TestThread(b) th.start() out.pushBack("Action-1") b.pushBack(4) out.pushBack("Action-3") assert out[0] == "Action-1" assert out[1] == "Action-2" assert out[2] == "Action-3"
def test_cmd_buffer_indexing()
-
Expand source code
def test_cmd_buffer_indexing(): b = Buffer() b.pushBack(10).pushBack(20).pushBack(30) assert b[0] == 10 assert b[1] == 20 assert b[2] == 30 b = Buffer() b.pushBack(50).pushBack(10).pushBack(20).pushBack(30) assert len(b) == 4 assert 50 == b.popFront() assert b[0] == 10 assert b[1] == 20 assert b[2] == 30 c = Buffer(max_capacity=3) c.pushBack(50).pushBack(10).pushBack(20) c.popFront() c.popFront() c.pushBack(21).pushBack(22) assert c[0] == 20 assert c[1] == 21 assert c[2] == 22 assert len(c) == 3
def test_cmd_buffer_push_pop()
-
Expand source code
def test_cmd_buffer_push_pop(): b = Buffer() b.pushBack(10).pushBack(20) assert len(b) == 2 assert b.popFront() == 10 assert b.popFront() == 20 assert len(b) == 0 assert b.isEmpty()
def test_cmd_buffer_waiting()
-
Expand source code
def test_cmd_buffer_waiting(): b = Buffer() assert b.isEmpty() b.pushBack(10) b.pushBack(20) b.pushBack(30) assert not b.isEmpty() b.waitForItem() assert b.front() == 10 assert b.front() == 10 assert b.popFront() == 10 assert b.front() == 20 assert b.popFront() == 20
Classes
class Buffer (max_capacity=100)
-
Thread safe ring - buffer of commands
Expand source code
class Buffer: """Thread safe ring - buffer of commands""" def __init__(self, max_capacity=100): self.max_capacity = max_capacity self.items = [None] * self.max_capacity # List with reserved memory to eliminate problems with memory resizing self.front_index = 0 # Read position for popFront self.back_index = 0 # Write position for pushBack self.length = 0 # Length of buffer in elements self.lock = threading.Lock() # Be default lock is not acquired self.item_is_ready = threading.Semaphore(value=0) # Semaphore to signal when there are items in the buffer def __len__(self): """Total number of items in the thread-safe container.""" self.lock.acquire() L = self.length self.lock.release() return L def isFull(self): """Check that buffer is full""" return len(self) == self.max_capacity def isEmpty(self): """Predict which allow to check is container empty.""" return len(self) == 0 def pushBack(self, item): """ Push item into the back of a container, with blocking. Args: item (object): item which is inserted into container """ while True: self.lock.acquire() if self.length == self.max_capacity: self.lock.release() time.sleep(0.0001) continue else: self.items[self.back_index] = item self.back_index = (self.back_index + 1) % self.max_capacity self.length += 1 self.lock.release() self.item_is_ready.release() break return self def waitForItem(self): """Wait for item available for popping from container via popFront(), blocking.""" self.item_is_ready.acquire() return self def popFront(self): """ Get item from the front of a container, no blocking. Method does not perform checking is any element in the container is available. Please use waitForItem() or len() if you're not sure. Returns: object: item from a container """ self.lock.acquire() return_item = self.items[self.front_index] self.front_index = (self.front_index + 1) % self.max_capacity self.length -= 1 self.lock.release() return return_item def front(self): """Get item from the front of a container, not blocking.""" self.lock.acquire() return_item = self.items[self.front_index] self.lock.release() return return_item def get(self, index): """ Get item from the front of a container, not blocking Method does not perform checking is index within need range accessible range. Returns: object: item from a container """ self.lock.acquire() index = index + self.front_index index = index % self.max_capacity return_item = self.items[index] self.lock.release() return return_item def __getitem__(self, index): return self.get(index)
Methods
def front(self)
-
Get item from the front of a container, not blocking.
Expand source code
def front(self): """Get item from the front of a container, not blocking.""" self.lock.acquire() return_item = self.items[self.front_index] self.lock.release() return return_item
def get(self, index)
-
Get item from the front of a container, not blocking
Method does not perform checking is index within need range accessible range.
Returns: object: item from a container
Expand source code
def get(self, index): """ Get item from the front of a container, not blocking Method does not perform checking is index within need range accessible range. Returns: object: item from a container """ self.lock.acquire() index = index + self.front_index index = index % self.max_capacity return_item = self.items[index] self.lock.release() return return_item
def isEmpty(self)
-
Predict which allow to check is container empty.
Expand source code
def isEmpty(self): """Predict which allow to check is container empty.""" return len(self) == 0
def isFull(self)
-
Check that buffer is full
Expand source code
def isFull(self): """Check that buffer is full""" return len(self) == self.max_capacity
def popFront(self)
-
Get item from the front of a container, no blocking.
Method does not perform checking is any element in the container is available. Please use waitForItem() or len() if you're not sure.
Returns: object: item from a container
Expand source code
def popFront(self): """ Get item from the front of a container, no blocking. Method does not perform checking is any element in the container is available. Please use waitForItem() or len() if you're not sure. Returns: object: item from a container """ self.lock.acquire() return_item = self.items[self.front_index] self.front_index = (self.front_index + 1) % self.max_capacity self.length -= 1 self.lock.release() return return_item
def pushBack(self, item)
-
Push item into the back of a container, with blocking.
Args
item
:object
- item which is inserted into container
Expand source code
def pushBack(self, item): """ Push item into the back of a container, with blocking. Args: item (object): item which is inserted into container """ while True: self.lock.acquire() if self.length == self.max_capacity: self.lock.release() time.sleep(0.0001) continue else: self.items[self.back_index] = item self.back_index = (self.back_index + 1) % self.max_capacity self.length += 1 self.lock.release() self.item_is_ready.release() break return self
def waitForItem(self)
-
Wait for item available for popping from container via popFront(), blocking.
Expand source code
def waitForItem(self): """Wait for item available for popping from container via popFront(), blocking.""" self.item_is_ready.acquire() return self