1/*
2 * Copyright 2004, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * All rights reserved. Distributed under the terms of the MIT license.
4 */
5#ifndef BLOCKING_QUEUE_H
6#define BLOCKING_QUEUE_H
7
8#include <vector>
9
10#include <Locker.h>
11
12#include "AutoLocker.h"
13
14using std::vector;
15
16typedef BLocker Locker;
17
18template<typename Element>
19class BlockingQueue : public Locker {
20public:
21								BlockingQueue(const char* name = NULL);
22								~BlockingQueue();
23
24			status_t			InitCheck() const;
25
26			status_t			Close(bool deleteElements,
27									const vector<Element*>** elements = NULL);
28
29			status_t			Push(Element* element);
30			status_t			Pop(Element** element,
31									bigtime_t timeout = B_INFINITE_TIMEOUT);
32			status_t			Peek(Element** element);
33			status_t			Remove(Element* element);
34
35			int32				Size();
36
37private:
38			vector<Element*>	fElements;
39			sem_id				fElementSemaphore;
40};
41
42// constructor
43template<typename Element>
44BlockingQueue<Element>::BlockingQueue(const char* name)
45	: fElements(),
46	  fElementSemaphore(-1)
47{
48	fElementSemaphore = create_sem(0, (name ? name : "blocking queue"));
49}
50
51// destructor
52template<typename Element>
53BlockingQueue<Element>::~BlockingQueue()
54{
55	if (fElementSemaphore >= 0)
56		delete_sem(fElementSemaphore);
57}
58
59// InitCheck
60template<typename Element>
61status_t
62BlockingQueue<Element>::InitCheck() const
63{
64	return (fElementSemaphore < 0 ? fElementSemaphore : B_OK);
65}
66
67// Close
68template<typename Element>
69status_t
70BlockingQueue<Element>::Close(bool deleteElements,
71	const vector<Element*>** elements)
72{
73	AutoLocker<Locker> _(this);
74	status_t error = delete_sem(fElementSemaphore);
75	if (error != B_OK)
76		return error;
77	fElementSemaphore = -1;
78	if (elements)
79		*elements = &fElements;
80	if (deleteElements) {
81		int32 count = fElements.size();
82		for (int32 i = 0; i < count; i++)
83			delete fElements[i];
84	}
85	return error;
86}
87
88// Push
89template<typename Element>
90status_t
91BlockingQueue<Element>::Push(Element* element)
92{
93	AutoLocker<Locker> _(this);
94	if (fElementSemaphore < 0)
95		return B_NO_INIT;
96	try {
97		fElements.push_back(element);
98	} catch (std::bad_alloc&) {
99		return B_NO_MEMORY;
100	}
101	status_t error = release_sem(fElementSemaphore);
102	if (error != B_OK)
103		fElements.erase(fElements.begin() + fElements.size() - 1);
104	return error;
105}
106
107// Pop
108template<typename Element>
109status_t
110BlockingQueue<Element>::Pop(Element** element, bigtime_t timeout)
111{
112	status_t error = acquire_sem_etc(fElementSemaphore, 1, B_RELATIVE_TIMEOUT,
113		timeout);
114	if (error != B_OK)
115		return error;
116	AutoLocker<Locker> _(this);
117	if (fElementSemaphore < 0)
118		return B_NO_INIT;
119	int32 count = fElements.size();
120	if (count == 0)
121		return B_ERROR;
122	*element = fElements[0];
123	fElements.erase(fElements.begin());
124	return B_OK;
125}
126
127// Peek
128template<typename Element>
129status_t
130BlockingQueue<Element>::Peek(Element** element)
131{
132	AutoLocker<Locker> _(this);
133	if (fElementSemaphore < 0)
134		return B_NO_INIT;
135	int32 count = fElements.size();
136	if (count == 0)
137		return B_ENTRY_NOT_FOUND;
138	*element = fElements[0];
139	return B_OK;
140}
141
142// Remove
143template<typename Element>
144status_t
145BlockingQueue<Element>::Remove(Element* element)
146{
147	status_t error = acquire_sem_etc(fElementSemaphore, 1,
148		B_RELATIVE_TIMEOUT, 0);
149	if (error != B_OK)
150		return error;
151	AutoLocker<Locker> _(this);
152	if (fElementSemaphore < 0)
153		return B_NO_INIT;
154
155	int32 count = 0;
156	for (int32 i = fElements.size() - 1; i >= 0; i--) {
157		if (fElements[i] == element) {
158			fElements.erase(fElements.begin() + i);
159			count++;
160		}
161	}
162	if (count == 0) {
163		release_sem(fElementSemaphore);
164		return B_ENTRY_NOT_FOUND;
165	}
166#if 0
167	if (count > 1) {
168		ERROR(("ERROR: BlockingQueue::Remove(): Removed %ld elements!\n",
169			count));
170	}
171#endif
172	return error;
173}
174
175// Size
176template<typename Element>
177int32
178BlockingQueue<Element>::Size()
179{
180	AutoLocker<Locker> _(this);
181	return (fElements.size());
182}
183
184#endif	// BLOCKING_QUEUE_H
185