1 /* 2 * Copyright (c) 2004, Marcus Overhagen 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without modification, 6 * are permitted provided that the following conditions are met: 7 * 8 * * Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 18 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 19 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 21 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 22 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 23 * OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 #include <string.h> 26 27 #include <Locker.h> 28 29 #include "ChunkCache.h" 30 #include "debug.h" 31 32 33 ChunkCache::ChunkCache() 34 { 35 fLocker = new BLocker("media chunk cache locker"); 36 37 // fEmptyChunkCount must be one less than the real chunk count, 38 // because the buffer returned by GetNextChunk must be preserved 39 // until the next call of that function, and must not be overwritten. 40 fEmptyChunkCount = CHUNK_COUNT - 1; 41 fReadyChunkCount = 0; 42 fNeedsRefill = 1; 43 44 fGetWaitSem = create_sem(0, "media chunk cache sem"); 45 46 fNextPut = &fChunkInfos[0]; 47 fNextGet = &fChunkInfos[0]; 48 49 for (int i = 0; i < CHUNK_COUNT; i++) { 50 fChunkInfos[i].next = (i == CHUNK_COUNT -1) ? &fChunkInfos[0] : &fChunkInfos[i + 1]; 51 fChunkInfos[i].buffer = NULL; 52 fChunkInfos[i].sizeUsed = 0; 53 fChunkInfos[i].sizeMax = 0; 54 fChunkInfos[i].err = B_ERROR; 55 } 56 } 57 58 59 ChunkCache::~ChunkCache() 60 { 61 delete_sem(fGetWaitSem); 62 delete fLocker; 63 for (int i = 0; i < CHUNK_COUNT; i++) { 64 free(fChunkInfos[i].buffer); 65 } 66 } 67 68 69 void 70 ChunkCache::MakeEmpty() 71 { 72 fLocker->Lock(); 73 fEmptyChunkCount = CHUNK_COUNT - 1; 74 fReadyChunkCount = 0; 75 fNextPut = &fChunkInfos[0]; 76 fNextGet = &fChunkInfos[0]; 77 atomic_or(&fNeedsRefill, 1); 78 fLocker->Unlock(); 79 } 80 81 82 bool 83 ChunkCache::NeedsRefill() 84 { 85 return atomic_or(&fNeedsRefill, 0); 86 } 87 88 89 status_t 90 ChunkCache::GetNextChunk(const void **chunkBuffer, size_t *chunkSize, media_header *mediaHeader) 91 { 92 uint8 retry_count = 0; 93 94 // printf("ChunkCache::GetNextChunk: %p fEmptyChunkCount %ld, fReadyChunkCount %ld\n", fNextGet, fEmptyChunkCount, fReadyChunkCount); 95 retry: 96 acquire_sem(fGetWaitSem); 97 98 fLocker->Lock(); 99 if (fReadyChunkCount == 0) { 100 fLocker->Unlock(); 101 printf("ChunkCache::GetNextChunk: %p retrying\n", fNextGet); 102 // Limit to 5 retries 103 retry_count++; 104 if (retry_count > 4) { 105 return B_ERROR; 106 } 107 goto retry; 108 } 109 fEmptyChunkCount++; 110 fReadyChunkCount--; 111 atomic_or(&fNeedsRefill, 1); 112 fLocker->Unlock(); 113 114 *chunkBuffer = fNextGet->buffer; 115 *chunkSize = fNextGet->sizeUsed; 116 *mediaHeader = fNextGet->mediaHeader; 117 status_t err = fNextGet->err; 118 fNextGet = fNextGet->next; 119 120 return err; 121 } 122 123 124 void 125 ChunkCache::PutNextChunk(const void *chunkBuffer, size_t chunkSize, const media_header &mediaHeader, status_t err) 126 { 127 // printf("ChunkCache::PutNextChunk: %p fEmptyChunkCount %ld, fReadyChunkCount %ld\n", fNextPut, fEmptyChunkCount, fReadyChunkCount); 128 129 if (err == B_OK) { 130 if (fNextPut->sizeMax < chunkSize) { 131 // printf("ChunkCache::PutNextChunk: %p resizing from %ld to %ld\n", fNextPut, fNextPut->sizeMax, chunkSize); 132 free(fNextPut->buffer); 133 fNextPut->buffer = malloc((chunkSize + 1024) & ~1023); 134 fNextPut->sizeMax = chunkSize; 135 } 136 memcpy(fNextPut->buffer, chunkBuffer, chunkSize); 137 fNextPut->sizeUsed = chunkSize; 138 } 139 140 fNextPut->mediaHeader = mediaHeader; 141 fNextPut->err = err; 142 143 fNextPut = fNextPut->next; 144 145 fLocker->Lock(); 146 fEmptyChunkCount--; 147 fReadyChunkCount++; 148 if (fEmptyChunkCount == 0) 149 atomic_and(&fNeedsRefill, 0); 150 fLocker->Unlock(); 151 152 release_sem(fGetWaitSem); 153 } 154