99 *
1010 *
1111 * IDENTIFICATION
12- * $PostgreSQL: pgsql/src/backend/storage/buffer/localbuf.c,v 1.64 2005/03/18 16:16:09 tgl Exp $
12+ * $PostgreSQL: pgsql/src/backend/storage/buffer/localbuf.c,v 1.65 2005/03/19 17:39:43 tgl Exp $
1313 *
1414 *-------------------------------------------------------------------------
1515 */
1818#include "storage/buf_internals.h"
1919#include "storage/bufmgr.h"
2020#include "storage/smgr.h"
21- #include "utils/relcache .h"
21+ #include "utils/memutils .h"
2222#include "utils/resowner.h"
2323
2424
2525/*#define LBDEBUG*/
2626
27+ /* entry for buffer lookup hashtable */
28+ typedef struct
29+ {
30+ BufferTag key ;/* Tag of a disk page */
31+ int id ;/* Associated local buffer's index */
32+ }LocalBufferLookupEnt ;
33+
2734/* Note: this macro only works on local buffers, not shared ones! */
2835#define LocalBufHdrGetBlock (bufHdr )\
2936LocalBufferBlockPointers[-((bufHdr)->buf_id + 2)]
3037
31- /* should be a GUC parameter some day */
32- int NLocBuffer = 64 ;
38+ int NLocBuffer = 0 ;/* until buffers are initialized */
3339
3440BufferDesc * LocalBufferDescriptors = NULL ;
3541Block * LocalBufferBlockPointers = NULL ;
3642int32 * LocalRefCount = NULL ;
3743
3844static int nextFreeLocalBuf = 0 ;
3945
46+ static HTAB * LocalBufHash = NULL ;
47+
4048
4149/*
4250 * LocalBufferAlloc -
43- *allocate a local buffer. We do round robin allocation for now .
51+ *Find or create a local buffer for the given page of the given relation .
4452 *
4553 * API is similar to bufmgr.c's BufferAlloc, except that we do not need
4654 * to do any locking since this is all local.Also, IO_IN_PROGRESS
@@ -50,35 +58,39 @@ BufferDesc *
5058LocalBufferAlloc (Relation reln ,BlockNumber blockNum ,bool * foundPtr )
5159{
5260BufferTag newTag ;/* identity of requested block */
53- int i ;
54- int trycounter ;
61+ LocalBufferLookupEnt * hresult ;
5562BufferDesc * bufHdr ;
63+ int b ;
64+ int trycounter ;
65+ bool found ;
5666
5767INIT_BUFFERTAG (newTag ,reln ,blockNum );
5868
59- /* a low tech search for now -- should use a hashtable */
60- for (i = 0 ;i < NLocBuffer ;i ++ )
69+ /* See if the desired buffer already exists */
70+ hresult = (LocalBufferLookupEnt * )
71+ hash_search (LocalBufHash , (void * )& newTag ,HASH_FIND ,NULL );
72+
73+ if (hresult )
6174{
62- bufHdr = & LocalBufferDescriptors [ i ] ;
63- if ( BUFFERTAGS_EQUAL ( bufHdr -> tag , newTag ))
64- {
75+ b = hresult -> id ;
76+ bufHdr = & LocalBufferDescriptors [ b ];
77+ Assert ( BUFFERTAGS_EQUAL ( bufHdr -> tag , newTag ));
6578#ifdef LBDEBUG
66- fprintf (stderr ,"LB ALLOC (%u,%d) %d\n" ,
67- RelationGetRelid (reln ),blockNum ,- i - 1 );
79+ fprintf (stderr ,"LB ALLOC (%u,%d) %d\n" ,
80+ RelationGetRelid (reln ),blockNum ,- b - 1 );
6881#endif
6982
70- LocalRefCount [i ]++ ;
71- ResourceOwnerRememberBuffer (CurrentResourceOwner ,
72- BufferDescriptorGetBuffer (bufHdr ));
73- if (bufHdr -> flags & BM_VALID )
74- * foundPtr = TRUE;
75- else
76- {
77- /* Previous read attempt must have failed; try again */
78- * foundPtr = FALSE;
79- }
80- return bufHdr ;
83+ LocalRefCount [b ]++ ;
84+ ResourceOwnerRememberBuffer (CurrentResourceOwner ,
85+ BufferDescriptorGetBuffer (bufHdr ));
86+ if (bufHdr -> flags & BM_VALID )
87+ * foundPtr = TRUE;
88+ else
89+ {
90+ /* Previous read attempt must have failed; try again */
91+ * foundPtr = FALSE;
8192}
93+ return bufHdr ;
8294}
8395
8496#ifdef LBDEBUG
@@ -93,7 +105,7 @@ LocalBufferAlloc(Relation reln, BlockNumber blockNum, bool *foundPtr)
93105trycounter = NLocBuffer ;
94106for (;;)
95107{
96- int b = nextFreeLocalBuf ;
108+ b = nextFreeLocalBuf ;
97109
98110if (++ nextFreeLocalBuf >=NLocBuffer )
99111nextFreeLocalBuf = 0 ;
@@ -136,31 +148,50 @@ LocalBufferAlloc(Relation reln, BlockNumber blockNum, bool *foundPtr)
136148 (char * )LocalBufHdrGetBlock (bufHdr ),
137149 true);
138150
151+ /* Mark not-dirty now in case we error out below */
152+ bufHdr -> flags &= ~BM_DIRTY ;
153+
139154LocalBufferFlushCount ++ ;
140155}
141156
142157/*
143158 * lazy memory allocation: allocate space on first use of a buffer.
144- *
145- * Note this path cannot be taken for a buffer that was previously in
146- * use, so it's okay to do it (and possibly error out) before marking
147- * the buffer as not dirty.
148159 */
149160if (LocalBufHdrGetBlock (bufHdr )== NULL )
150161{
151- char * data = ( char * ) malloc ( BLCKSZ ) ;
162+ char * data ;
152163
153- if (data == NULL )
154- ereport (ERROR ,
155- (errcode (ERRCODE_OUT_OF_MEMORY ),
156- errmsg ("out of memory" )));
164+ data = (char * )MemoryContextAlloc (TopMemoryContext ,BLCKSZ );
157165
158- /*
159- * Set pointer for use by BufferGetBlock() macro.
160- */
166+ /* Set pointer for use by BufferGetBlock() macro */
161167LocalBufHdrGetBlock (bufHdr )= (Block )data ;
162168}
163169
170+ /*
171+ * Update the hash table: remove old entry, if any, and make new one.
172+ */
173+ if (bufHdr -> flags & BM_TAG_VALID )
174+ {
175+ hresult = (LocalBufferLookupEnt * )
176+ hash_search (LocalBufHash , (void * )& bufHdr -> tag ,
177+ HASH_REMOVE ,NULL );
178+ if (!hresult )/* shouldn't happen */
179+ elog (ERROR ,"local buffer hash table corrupted" );
180+ /* mark buffer invalid just in case hash insert fails */
181+ CLEAR_BUFFERTAG (bufHdr -> tag );
182+ bufHdr -> flags &= ~(BM_VALID |BM_TAG_VALID );
183+ }
184+
185+ hresult = (LocalBufferLookupEnt * )
186+ hash_search (LocalBufHash , (void * )& newTag ,HASH_ENTER ,& found );
187+ if (!hresult )
188+ ereport (ERROR ,
189+ (errcode (ERRCODE_OUT_OF_MEMORY ),
190+ errmsg ("out of memory" )));
191+ if (found )/* shouldn't happen */
192+ elog (ERROR ,"local buffer hash table corrupted" );
193+ hresult -> id = b ;
194+
164195/*
165196 * it's all ours now.
166197 */
@@ -215,20 +246,39 @@ WriteLocalBuffer(Buffer buffer, bool release)
215246void
216247InitLocalBuffer (void )
217248{
249+ int nbufs = 64 ;/* should be from a GUC var */
250+ HASHCTL info ;
218251int i ;
219252
220- /*
221- * these aren't going away. I'm not gonna use palloc.
222- */
253+ /* Create the lookup hash table */
254+ MemSet (& info ,0 ,sizeof (info ));
255+ info .keysize = sizeof (BufferTag );
256+ info .entrysize = sizeof (LocalBufferLookupEnt );
257+ info .hash = tag_hash ;
258+
259+ LocalBufHash = hash_create ("Local Buffer Lookup Table" ,
260+ nbufs ,
261+ & info ,
262+ HASH_ELEM |HASH_FUNCTION );
263+
264+ if (!LocalBufHash )
265+ elog (ERROR ,"could not initialize local buffer hash table" );
266+
267+ /* Allocate and zero buffer headers and auxiliary arrays */
223268LocalBufferDescriptors = (BufferDesc * )
224- calloc (NLocBuffer ,sizeof (* LocalBufferDescriptors ));
269+ MemoryContextAllocZero (TopMemoryContext ,
270+ nbufs * sizeof (BufferDesc ));
225271LocalBufferBlockPointers = (Block * )
226- calloc (NLocBuffer ,sizeof (* LocalBufferBlockPointers ));
272+ MemoryContextAllocZero (TopMemoryContext ,
273+ nbufs * sizeof (Block ));
227274LocalRefCount = (int32 * )
228- calloc (NLocBuffer ,sizeof (* LocalRefCount ));
275+ MemoryContextAllocZero (TopMemoryContext ,
276+ nbufs * sizeof (int32 ));
277+
229278nextFreeLocalBuf = 0 ;
230279
231- for (i = 0 ;i < NLocBuffer ;i ++ )
280+ /* initialize fields that need to start off nonzero */
281+ for (i = 0 ;i < nbufs ;i ++ )
232282{
233283BufferDesc * buf = & LocalBufferDescriptors [i ];
234284
@@ -240,6 +290,9 @@ InitLocalBuffer(void)
240290 */
241291buf -> buf_id = - i - 2 ;
242292}
293+
294+ /* Initialization done, mark buffers allocated */
295+ NLocBuffer = nbufs ;
243296}
244297
245298/*
271324AtProcExit_LocalBuffers (void )
272325{
273326/* just zero the refcounts ... */
274- MemSet (LocalRefCount ,0 ,NLocBuffer * sizeof (* LocalRefCount ));
327+ if (LocalRefCount )
328+ MemSet (LocalRefCount ,0 ,NLocBuffer * sizeof (* LocalRefCount ));
275329}