1/*
2 * Copyright 2002, Marcus Overhagen. All rights reserved.
3 * Copyright 2009, Axel D��rfler, axeld@pinc-software.de.
4 * Distributed under the terms of the MIT License.
5 */
6
7
8#include "BufferManager.h"
9
10#include <Autolock.h>
11
12#include "MediaDebug.h"
13#include "SharedBufferList.h"
14
15
16BufferManager::BufferManager()
17	:
18	fSharedBufferList(NULL),
19	fSharedBufferListArea(-1),
20	fNextBufferID(1),
21	fLocker("buffer manager locker")
22{
23	fSharedBufferListArea
24		= BPrivate::SharedBufferList::Create(&fSharedBufferList);
25}
26
27
28BufferManager::~BufferManager()
29{
30	fSharedBufferList->Put();
31}
32
33
34area_id
35BufferManager::SharedBufferListArea()
36{
37	return fSharedBufferListArea;
38}
39
40
41status_t
42BufferManager::RegisterBuffer(team_id team, media_buffer_id bufferID,
43	size_t* _size, int32* _flags, size_t* _offset, area_id* _area)
44{
45	BAutolock lock(fLocker);
46
47	TRACE("RegisterBuffer team = %" B_PRId32 ", bufferid = %" B_PRId32 "\n",
48		team, bufferID);
49
50	buffer_info* info;
51	if (!fBufferInfoMap.Get(bufferID, info)) {
52		ERROR("failed to register buffer! team = %" B_PRId32 ", bufferid = %"
53			B_PRId32 "\n", team, bufferID);
54		return B_ERROR;
55	}
56
57	info->teams.insert(team);
58
59	*_area = info->area;
60	*_offset = info->offset;
61	*_size = info->size,
62	*_flags = info->flags;
63
64	return B_OK;
65}
66
67
68status_t
69BufferManager::RegisterBuffer(team_id team, size_t size, int32 flags,
70	size_t offset, area_id area, media_buffer_id* _bufferID)
71{
72	BAutolock lock(fLocker);
73	TRACE("RegisterBuffer team = %" B_PRId32 ", area = %"
74		B_PRId32 ", offset = %" B_PRIuSIZE ", size = %" B_PRIuSIZE "\n",
75		team, area, offset, size);
76
77	area_id clonedArea = _CloneArea(area);
78	if (clonedArea < 0) {
79		ERROR("RegisterBuffer: failed to clone buffer! error = %#" B_PRIx32
80			", team = %" B_PRId32 ", area = %" B_PRId32 ", offset = %"
81			B_PRIuSIZE ", size = %" B_PRIuSIZE "\n", clonedArea, team,
82			area, offset, size);
83		return clonedArea;
84	}
85
86	buffer_info info;
87	info.id = fNextBufferID++;
88	info.area = clonedArea;
89	info.offset = offset;
90	info.size = size;
91	info.flags = flags;
92
93	try {
94		info.teams.insert(team);
95		if (fBufferInfoMap.Put(info.id, info) != B_OK)
96			throw std::bad_alloc();
97	} catch (std::bad_alloc& exception) {
98		_ReleaseClonedArea(clonedArea);
99		return B_NO_MEMORY;
100	}
101
102	TRACE("RegisterBuffer: done, bufferID = %" B_PRId32 "\n", info.id);
103
104	*_bufferID = info.id;
105	return B_OK;
106}
107
108
109status_t
110BufferManager::UnregisterBuffer(team_id team, media_buffer_id bufferID)
111{
112	BAutolock lock(fLocker);
113	TRACE("UnregisterBuffer: team = %" B_PRId32 ", bufferID = %" B_PRId32 "\n",
114		team, bufferID);
115
116	buffer_info* info;
117	if (!fBufferInfoMap.Get(bufferID, info)) {
118		ERROR("UnregisterBuffer: failed to unregister buffer! team = %"
119			B_PRId32 ", bufferID = %" B_PRId32 "\n", team, bufferID);
120		return B_ERROR;
121	}
122
123	if (info->teams.find(team) == info->teams.end()) {
124		ERROR("UnregisterBuffer: failed to find team = %" B_PRId32 " belonging"
125			" to bufferID = %" B_PRId32 "\n", team, bufferID);
126		return B_ERROR;
127	}
128
129	info->teams.erase(team);
130
131	TRACE("UnregisterBuffer: team = %" B_PRId32 " removed from bufferID = %"
132		B_PRId32 "\n", team, bufferID);
133
134	if (info->teams.empty()) {
135		_ReleaseClonedArea(info->area);
136		fBufferInfoMap.Remove(bufferID);
137
138		TRACE("UnregisterBuffer: bufferID = %" B_PRId32 " removed\n", bufferID);
139	}
140
141	return B_OK;
142}
143
144
145void
146BufferManager::CleanupTeam(team_id team)
147{
148	BAutolock lock(fLocker);
149
150	TRACE("BufferManager::CleanupTeam: team %" B_PRId32 "\n", team);
151
152	BufferInfoMap::Iterator iterator = fBufferInfoMap.GetIterator();
153	while (iterator.HasNext()) {
154		BufferInfoMap::Entry entry = iterator.Next();
155
156		entry.value.teams.erase(team);
157
158		if (entry.value.teams.empty()) {
159			PRINT(1, "BufferManager::CleanupTeam: removing buffer id %"
160				B_PRId32 " that has no teams\n", entry.key.GetHashCode());
161			_ReleaseClonedArea(entry.value.area);
162			fBufferInfoMap.Remove(iterator);
163		}
164	}
165}
166
167
168void
169BufferManager::Dump()
170{
171	BAutolock lock(fLocker);
172
173	printf("\n");
174	printf("BufferManager: list of buffers follows:\n");
175
176	BufferInfoMap::Iterator iterator = fBufferInfoMap.GetIterator();
177	while (iterator.HasNext()) {
178		buffer_info info = iterator.Next().value;
179		printf(" buffer-id %" B_PRId32 ", area-id %" B_PRId32 ", offset %ld, "
180			"size %ld, flags %#08" B_PRIx32 "\n", info.id, info.area,
181			info.offset, info.size, info.flags);
182		printf("   assigned teams: ");
183
184		std::set<team_id>::iterator teamIterator = info.teams.begin();
185		for (; teamIterator != info.teams.end(); teamIterator++) {
186			printf("%" B_PRId32 ", ", *teamIterator);
187		}
188		printf("\n");
189	}
190	printf("BufferManager: list end\n");
191}
192
193
194area_id
195BufferManager::_CloneArea(area_id area)
196{
197	{
198		clone_info* info;
199		if (fCloneInfoMap.Get(area, info)) {
200			// we have already cloned this particular area
201			TRACE("BufferManager::_CloneArea() area %" B_PRId32 " has already"
202				" been cloned (id %" B_PRId32 ")\n", area, info->clone);
203
204			info->ref_count++;
205			return info->clone;
206		}
207	}
208
209	void* address;
210	area_id clonedArea = clone_area("media_server cloned buffer", &address,
211		B_ANY_ADDRESS, B_READ_AREA | B_WRITE_AREA | B_CLONEABLE_AREA, area);
212
213	TRACE("BufferManager::_CloneArea() cloned area %" B_PRId32 ", clone id %"
214		B_PRId32 "\n", area, clonedArea);
215
216	if (clonedArea < 0)
217		return clonedArea;
218
219	clone_info info;
220	info.clone = clonedArea;
221	info.ref_count = 1;
222
223	if (fCloneInfoMap.Put(area, info) == B_OK) {
224		if (fSourceInfoMap.Put(clonedArea, area) == B_OK)
225			return clonedArea;
226
227		fCloneInfoMap.Remove(area);
228	}
229
230	delete_area(clonedArea);
231	return B_NO_MEMORY;
232}
233
234
235void
236BufferManager::_ReleaseClonedArea(area_id clone)
237{
238	area_id source = fSourceInfoMap.Get(clone);
239
240	clone_info* info;
241	if (!fCloneInfoMap.Get(source, info)) {
242		ERROR("BufferManager::_ReleaseClonedArea(): could not find clone info "
243			"for id %" B_PRId32 " (clone %" B_PRId32 ")\n", source, clone);
244		return;
245	}
246
247	if (--info->ref_count == 0) {
248		TRACE("BufferManager::_ReleaseClonedArea(): delete cloned area %"
249			B_PRId32 " (source %" B_PRId32 ")\n", clone, source);
250
251		fSourceInfoMap.Remove(clone);
252		fCloneInfoMap.Remove(source);
253		delete_area(clone);
254	} else {
255		TRACE("BufferManager::_ReleaseClonedArea(): released cloned area %"
256			B_PRId32 " (source %" B_PRId32 ")\n", clone, source);
257	}
258}
259