1// BlockingQueue.h
2//
3// Copyright (c) 2004, Ingo Weinhold (bonefish@cs.tu-berlin.de)
4//
5// Permission is hereby granted, free of charge, to any person obtaining a
6// copy of this software and associated documentation files (the "Software"),
7// to deal in the Software without restriction, including without limitation
8// the rights to use, copy, modify, merge, publish, distribute, sublicense,
9// and/or sell copies of the Software, and to permit persons to whom the
10// Software is furnished to do so, subject to the following conditions:
11//
12// The above copyright notice and this permission notice shall be included in
13// all copies or substantial portions of the Software.
14//
15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21// DEALINGS IN THE SOFTWARE.
22//
23// Except as contained in this notice, the name of a copyright holder shall
24// not be used in advertising or otherwise to promote the sale, use or other
25// dealings in this Software without prior written authorization of the
26// copyright holder.
27
28#ifndef BLOCKING_QUEUE_H
29#define BLOCKING_QUEUE_H
30
31#include <AutoLocker.h>
32#include <OS.h>
33
34#include "DebugSupport.h"
35#include "Locker.h"
36#include "Vector.h"
37
38template<typename Element>
39class BlockingQueue : public Locker {
40public:
41								BlockingQueue(const char* name = NULL);
42								~BlockingQueue();
43
44			status_t			InitCheck() const;
45
46			status_t			Close(bool deleteElements,
47									const Vector<Element*>** elements = NULL);
48
49			status_t			Push(Element* element);
50			status_t			Pop(Element** element,
51									bigtime_t timeout = B_INFINITE_TIMEOUT);
52			status_t			Peek(Element** element);
53			status_t			Remove(Element* element);
54
55			int32				Size() const;
56
57private:
58			Vector<Element*>	fElements;
59			sem_id				fElementSemaphore;
60};
61
62// constructor
63template<typename Element>
64BlockingQueue<Element>::BlockingQueue(const char* name)
65	: fElements(),
66	  fElementSemaphore(-1)
67{
68	fElementSemaphore = create_sem(0, (name ? name : "blocking queue"));
69}
70
71// destructor
72template<typename Element>
73BlockingQueue<Element>::~BlockingQueue()
74{
75	if (fElementSemaphore >= 0)
76		delete_sem(fElementSemaphore);
77}
78
79// InitCheck
80template<typename Element>
81status_t
82BlockingQueue<Element>::InitCheck() const
83{
84	return (fElementSemaphore < 0 ? fElementSemaphore : B_OK);
85}
86
87// Close
88template<typename Element>
89status_t
90BlockingQueue<Element>::Close(bool deleteElements,
91	const Vector<Element*>** elements)
92{
93	AutoLocker<Locker> _(this);
94	status_t error = delete_sem(fElementSemaphore);
95	if (error != B_OK)
96		return error;
97	fElementSemaphore = -1;
98	if (elements)
99		*elements = &fElements;
100	if (deleteElements) {
101		int32 count = fElements.Count();
102		for (int32 i = 0; i < count; i++)
103			delete fElements.ElementAt(i);
104	}
105	return error;
106}
107
108// Push
109template<typename Element>
110status_t
111BlockingQueue<Element>::Push(Element* element)
112{
113	AutoLocker<Locker> _(this);
114	if (fElementSemaphore < 0)
115		return B_NO_INIT;
116	status_t error = fElements.PushBack(element);
117	if (error != B_OK)
118		return error;
119	error = release_sem(fElementSemaphore);
120	if (error != B_OK)
121		fElements.Erase(fElements.Count() - 1);
122	return error;
123}
124
125// Pop
126template<typename Element>
127status_t
128BlockingQueue<Element>::Pop(Element** element, bigtime_t timeout)
129{
130	status_t error = acquire_sem_etc(fElementSemaphore, 1, B_RELATIVE_TIMEOUT,
131		timeout);
132	if (error != B_OK)
133		return error;
134	AutoLocker<Locker> _(this);
135	if (fElementSemaphore < 0)
136		return B_NO_INIT;
137	int32 count = fElements.Count();
138	if (count == 0)
139		return B_ERROR;
140	*element = fElements.ElementAt(0);
141	fElements.Erase(0);
142	return B_OK;
143}
144
145// Peek
146template<typename Element>
147status_t
148BlockingQueue<Element>::Peek(Element** element)
149{
150	AutoLocker<Locker> _(this);
151	if (fElementSemaphore < 0)
152		return B_NO_INIT;
153	int32 count = fElements.Count();
154	if (count == 0)
155		return B_ENTRY_NOT_FOUND;
156	*element = fElements.ElementAt(0);
157	return B_OK;
158}
159
160// Remove
161template<typename Element>
162status_t
163BlockingQueue<Element>::Remove(Element* element)
164{
165	status_t error = acquire_sem_etc(fElementSemaphore, 1,
166		B_RELATIVE_TIMEOUT, 0);
167	if (error != B_OK)
168		return error;
169	AutoLocker<Locker> _(this);
170	if (fElementSemaphore < 0)
171		return B_NO_INIT;
172	int32 count = fElements.Remove(element);
173	if (count == 0) {
174		release_sem(fElementSemaphore);
175		return B_ENTRY_NOT_FOUND;
176	}
177	if (count > 1) {
178		ERROR("ERROR: BlockingQueue::Remove(): Removed %ld elements!\n",
179			count);
180	}
181	return error;
182}
183
184// Size
185template<typename Element>
186int32
187BlockingQueue<Element>::Size() const
188{
189	AutoLocker<Locker> _(this);
190	return (fElements.Count());
191}
192
193#endif	// BLOCKING_QUEUE_H
194