-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathFIFO_Array.h
115 lines (114 loc) · 3.13 KB
/
FIFO_Array.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#pragma once
#include <atomic>
#include <thread>
#include <iostream>
#include <list>
#include <mutex>
#include <condition_variable>
#include <vector>
#include <stdexcept>
// a thread-safe queue with a maximal size based on std::list<>::splice()
template <typename buffer>
class ThreadSafeQ
{
private:
// check whether the Q is running or closed
enum class State
{
OPEN,
CLOSED
};
State state; // The current state
size_t currSize; // The current size
size_t maxSize; // The maximal size
std::condition_variable cvPush, cvPop; // The condition variables to use for pushing/popping
std::mutex mutex; // The mutex for locking the Q
std::list<buffer> list; // The list that the Q is implemented with
public:
// enum to return to check whether could push or pop or queue was closed
enum QueueResult
{
OK,
CLOSED
};
// create and initialize the Q with maximum size
explicit ThreadSafeQ(size_t maxSize = 0) : state(State::OPEN), currSize(0), maxSize(maxSize)
{}
// Push data to Q, if queue is full then blocks
void push(buffer const& data)
{
// Creating temporary Q
decltype(list) tmpl;
tmpl.push_back(data);
// Push with lock
{
std::unique_lock<std::mutex> lock(mutex);
// wait until space is there in the Q
while (currSize == maxSize)
cvPush.wait(lock);
// Check whether the Q is closed or not and pushing is allowed
if (state == State::CLOSED)
throw std::runtime_error("The queue is closed and trying to push.");
// Pushing to Q
currSize += 1;
list.splice(list.end(), tmpl, tmpl.begin());
// popping thread to wake up
if (currSize == 1u)
cvPop.notify_one();
}
}
// Push data to Q with rvalue reference
void push(buffer&& data)
{
// Create temporary queue.
decltype(list) tmpl;
tmpl.push_back(data);
// Push with lock
{
std::unique_lock<std::mutex> lock(mutex);
// wait until space is there in the Q
while (currSize == maxSize)
cvPush.wait(lock);
// Check whether the Q is closed or not and pushing is allowed
if (state == State::CLOSED)
throw std::runtime_error("The queue is closed and trying to push.");
// Pushing to Q
currSize += 1;
list.splice(list.end(), tmpl, tmpl.begin());
// popping thread to wake up
cvPop.notify_one();
}
}
// Poping value from Q and write to var
// If successful, OK is returned, else if the Q is empty and was closed, then CLOSED is returned
QueueResult pop(buffer& var)
{
decltype(list) tmpl;
// Pop data to the tmpl
{
std::unique_lock<std::mutex> lock(mutex);
// wait until there is data, if there is no data
while (list.empty() && state != State::CLOSED)
cvPop.wait(lock);
// cannot return anything, if the Q was closed and the list is empty
if (list.empty() && state == State::CLOSED)
return CLOSED;
// If data found
currSize -= 1;
tmpl.splice(tmpl.begin(), list, list.begin());
// one pushing thread wake up
cvPush.notify_one();
}
// data write to var
var = tmpl.front();
return OK;
}
// No pushing data when the queue is closed
void close() noexcept
{
std::unique_lock<std::mutex> lock(mutex);
state = State::CLOSED;
// all consumers notify
cvPop.notify_all();
}
};