wangzhengquan
2020-06-12 9f2f9eed5ca905c4641b296773ac21aaa735624d
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
116
117
118
119
#ifndef _SAFEQUEUE_H_
#define _SAFEQUEUE_H_
 
#include <queue>
#include <condition_variable>
#include <mutex>
#include <chrono>
#include <limits> // std::numeric_limits<>::max
 
#define SAFE_QUEUE_DEFAULT_MAX_SIZE std::numeric_limits<std::size_t >::max()
 
/// @brief thread-safe queue
/// It uses a mutex+condition variables to protect the internal queue
/// implementation. Inserting or reading elements use the same mutex
template <typename T>
class SafeQueue
{
public:
    /// @brief constructor
    /// @param a_maxSize optional parameter with the maximum size of the queue
    SafeQueue(std::size_t a_maxSize = SAFE_QUEUE_DEFAULT_MAX_SIZE);
    
    /// @brief destructor
    ~SafeQueue();
    
    /// @brief copy contructor
    /// WARNING: Use with great care, this function call can take a long time
    /// and block other threads from pushing/popping elements into the source
    /// queue
    SafeQueue(const SafeQueue<T>& a_src);
    
    /// @brief operator= overloading
    /// This function blocks the a_src and "this" SafeQueues and copies the
    /// contents of a_src into "this" SafeQueue. 
    /// WARNING: Use with great care, this function call can take a long time
    /// and block other threads from pushing/popping elements into the queues
    /// @param a_src the "right" side of the operator=
    /// @return a const reference to this object
    const SafeQueue<T>& operator=(const SafeQueue<T> &a_src);
    
    /// @brief move contructor
    SafeQueue(SafeQueue<T>&& a_src);
    
    /// @brief move assignment
    SafeQueue<T>& operator=(SafeQueue<T>&& a_src);
 
    /// @brief Check if the queue is empty
    /// This call can block if another thread owns the lock that protects the
    /// queue
    /// @return true if the queue is empty. False otherwise
    bool isEmpty() const;
 
    /// @brief inserts an element into queue queue
    /// This call can block if another thread owns the lock that protects the
    /// queue. If the queue is full The thread will be blocked in this queue
    /// until someone else gets an element from the queue
    /// @param element to insert into the queue
    void push(const T &a_elem);
 
    /// @brief inserts an element into queue queue
    /// This call can block if another thread owns the lock that protects the
    /// queue. If the queue is full The call will return false and the element
    /// won't be inserted
    /// @param element to insert into the queue
    /// @return True if the elem was successfully inserted into the queue.
    ///         False otherwise
    bool tryPush(const T &a_elem);
 
    /// @brief extracts an element from the queue (and deletes it from the q)
    /// If the queue is empty this call will block the thread until there is
    /// something in the queue to be extracted
    /// @param a reference where the element from the queue will be saved to
    void pop(T &out_data);
 
    /// @brief extracts an element from the queue (and deletes it from the q)
    /// This call gets the block that protects the queue. It will extract the
    /// element from the queue only if there are elements in it
    /// @param reference to the variable where the result will be saved
    /// @return True if the element was retrieved from the queue.
    ///         False if the queue was empty
    bool tryPop(T &out_data);
 
    /// @brief extracts an element from the queue (and deletes it from the q)
    /// If the queue is empty this call will block the thread until there
    /// is something in the queue to be extracted or until the timer
    /// (2nd parameter) expires
    /// @param reference to the variable where the result will be saved
    /// @param duration to wait before returning if the queue was empty
    ///        you may also pass into this a std::seconds or std::milliseconds
    ///        (defined in std::chrono)
    /// @return True if the element was retrieved from the queue.
    ///         False if the timeout was hit and nothing could be extracted
    ///         from the queue
    bool timedWaitPop(T &data, std::chrono::microseconds a_microsecs);
 
protected:
    /// the actual queue data structure protected by this SafeQueue wrapper
    std::queue<T> m_theQueue;
    /// maximum number of elements for the queue
    std::size_t m_maximumSize;
    /// Mutex to protect the queue
    mutable std::mutex m_mutex;
    /// Conditional variable to wake up threads
    mutable std::condition_variable m_cond;
    
    /// @brief calculate if copying a_src into this instance will need to 
    ///        wake up potential threads waiting to perform push or pop ops.
    /// WARNING: It assumes the caller holds all mutexes required to access
    /// to the data of this and a_src SafeQueues
    /// @param a_src const reference to the SafeQueue that will be copied 
    ///        into this object
    /// @return true if threads will need to be waken up. False otherwise
    inline bool wakeUpSignalNeeded(const SafeQueue<T> &a_src) const;
};
 
// include the implementation file
#include "safe_queue_impl.h"
 
#endif /* _SAFEQUEUE_H_ */