xref: /haiku/src/servers/media/BufferManager.cpp (revision b289aaf66bbf6e173aa90fa194fc256965f1b34d)
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 
16 BufferManager::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 
28 BufferManager::~BufferManager()
29 {
30 	fSharedBufferList->Put();
31 }
32 
33 
34 area_id
35 BufferManager::SharedBufferListArea()
36 {
37 	return fSharedBufferListArea;
38 }
39 
40 
41 status_t
42 BufferManager::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 
67 status_t
68 BufferManager::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 
106 status_t
107 BufferManager::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 
141 void
142 BufferManager::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 
164 void
165 BufferManager::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 
189 area_id
190 BufferManager::_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 
230 void
231 BufferManager::_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