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