|  | /* | 
|  | * Licensed to the Apache Software Foundation (ASF) under one | 
|  | * or more contributor license agreements.  See the NOTICE file | 
|  | * distributed with this work for additional information | 
|  | * regarding copyright ownership.  The ASF licenses this file | 
|  | * to you under the Apache License, Version 2.0 (the | 
|  | * "License"); you may not use this file except in compliance | 
|  | * with the License.  You may obtain a copy of the License at | 
|  | * | 
|  | *   http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, | 
|  | * software distributed under the License is distributed on an | 
|  | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | 
|  | * KIND, either express or implied.  See the License for the | 
|  | * specific language governing permissions and limitations | 
|  | * under the License. | 
|  | */ | 
|  |  | 
|  | /*------------------------------------------------------------------------- | 
|  | * | 
|  | * cdbchunkpool.c | 
|  | * | 
|  | * Allocates chunks of a fixed size from an underlying AsetDirectContext, | 
|  | * and keeps a freelist for recycling them. | 
|  | * | 
|  | *------------------------------------------------------------------------- | 
|  | */ | 
|  | #include "postgres.h" | 
|  |  | 
|  | #include "cdb/cdbchunkpool.h"               /* me */ | 
|  | #include "utils/memutils.h"                 /* AsetDirectContextCreate() etc */ | 
|  |  | 
|  |  | 
|  | #ifdef USE_ASSERT_CHECKING | 
|  | /* Fill a 32 or 64 bit pointer with 32 or 64 bits of 0xDEAD avoiding warnings */ | 
|  | #define DEADDEAD    ((void *)(Size)((((((0xDEAD << 16) + 0xDEAD) << 16) + 0xDEAD) << 16) + 0xDEAD)) | 
|  | #endif | 
|  |  | 
|  |  | 
|  | CdbChunkPool * | 
|  | CdbChunkPool_Create(Size            bytesperchunk, | 
|  | MemoryContext   parentcontext, | 
|  | const char     *chunkcontextname) | 
|  | { | 
|  | CdbChunkPool   *pool; | 
|  |  | 
|  | /* Allocate the ChunkPool object with empty 'freechunks' collection. */ | 
|  | pool = (CdbChunkPool *)CdbPtrBuf_CreateWrap(sizeof(*pool), | 
|  | offsetof(CdbChunkPool, freechunks), | 
|  | 10,   /* num_initial_cells */ | 
|  | 50,   /* num_cells_expand */ | 
|  | parentcontext); | 
|  |  | 
|  | /* Round 'bytesperchunk' up to MAXALIGN. */ | 
|  | pool->bytesperchunk = MAXALIGN(bytesperchunk); | 
|  |  | 
|  | /* | 
|  | * Create a private memory context from which to allocate chunks. | 
|  | * | 
|  | * A special AsetDirectContext is used to obtain each chunk directly | 
|  | * from the host system's allocator (malloc/mmap/etc) without wrapping | 
|  | * it in additional header/trailer bytes of our own. | 
|  | * | 
|  | * Chunks allocated from an AsetDirectContext must not be passed to | 
|  | * pfree() or repalloc().  Chunks are to be freed only by deleting or | 
|  | * resetting the context. | 
|  | */ | 
|  | pool->chunkcontext = AsetDirectContextCreate(parentcontext, chunkcontextname); | 
|  |  | 
|  | return pool; | 
|  | }                               /* CdbChunkPool_Create */ | 
|  |  | 
|  |  | 
|  | void | 
|  | CdbChunkPool_Destroy(CdbChunkPool *pool) | 
|  | { | 
|  | CdbPtrBuf_Reset(&pool->freechunks); | 
|  | if (pool->chunkcontext) | 
|  | MemoryContextDelete(pool->chunkcontext); | 
|  | pfree(pool); | 
|  | }                               /* CdbChunkPool_Destroy */ | 
|  |  | 
|  |  | 
|  | void * | 
|  | CdbChunkPool_Alloc(CdbChunkPool *pool) | 
|  | { | 
|  | void   *chunk; | 
|  |  | 
|  | #ifdef USE_ASSERT_CHECKING | 
|  | if (!CdbPtrBuf_IsOk(&pool->freechunks) || | 
|  | !MemoryContextIsValid(pool->chunkcontext)) | 
|  | elog(WARNING, "Chunk pool internal error in context '%s'", | 
|  | pool->chunkcontext->name); | 
|  | #endif | 
|  |  | 
|  | if (CdbPtrBuf_IsEmpty(&pool->freechunks)) | 
|  | { | 
|  | pool->nchunk++; | 
|  | chunk = MemoryContextAlloc(pool->chunkcontext, pool->bytesperchunk); | 
|  | } | 
|  | else | 
|  | { | 
|  | chunk = *CdbPtrBuf_PopCell(&pool->freechunks); | 
|  | #ifdef USE_ASSERT_CHECKING | 
|  | if (!chunk || | 
|  | ((void **)chunk)[0] != DEADDEAD || | 
|  | ((void **)chunk)[1] != (void *)pool || | 
|  | ((void **)((char *)chunk + pool->bytesperchunk))[-1] != DEADDEAD) | 
|  | elog(ERROR, "Chunk pool internal error in context '%s'", | 
|  | pool->chunkcontext->name); | 
|  | ((void **)chunk)[0] = 0; | 
|  | ((void **)chunk)[1] = 0; | 
|  | #endif | 
|  | } | 
|  |  | 
|  | return chunk; | 
|  | }                               /* CdbChunkPool_Alloc */ | 
|  |  | 
|  |  | 
|  | void | 
|  | CdbChunkPool_Free(CdbChunkPool *pool, void *chunk) | 
|  | { | 
|  | #ifdef USE_ASSERT_CHECKING | 
|  | if (!CdbPtrBuf_IsOk(&pool->freechunks) || | 
|  | !MemoryContextIsValid(pool->chunkcontext)) | 
|  | elog(WARNING, "Chunk pool internal error in context '%s'", | 
|  | pool->chunkcontext->name); | 
|  | if (!chunk) | 
|  | elog(WARNING, "CdbChunkPool_Free(pool, NULL) in context '%s'", | 
|  | pool->chunkcontext->name); | 
|  | if (pool->nchunk <= CdbPtrBuf_Length(&pool->freechunks)) | 
|  | elog(WARNING, "CdbChunkPool_Free to wrong pool in context '%s'", | 
|  | pool->chunkcontext->name); | 
|  | if (((void **)chunk)[0] == DEADDEAD) | 
|  | elog(WARNING, "Possible double CdbChunkPool_Free in context '%s'", | 
|  | pool->chunkcontext->name); | 
|  | ((void **)chunk)[0] = DEADDEAD; | 
|  | ((void **)chunk)[1] = pool; | 
|  | ((void **)((char *)chunk + pool->bytesperchunk))[-1] = DEADDEAD; | 
|  | #endif | 
|  | CdbPtrBuf_Append(&pool->freechunks, chunk); | 
|  | }                               /* CdbChunkPool_Free */ | 
|  |  | 
|  |  | 
|  | Size | 
|  | CdbChunkPool_GetCurrentSpace(CdbChunkPool *pool) | 
|  | { | 
|  | return (pool->nchunk - CdbPtrBuf_Length(&pool->freechunks)) | 
|  | * pool->bytesperchunk; | 
|  | }                               /* CdbChunkPool_GetCurrentSpace */ | 
|  |  | 
|  |  |