1/*
2 * Copyright (C) 2008 Apple Inc. All rights reserved.
3 * Copyright (C) 2009 Google Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1.  Redistributions of source code must retain the above copyright
10 *     notice, this list of conditions and the following disclaimer.
11 * 2.  Redistributions in binary form must reproduce the above copyright
12 *     notice, this list of conditions and the following disclaimer in the
13 *     documentation and/or other materials provided with the distribution.
14 * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
15 *     its contributors may be used to endorse or promote products derived
16 *     from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30#ifndef MessageQueue_h
31#define MessageQueue_h
32
33#include <limits>
34#include "wtf/Assertions.h"
35#include "wtf/Deque.h"
36#include "wtf/Noncopyable.h"
37#include "wtf/OwnPtr.h"
38#include "wtf/PassOwnPtr.h"
39#include "wtf/ThreadingPrimitives.h"
40
41namespace WTF {
42
43    enum MessageQueueWaitResult {
44        MessageQueueTerminated,       // Queue was destroyed while waiting for message.
45        MessageQueueTimeout,          // Timeout was specified and it expired.
46        MessageQueueMessageReceived   // A message was successfully received and returned.
47    };
48
49    // The queue takes ownership of messages and transfer it to the new owner
50    // when messages are fetched from the queue.
51    // Essentially, MessageQueue acts as a queue of OwnPtr<DataType>.
52    template<typename DataType>
53    class MessageQueue {
54        WTF_MAKE_NONCOPYABLE(MessageQueue);
55    public:
56        MessageQueue() : m_killed(false) { }
57
58        // Returns true if the queue is still alive, false if the queue has been killed.
59        bool append(PassOwnPtr<DataType>);
60        void appendAndKill(PassOwnPtr<DataType>);
61        bool appendAndCheckEmpty(PassOwnPtr<DataType>);
62        void prepend(PassOwnPtr<DataType>);
63
64        PassOwnPtr<DataType> waitForMessage();
65        PassOwnPtr<DataType> tryGetMessage();
66        PassOwnPtr<DataType> tryGetMessageIgnoringKilled();
67        PassOwnPtr<DataType> waitForMessageWithTimeout(MessageQueueWaitResult&, double absoluteTime);
68
69        void kill();
70        bool killed() const;
71
72        // The result of isEmpty() is only valid if no other thread is manipulating the queue at the same time.
73        bool isEmpty();
74
75        static double infiniteTime() { return std::numeric_limits<double>::max(); }
76
77    private:
78        mutable Mutex m_mutex;
79        ThreadCondition m_condition;
80        Deque<OwnPtr<DataType> > m_queue;
81        bool m_killed;
82    };
83
84    template<typename DataType>
85    inline bool MessageQueue<DataType>::append(PassOwnPtr<DataType> message)
86    {
87        MutexLocker lock(m_mutex);
88        m_queue.append(message);
89        m_condition.signal();
90        return !m_killed;
91    }
92
93    template<typename DataType>
94    inline void MessageQueue<DataType>::appendAndKill(PassOwnPtr<DataType> message)
95    {
96        MutexLocker lock(m_mutex);
97        m_queue.append(message);
98        m_killed = true;
99        m_condition.broadcast();
100    }
101
102    // Returns true if the queue was empty before the item was added.
103    template<typename DataType>
104    inline bool MessageQueue<DataType>::appendAndCheckEmpty(PassOwnPtr<DataType> message)
105    {
106        MutexLocker lock(m_mutex);
107        bool wasEmpty = m_queue.isEmpty();
108        m_queue.append(message);
109        m_condition.signal();
110        return wasEmpty;
111    }
112
113    template<typename DataType>
114    inline void MessageQueue<DataType>::prepend(PassOwnPtr<DataType> message)
115    {
116        MutexLocker lock(m_mutex);
117        m_queue.prepend(message);
118        m_condition.signal();
119    }
120
121    template<typename DataType>
122    inline PassOwnPtr<DataType> MessageQueue<DataType>::waitForMessage()
123    {
124        MessageQueueWaitResult exitReason;
125        OwnPtr<DataType> result = waitForMessageWithTimeout(exitReason, infiniteTime());
126        ASSERT(exitReason == MessageQueueTerminated || exitReason == MessageQueueMessageReceived);
127        return result.release();
128    }
129
130    template<typename DataType>
131    inline PassOwnPtr<DataType> MessageQueue<DataType>::waitForMessageWithTimeout(MessageQueueWaitResult& result, double absoluteTime)
132    {
133        MutexLocker lock(m_mutex);
134        bool timedOut = false;
135
136        while (!m_killed && !timedOut && m_queue.isEmpty())
137            timedOut = !m_condition.timedWait(m_mutex, absoluteTime);
138
139        ASSERT(!timedOut || absoluteTime != infiniteTime());
140
141        if (m_killed) {
142            result = MessageQueueTerminated;
143            return nullptr;
144        }
145
146        if (timedOut) {
147            result = MessageQueueTimeout;
148            return nullptr;
149        }
150
151        ASSERT_WITH_SECURITY_IMPLICATION(!m_queue.isEmpty());
152        result = MessageQueueMessageReceived;
153
154        return m_queue.takeFirst();
155    }
156
157    template<typename DataType>
158    inline PassOwnPtr<DataType> MessageQueue<DataType>::tryGetMessage()
159    {
160        MutexLocker lock(m_mutex);
161        if (m_killed)
162            return nullptr;
163        if (m_queue.isEmpty())
164            return nullptr;
165
166        return m_queue.takeFirst();
167    }
168
169    template<typename DataType>
170    inline PassOwnPtr<DataType> MessageQueue<DataType>::tryGetMessageIgnoringKilled()
171    {
172        MutexLocker lock(m_mutex);
173        if (m_queue.isEmpty())
174            return nullptr;
175
176        return m_queue.takeFirst();
177    }
178
179    template<typename DataType>
180    inline bool MessageQueue<DataType>::isEmpty()
181    {
182        MutexLocker lock(m_mutex);
183        if (m_killed)
184            return true;
185        return m_queue.isEmpty();
186    }
187
188    template<typename DataType>
189    inline void MessageQueue<DataType>::kill()
190    {
191        MutexLocker lock(m_mutex);
192        m_killed = true;
193        m_condition.broadcast();
194    }
195
196    template<typename DataType>
197    inline bool MessageQueue<DataType>::killed() const
198    {
199        MutexLocker lock(m_mutex);
200        return m_killed;
201    }
202} // namespace WTF
203
204using WTF::MessageQueue;
205// MessageQueueWaitResult enum and all its values.
206using WTF::MessageQueueWaitResult;
207using WTF::MessageQueueTerminated;
208using WTF::MessageQueueTimeout;
209using WTF::MessageQueueMessageReceived;
210
211#endif // MessageQueue_h
212