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 "debug.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 = %ld, bufferid = %ld\n", team, bufferID);
48
49	buffer_info* info;
50	if (!fBufferInfoMap.Get(bufferID, info)) {
51		ERROR("failed to register buffer! team = %ld, bufferid = %ld\n", team,
52			bufferID);
53		return B_ERROR;
54	}
55
56	info->teams.insert(team);
57
58	*_area = info->area;
59	*_offset = info->offset;
60	*_size = info->size,
61	*_flags = info->flags;
62
63	return B_OK;
64}
65
66
67status_t
68BufferManager::RegisterBuffer(team_id team, size_t size, int32 flags,
69	size_t offset, area_id area, media_buffer_id* _bufferID)
70{
71	BAutolock lock(fLocker);
72	TRACE("RegisterBuffer team = %ld, area = %ld, offset = %ld, size = %ld\n",
73		team, area, offset, size);
74
75	area_id clonedArea = _CloneArea(area);
76	if (clonedArea < 0) {
77		ERROR("RegisterBuffer: failed to clone buffer! error = %#lx, team = "
78			"%ld, area = %ld, offset = %ld, size = %ld\n", clonedArea, team,
79			area, offset, size);
80		return clonedArea;
81	}
82
83	buffer_info info;
84	info.id = fNextBufferID++;
85	info.area = clonedArea;
86	info.offset = offset;
87	info.size = size;
88	info.flags = flags;
89
90	try {
91		info.teams.insert(team);
92		if (fBufferInfoMap.Put(info.id, info) != B_OK)
93			throw std::bad_alloc();
94	} catch (std::bad_alloc& exception) {
95		_ReleaseClonedArea(clonedArea);
96		return B_NO_MEMORY;
97	}
98
99	TRACE("RegisterBuffer: done, bufferID = %ld\n", info.id);
100
101	*_bufferID = info.id;
102	return B_OK;
103}
104
105
106status_t
107BufferManager::UnregisterBuffer(team_id team, media_buffer_id bufferID)
108{
109	BAutolock lock(fLocker);
110	TRACE("UnregisterBuffer: team = %ld, bufferID = %ld\n", team, bufferID);
111
112	buffer_info* info;
113	if (!fBufferInfoMap.Get(bufferID, info)) {
114		ERROR("UnregisterBuffer: failed to unregister buffer! team = %ld, "
115			"bufferID = %ld\n", team, bufferID);
116		return B_ERROR;
117	}
118
119	if (info->teams.find(team) == info->teams.end()) {
120		ERROR("UnregisterBuffer: failed to find team = %ld belonging to "
121			"bufferID = %ld\n", team, bufferID);
122		return B_ERROR;
123	}
124
125	info->teams.erase(team);
126
127	TRACE("UnregisterBuffer: team = %ld removed from bufferID = %ld\n", team,
128		bufferID);
129
130	if (info->teams.empty()) {
131		_ReleaseClonedArea(info->area);
132		fBufferInfoMap.Remove(bufferID);
133
134		TRACE("UnregisterBuffer: bufferID = %ld removed\n", bufferID);
135	}
136
137	return B_OK;
138}
139
140
141void
142BufferManager::CleanupTeam(team_id team)
143{
144	BAutolock lock(fLocker);
145
146	TRACE("BufferManager::CleanupTeam: team %ld\n", team);
147
148	BufferInfoMap::Iterator iterator = fBufferInfoMap.GetIterator();
149	while (iterator.HasNext()) {
150		BufferInfoMap::Entry entry = iterator.Next();
151
152		entry.value.teams.erase(team);
153
154		if (entry.value.teams.empty()) {
155			PRINT(1, "BufferManager::CleanupTeam: removing buffer id %ld that "
156				"has no teams\n", entry.key.GetHashCode());
157			_ReleaseClonedArea(entry.value.area);
158			iterator.Remove();
159		}
160	}
161}
162
163
164void
165BufferManager::Dump()
166{
167	BAutolock lock(fLocker);
168
169	printf("\n");
170	printf("BufferManager: list of buffers follows:\n");
171
172	BufferInfoMap::Iterator iterator = fBufferInfoMap.GetIterator();
173	while (iterator.HasNext()) {
174		buffer_info& info = *iterator.NextValue();
175		printf(" buffer-id %ld, area-id %ld, offset %ld, size %ld, flags "
176			"%#08lx\n", info.id, info.area, info.offset, info.size, info.flags);
177		printf("   assigned teams: ");
178
179		std::set<team_id>::iterator teamIterator = info.teams.begin();
180		for (; teamIterator != info.teams.end(); teamIterator++) {
181			printf("%ld, ", *teamIterator);
182		}
183		printf("\n");
184	}
185	printf("BufferManager: list end\n");
186}
187
188
189area_id
190BufferManager::_CloneArea(area_id area)
191{
192	{
193		clone_info* info;
194		if (fCloneInfoMap.Get(area, info)) {
195			// we have already cloned this particular area
196			TRACE("BufferManager::_CloneArea() area %ld has already been "
197				"cloned (id %ld)\n", area, info->clone);
198
199			info->ref_count++;
200			return info->clone;
201		}
202	}
203
204	void* address;
205	area_id clonedArea = clone_area("media_server cloned buffer", &address,
206		B_ANY_ADDRESS, B_READ_AREA | B_WRITE_AREA, area);
207
208	TRACE("BufferManager::_CloneArea() cloned area %ld, clone id %ld\n",
209		area, clonedArea);
210
211	if (clonedArea < 0)
212		return clonedArea;
213
214	clone_info info;
215	info.clone = clonedArea;
216	info.ref_count = 1;
217
218	if (fCloneInfoMap.Put(area, info) == B_OK) {
219		if (fSourceInfoMap.Put(clonedArea, area) == B_OK)
220			return clonedArea;
221
222		fCloneInfoMap.Remove(area);
223	}
224
225	delete_area(clonedArea);
226	return B_NO_MEMORY;
227}
228
229
230void
231BufferManager::_ReleaseClonedArea(area_id clone)
232{
233	area_id source = fSourceInfoMap.Get(clone);
234
235	clone_info* info;
236	if (!fCloneInfoMap.Get(source, info)) {
237		ERROR("BufferManager::_ReleaseClonedArea(): could not find clone info "
238			"for id %ld (clone %ld)\n", source, clone);
239		return;
240	}
241
242	if (--info->ref_count == 0) {
243		TRACE("BufferManager::_ReleaseClonedArea(): delete cloned area %ld "
244			"(source %ld)\n", clone, source);
245
246		fSourceInfoMap.Remove(clone);
247		fCloneInfoMap.Remove(source);
248		delete_area(clone);
249	} else {
250		TRACE("BufferManager::_ReleaseClonedArea(): released cloned area %ld "
251			"(source %ld)\n", clone, source);
252	}
253}
254