xref: /haiku/src/kits/media/ChunkCache.cpp (revision 220d04022750f40f8bac8f01fa551211e28d04f2)
1 /*
2  * Copyright 2009, Axel Dörfler, axeld@pinc-software.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "ChunkCache.h"
8 
9 #include <new>
10 #include <stdlib.h>
11 #include <string.h>
12 
13 #include "debug.h"
14 
15 // #pragma mark -
16 
17 
18 ChunkCache::ChunkCache(sem_id waitSem, size_t maxBytes)
19 	:
20 	BLocker("media chunk cache"),
21 	fWaitSem(waitSem)
22 {
23 	rtm_create_pool(&fRealTimePool, maxBytes, "media chunk cache");
24 	fMaxBytes = rtm_available(fRealTimePool);
25 }
26 
27 
28 ChunkCache::~ChunkCache()
29 {
30 	rtm_delete_pool(fRealTimePool);
31 }
32 
33 
34 status_t
35 ChunkCache::InitCheck() const
36 {
37 	if (fRealTimePool == NULL)
38 		return B_NO_MEMORY;
39 
40 	return B_OK;
41 }
42 
43 
44 void
45 ChunkCache::MakeEmpty()
46 {
47 	ASSERT(IsLocked());
48 
49 	while (!fChunkCache.empty()) {
50 		RecycleChunk(fChunkCache.front());
51 		fChunkCache.pop();
52 	}
53 
54 	release_sem(fWaitSem);
55 }
56 
57 
58 bool
59 ChunkCache::SpaceLeft() const
60 {
61 	ASSERT(IsLocked());
62 
63 	if (fChunkCache.size() >= CACHE_MAX_ENTRIES) {
64 		return false;
65 	}
66 
67 	// If there is no more memory we are likely to fail soon after
68 	return sizeof(chunk_buffer) + 2048 < rtm_available(fRealTimePool);
69 }
70 
71 
72 chunk_buffer*
73 ChunkCache::NextChunk(Reader* reader, void* cookie)
74 {
75 	ASSERT(IsLocked());
76 
77 	chunk_buffer* chunk = NULL;
78 
79 	if (fChunkCache.empty()) {
80 		TRACE("ChunkCache is empty, going direct to reader\n");
81 		if (ReadNextChunk(reader, cookie)) {
82 			return NextChunk(reader, cookie);
83 		}
84 	} else {
85 		chunk = fChunkCache.front();
86 		fChunkCache.pop();
87 
88 		release_sem(fWaitSem);
89 	}
90 
91 	return chunk;
92 }
93 
94 
95 /*	Moves the specified chunk to the unused list.
96 	This means the chunk data can be overwritten again.
97 */
98 void
99 ChunkCache::RecycleChunk(chunk_buffer* chunk)
100 {
101 	ASSERT(IsLocked());
102 
103 	rtm_free(chunk->buffer);
104 	chunk->capacity = 0;
105 	chunk->size = 0;
106 	chunk->buffer = NULL;
107 	fUnusedChunks.push_back(chunk);
108 }
109 
110 
111 bool
112 ChunkCache::ReadNextChunk(Reader* reader, void* cookie)
113 {
114 	ASSERT(IsLocked());
115 
116 	// retrieve chunk buffer
117 	chunk_buffer* chunk = NULL;
118 	if (fUnusedChunks.empty()) {
119 		// allocate a new one
120 		chunk = (chunk_buffer*)rtm_alloc(fRealTimePool, sizeof(chunk_buffer));
121 		if (chunk == NULL) {
122 			ERROR("RTM Pool empty allocating chunk buffer structure");
123 			return false;
124 		}
125 
126 		chunk->size = 0;
127 		chunk->capacity = 0;
128 		chunk->buffer = NULL;
129 
130 	} else {
131 		chunk = fUnusedChunks.front();
132 		fUnusedChunks.pop_front();
133 	}
134 
135 	const void* buffer;
136 	size_t bufferSize;
137 	chunk->status = reader->GetNextChunk(cookie, &buffer, &bufferSize,
138 		&chunk->header);
139 	if (chunk->status == B_OK) {
140 		if (chunk->capacity < bufferSize) {
141 			// adapt buffer size
142 			rtm_free(chunk->buffer);
143 			chunk->capacity = (bufferSize + 2047) & ~2047;
144 			chunk->buffer = rtm_alloc(fRealTimePool, chunk->capacity);
145 			if (chunk->buffer == NULL) {
146 				rtm_free(chunk);
147 				ERROR("RTM Pool empty allocating chunk buffer\n");
148 				return false;
149 			}
150 		}
151 
152 		memcpy(chunk->buffer, buffer, bufferSize);
153 		chunk->size = bufferSize;
154 	}
155 
156 	fChunkCache.push(chunk);
157 	return chunk->status == B_OK;
158 }
159