bufmgr.c
上传用户:blenddy
上传日期:2007-01-07
资源大小:6495k
文件大小:50k
源码类别:

数据库系统

开发平台:

Unix_Linux

  1. /*-------------------------------------------------------------------------
  2.  *
  3.  * bufmgr.c
  4.  *   buffer manager interface routines
  5.  *
  6.  * Copyright (c) 1994, Regents of the University of California
  7.  *
  8.  *
  9.  * IDENTIFICATION
  10.  *   $Header: /usr/local/cvsroot/pgsql/src/backend/storage/buffer/bufmgr.c,v 1.56 1999/06/29 04:54:47 vadim Exp $
  11.  *
  12.  *-------------------------------------------------------------------------
  13.  */
  14. /*
  15.  *
  16.  * BufferAlloc() -- lookup a buffer in the buffer table.  If
  17.  * it isn't there add it, but do not read it into memory.
  18.  * This is used when we are about to reinitialize the
  19.  * buffer so don't care what the current disk contents are.
  20.  * BufferAlloc() pins the new buffer in memory.
  21.  *
  22.  * ReadBuffer() -- same as BufferAlloc() but reads the data
  23.  * on a buffer cache miss.
  24.  *
  25.  * ReleaseBuffer() -- unpin the buffer
  26.  *
  27.  * WriteNoReleaseBuffer() -- mark the buffer contents as "dirty"
  28.  * but don't unpin.  The disk IO is delayed until buffer
  29.  * replacement if WriteMode is BUFFER_LATE_WRITE.
  30.  *
  31.  * WriteBuffer() -- WriteNoReleaseBuffer() + ReleaseBuffer()
  32.  *
  33.  * FlushBuffer() -- as above but never delayed write.
  34.  *
  35.  * BufferSync() -- flush all dirty buffers in the buffer pool.
  36.  *
  37.  * InitBufferPool() -- Init the buffer module.
  38.  *
  39.  * See other files:
  40.  * freelist.c -- chooses victim for buffer replacement
  41.  * buf_table.c -- manages the buffer lookup table
  42.  */
  43. #include <sys/types.h>
  44. #include <sys/file.h>
  45. #include <stdio.h>
  46. #include <string.h>
  47. #include <math.h>
  48. #include <signal.h>
  49. #include "postgres.h"
  50. /* declarations split between these three files */
  51. #include "storage/buf.h"
  52. #include "storage/buf_internals.h"
  53. #include "storage/bufmgr.h"
  54. #include "storage/fd.h"
  55. #include "storage/ipc.h"
  56. #include "storage/s_lock.h"
  57. #include "storage/shmem.h"
  58. #include "storage/spin.h"
  59. #include "storage/smgr.h"
  60. #include "storage/lmgr.h"
  61. #include "miscadmin.h"
  62. #include "utils/builtins.h"
  63. #include "utils/hsearch.h"
  64. #include "utils/palloc.h"
  65. #include "utils/memutils.h"
  66. #include "utils/relcache.h"
  67. #include "executor/execdebug.h" /* for NDirectFileRead */
  68. #include "catalog/catalog.h"
  69. extern SPINLOCK BufMgrLock;
  70. extern long int ReadBufferCount;
  71. extern long int ReadLocalBufferCount;
  72. extern long int BufferHitCount;
  73. extern long int LocalBufferHitCount;
  74. extern long int BufferFlushCount;
  75. extern long int LocalBufferFlushCount;
  76. /*
  77.  * It's used to avoid disk writes for read-only transactions
  78.  * (i.e. when no one shared buffer was changed by transaction).
  79.  * We set it to true in WriteBuffer/WriteNoReleaseBuffer when
  80.  * marking shared buffer as dirty. We set it to false in xact.c
  81.  * after transaction is committed/aborted.
  82.  */
  83. bool SharedBufferChanged = false;
  84. static int WriteMode = BUFFER_LATE_WRITE; /* Delayed write is
  85.  * default */
  86. static void WaitIO(BufferDesc *buf, SPINLOCK spinlock);
  87. #ifndef HAS_TEST_AND_SET
  88. static void SignalIO(BufferDesc *buf);
  89. extern long *NWaitIOBackendP; /* defined in buf_init.c */
  90. #endif  /* HAS_TEST_AND_SET */
  91. static Buffer ReadBufferWithBufferLock(Relation relation, BlockNumber blockNum,
  92.  bool bufferLockHeld);
  93. static BufferDesc *BufferAlloc(Relation reln, BlockNumber blockNum,
  94. bool *foundPtr, bool bufferLockHeld);
  95. static int FlushBuffer(Buffer buffer, bool release);
  96. static void BufferSync(void);
  97. static int BufferReplace(BufferDesc *bufHdr, bool bufferLockHeld);
  98. void PrintBufferDescs(void);
  99. /* not static but used by vacuum only ... */
  100. int BlowawayRelationBuffers(Relation rel, BlockNumber block);
  101. /* ---------------------------------------------------
  102.  * RelationGetBufferWithBuffer
  103.  * see if the given buffer is what we want
  104.  * if yes, we don't need to bother the buffer manager
  105.  * ---------------------------------------------------
  106.  */
  107. Buffer
  108. RelationGetBufferWithBuffer(Relation relation,
  109. BlockNumber blockNumber,
  110. Buffer buffer)
  111. {
  112. BufferDesc *bufHdr;
  113. if (BufferIsValid(buffer))
  114. {
  115. if (!BufferIsLocal(buffer))
  116. {
  117. LockRelId  *lrelId = &(((LockInfo) (relation->lockInfo))->lockRelId);
  118. bufHdr = &BufferDescriptors[buffer - 1];
  119. SpinAcquire(BufMgrLock);
  120. if (bufHdr->tag.blockNum == blockNumber &&
  121. bufHdr->tag.relId.relId == lrelId->relId &&
  122. bufHdr->tag.relId.dbId == lrelId->dbId)
  123. {
  124. SpinRelease(BufMgrLock);
  125. return buffer;
  126. }
  127. return ReadBufferWithBufferLock(relation, blockNumber, true);
  128. }
  129. else
  130. {
  131. bufHdr = &LocalBufferDescriptors[-buffer - 1];
  132. if (bufHdr->tag.relId.relId == RelationGetRelid(relation) &&
  133. bufHdr->tag.blockNum == blockNumber)
  134. return buffer;
  135. }
  136. }
  137. return ReadBuffer(relation, blockNumber);
  138. }
  139. /*
  140.  * ReadBuffer -- returns a buffer containing the requested
  141.  * block of the requested relation.  If the blknum
  142.  * requested is P_NEW, extend the relation file and
  143.  * allocate a new block.
  144.  *
  145.  * Returns: the buffer number for the buffer containing
  146.  * the block read or NULL on an error.
  147.  *
  148.  * Assume when this function is called, that reln has been
  149.  * opened already.
  150.  */
  151. extern int ShowPinTrace;
  152. #undef ReadBuffer /* conflicts with macro when BUFMGR_DEBUG
  153.  * defined */
  154. /*
  155.  * ReadBuffer
  156.  *
  157.  */
  158. Buffer
  159. ReadBuffer(Relation reln, BlockNumber blockNum)
  160. {
  161. return ReadBufferWithBufferLock(reln, blockNum, false);
  162. }
  163. /*
  164.  * is_userbuffer
  165.  *
  166.  * XXX caller must have already acquired BufMgrLock
  167.  */
  168. #ifdef NOT_USED
  169. static bool
  170. is_userbuffer(Buffer buffer)
  171. {
  172. BufferDesc *buf = &BufferDescriptors[buffer - 1];
  173. if (IsSystemRelationName(buf->sb_relname))
  174. return false;
  175. return true;
  176. }
  177. #endif
  178. #ifdef NOT_USED
  179. Buffer
  180. ReadBuffer_Debug(char *file,
  181.  int line,
  182.  Relation reln,
  183.  BlockNumber blockNum)
  184. {
  185. Buffer buffer;
  186. buffer = ReadBufferWithBufferLock(reln, blockNum, false);
  187. if (ShowPinTrace && !BufferIsLocal(buffer) && is_userbuffer(buffer))
  188. {
  189. BufferDesc *buf = &BufferDescriptors[buffer - 1];
  190. fprintf(stderr, "PIN(RD) %ld relname = %s, blockNum = %d, 
  191. refcount = %ld, file: %s, line: %dn",
  192. buffer, buf->sb_relname, buf->tag.blockNum,
  193. PrivateRefCount[buffer - 1], file, line);
  194. }
  195. return buffer;
  196. }
  197. #endif
  198. /*
  199.  * ReadBufferWithBufferLock -- does the work of
  200.  * ReadBuffer() but with the possibility that
  201.  * the buffer lock has already been held. this
  202.  * is yet another effort to reduce the number of
  203.  * semops in the system.
  204.  */
  205. static Buffer
  206. ReadBufferWithBufferLock(Relation reln,
  207.  BlockNumber blockNum,
  208.  bool bufferLockHeld)
  209. {
  210. BufferDesc *bufHdr;
  211. int extend; /* extending the file by one block */
  212. int status;
  213. bool found;
  214. bool isLocalBuf;
  215. extend = (blockNum == P_NEW);
  216. isLocalBuf = reln->rd_myxactonly;
  217. if (isLocalBuf)
  218. {
  219. ReadLocalBufferCount++;
  220. bufHdr = LocalBufferAlloc(reln, blockNum, &found);
  221. if (found)
  222. LocalBufferHitCount++;
  223. }
  224. else
  225. {
  226. ReadBufferCount++;
  227. /*
  228.  * lookup the buffer.  IO_IN_PROGRESS is set if the requested
  229.  * block is not currently in memory.
  230.  */
  231. bufHdr = BufferAlloc(reln, blockNum, &found, bufferLockHeld);
  232. if (found)
  233. BufferHitCount++;
  234. }
  235. if (!bufHdr)
  236. return InvalidBuffer;
  237. /* if its already in the buffer pool, we're done */
  238. if (found)
  239. {
  240. /*
  241.  * This happens when a bogus buffer was returned previously and is
  242.  * floating around in the buffer pool. A routine calling this
  243.  * would want this extended.
  244.  */
  245. if (extend)
  246. {
  247. /* new buffers are zero-filled */
  248. MemSet((char *) MAKE_PTR(bufHdr->data), 0, BLCKSZ);
  249. smgrextend(DEFAULT_SMGR, reln,
  250.    (char *) MAKE_PTR(bufHdr->data));
  251. }
  252. return BufferDescriptorGetBuffer(bufHdr);
  253. }
  254. /*
  255.  * if we have gotten to this point, the reln pointer must be ok and
  256.  * the relation file must be open.
  257.  */
  258. if (extend)
  259. {
  260. /* new buffers are zero-filled */
  261. MemSet((char *) MAKE_PTR(bufHdr->data), 0, BLCKSZ);
  262. status = smgrextend(DEFAULT_SMGR, reln,
  263. (char *) MAKE_PTR(bufHdr->data));
  264. }
  265. else
  266. {
  267. status = smgrread(DEFAULT_SMGR, reln, blockNum,
  268.   (char *) MAKE_PTR(bufHdr->data));
  269. }
  270. if (isLocalBuf)
  271. return BufferDescriptorGetBuffer(bufHdr);
  272. /* lock buffer manager again to update IO IN PROGRESS */
  273. SpinAcquire(BufMgrLock);
  274. if (status == SM_FAIL)
  275. {
  276. /* IO Failed.  cleanup the data structures and go home */
  277. if (!BufTableDelete(bufHdr))
  278. {
  279. SpinRelease(BufMgrLock);
  280. elog(FATAL, "BufRead: buffer table broken after IO errorn");
  281. }
  282. /* remember that BufferAlloc() pinned the buffer */
  283. UnpinBuffer(bufHdr);
  284. /*
  285.  * Have to reset the flag so that anyone waiting for the buffer
  286.  * can tell that the contents are invalid.
  287.  */
  288. bufHdr->flags |= BM_IO_ERROR;
  289. bufHdr->flags &= ~BM_IO_IN_PROGRESS;
  290. }
  291. else
  292. {
  293. /* IO Succeeded.  clear the flags, finish buffer update */
  294. bufHdr->flags &= ~(BM_IO_ERROR | BM_IO_IN_PROGRESS);
  295. }
  296. /* If anyone was waiting for IO to complete, wake them up now */
  297. #ifdef HAS_TEST_AND_SET
  298. S_UNLOCK(&(bufHdr->io_in_progress_lock));
  299. #else
  300. if (bufHdr->refcount > 1)
  301. SignalIO(bufHdr);
  302. #endif
  303. SpinRelease(BufMgrLock);
  304. if (status == SM_FAIL)
  305. return InvalidBuffer;
  306. return BufferDescriptorGetBuffer(bufHdr);
  307. }
  308. /*
  309.  * BufferAlloc -- Get a buffer from the buffer pool but dont
  310.  * read it.
  311.  *
  312.  * Returns: descriptor for buffer
  313.  *
  314.  * When this routine returns, the BufMgrLock is guaranteed NOT be held.
  315.  */
  316. static BufferDesc *
  317. BufferAlloc(Relation reln,
  318. BlockNumber blockNum,
  319. bool *foundPtr,
  320. bool bufferLockHeld)
  321. {
  322. BufferDesc *buf,
  323.    *buf2;
  324. BufferTag newTag; /* identity of requested block */
  325. bool inProgress; /* buffer undergoing IO */
  326. bool newblock = FALSE;
  327. /* create a new tag so we can lookup the buffer */
  328. /* assume that the relation is already open */
  329. if (blockNum == P_NEW)
  330. {
  331. newblock = TRUE;
  332. blockNum = smgrnblocks(DEFAULT_SMGR, reln);
  333. }
  334. INIT_BUFFERTAG(&newTag, reln, blockNum);
  335. if (!bufferLockHeld)
  336. SpinAcquire(BufMgrLock);
  337. /* see if the block is in the buffer pool already */
  338. buf = BufTableLookup(&newTag);
  339. if (buf != NULL)
  340. {
  341. /*
  342.  * Found it.  Now, (a) pin the buffer so no one steals it from the
  343.  * buffer pool, (b) check IO_IN_PROGRESS, someone may be faulting
  344.  * the buffer into the buffer pool.
  345.  */
  346. PinBuffer(buf);
  347. inProgress = (buf->flags & BM_IO_IN_PROGRESS);
  348. *foundPtr = TRUE;
  349. if (inProgress)
  350. {
  351. WaitIO(buf, BufMgrLock);
  352. if (buf->flags & BM_IO_ERROR)
  353. {
  354. /*
  355.  * wierd race condition:
  356.  *
  357.  * We were waiting for someone else to read the buffer. While
  358.  * we were waiting, the reader boof'd in some way, so the
  359.  * contents of the buffer are still invalid.  By saying
  360.  * that we didn't find it, we can make the caller
  361.  * reinitialize the buffer.  If two processes are waiting
  362.  * for this block, both will read the block.  The second
  363.  * one to finish may overwrite any updates made by the
  364.  * first.  (Assume higher level synchronization prevents
  365.  * this from happening).
  366.  *
  367.  * This is never going to happen, don't worry about it.
  368.  */
  369. *foundPtr = FALSE;
  370. }
  371. }
  372. #ifdef BMTRACE
  373. _bm_trace((reln->rd_rel->relisshared ? 0 : MyDatabaseId), RelationGetRelid(reln), blockNum, BufferDescriptorGetBuffer(buf), BMT_ALLOCFND);
  374. #endif  /* BMTRACE */
  375. SpinRelease(BufMgrLock);
  376. return buf;
  377. }
  378. *foundPtr = FALSE;
  379. /*
  380.  * Didn't find it in the buffer pool.  We'll have to initialize a new
  381.  * buffer. First, grab one from the free list.  If it's dirty, flush
  382.  * it to disk. Remember to unlock BufMgr spinlock while doing the IOs.
  383.  */
  384. inProgress = FALSE;
  385. for (buf = (BufferDesc *) NULL; buf == (BufferDesc *) NULL;)
  386. {
  387. /* GetFreeBuffer will abort if it can't find a free buffer */
  388. buf = GetFreeBuffer();
  389. /*
  390.  * But it can return buf == NULL if we are in aborting transaction
  391.  * now and so elog(ERROR,...) in GetFreeBuffer will not abort
  392.  * again.
  393.  */
  394. if (buf == NULL)
  395. return NULL;
  396. /*
  397.  * There should be exactly one pin on the buffer after it is
  398.  * allocated -- ours.  If it had a pin it wouldn't have been on
  399.  * the free list.  No one else could have pinned it between
  400.  * GetFreeBuffer and here because we have the BufMgrLock.
  401.  */
  402. Assert(buf->refcount == 0);
  403. buf->refcount = 1;
  404. PrivateRefCount[BufferDescriptorGetBuffer(buf) - 1] = 1;
  405. if (buf->flags & BM_DIRTY)
  406. {
  407. bool smok;
  408. /*
  409.  * Set BM_IO_IN_PROGRESS to keep anyone from doing anything
  410.  * with the contents of the buffer while we write it out. We
  411.  * don't really care if they try to read it, but if they can
  412.  * complete a BufferAlloc on it they can then scribble into
  413.  * it, and we'd really like to avoid that while we are
  414.  * flushing the buffer.  Setting this flag should block them
  415.  * in WaitIO until we're done.
  416.  */
  417. inProgress = TRUE;
  418. buf->flags |= BM_IO_IN_PROGRESS;
  419. #ifdef HAS_TEST_AND_SET
  420. /*
  421.  * All code paths that acquire this lock pin the buffer first;
  422.  * since no one had it pinned (it just came off the free
  423.  * list), no one else can have this lock.
  424.  */
  425. Assert(S_LOCK_FREE(&(buf->io_in_progress_lock)));
  426. S_LOCK(&(buf->io_in_progress_lock));
  427. #endif  /* HAS_TEST_AND_SET */
  428. /*
  429.  * Write the buffer out, being careful to release BufMgrLock
  430.  * before starting the I/O.
  431.  *
  432.  * This #ifndef is here because a few extra semops REALLY kill
  433.  * you on machines that don't have spinlocks.  If you don't
  434.  * operate with much concurrency, well...
  435.  */
  436. smok = BufferReplace(buf, true);
  437. #ifndef OPTIMIZE_SINGLE
  438. SpinAcquire(BufMgrLock);
  439. #endif  /* OPTIMIZE_SINGLE */
  440. if (smok == FALSE)
  441. {
  442. elog(NOTICE, "BufferAlloc: cannot write block %u for %s/%s",
  443.  buf->tag.blockNum, buf->sb_dbname, buf->sb_relname);
  444. inProgress = FALSE;
  445. buf->flags |= BM_IO_ERROR;
  446. buf->flags &= ~BM_IO_IN_PROGRESS;
  447. #ifdef HAS_TEST_AND_SET
  448. S_UNLOCK(&(buf->io_in_progress_lock));
  449. #else /* !HAS_TEST_AND_SET */
  450. if (buf->refcount > 1)
  451. SignalIO(buf);
  452. #endif  /* !HAS_TEST_AND_SET */
  453. PrivateRefCount[BufferDescriptorGetBuffer(buf) - 1] = 0;
  454. buf->refcount--;
  455. if (buf->refcount == 0)
  456. {
  457. AddBufferToFreelist(buf);
  458. buf->flags |= BM_FREE;
  459. }
  460. buf = (BufferDesc *) NULL;
  461. }
  462. else
  463. {
  464. /*
  465.  * BM_JUST_DIRTIED cleared by BufferReplace and shouldn't
  466.  * be setted by anyone. - vadim 01/17/97
  467.  */
  468. if (buf->flags & BM_JUST_DIRTIED)
  469. {
  470. elog(FATAL, "BufferAlloc: content of block %u (%s) changed while flushing",
  471.  buf->tag.blockNum, buf->sb_relname);
  472. }
  473. else
  474. buf->flags &= ~BM_DIRTY;
  475. }
  476. /*
  477.  * Somebody could have pinned the buffer while we were doing
  478.  * the I/O and had given up the BufMgrLock (though they would
  479.  * be waiting for us to clear the BM_IO_IN_PROGRESS flag).
  480.  * That's why this is a loop -- if so, we need to clear the
  481.  * I/O flags, remove our pin and start all over again.
  482.  *
  483.  * People may be making buffers free at any time, so there's no
  484.  * reason to think that we have an immediate disaster on our
  485.  * hands.
  486.  */
  487. if (buf && buf->refcount > 1)
  488. {
  489. inProgress = FALSE;
  490. buf->flags &= ~BM_IO_IN_PROGRESS;
  491. #ifdef HAS_TEST_AND_SET
  492. S_UNLOCK(&(buf->io_in_progress_lock));
  493. #else /* !HAS_TEST_AND_SET */
  494. if (buf->refcount > 1)
  495. SignalIO(buf);
  496. #endif  /* !HAS_TEST_AND_SET */
  497. PrivateRefCount[BufferDescriptorGetBuffer(buf) - 1] = 0;
  498. buf->refcount--;
  499. buf = (BufferDesc *) NULL;
  500. }
  501. /*
  502.  * Somebody could have allocated another buffer for the same
  503.  * block we are about to read in. (While we flush out the
  504.  * dirty buffer, we don't hold the lock and someone could have
  505.  * allocated another buffer for the same block. The problem is
  506.  * we haven't gotten around to insert the new tag into the
  507.  * buffer table. So we need to check here. -ay 3/95
  508.  */
  509. buf2 = BufTableLookup(&newTag);
  510. if (buf2 != NULL)
  511. {
  512. /*
  513.  * Found it. Someone has already done what we're about to
  514.  * do. We'll just handle this as if it were found in the
  515.  * buffer pool in the first place.
  516.  */
  517. if (buf != NULL)
  518. {
  519. #ifdef HAS_TEST_AND_SET
  520. S_UNLOCK(&(buf->io_in_progress_lock));
  521. #else /* !HAS_TEST_AND_SET */
  522. if (buf->refcount > 1)
  523. SignalIO(buf);
  524. #endif  /* !HAS_TEST_AND_SET */
  525. /* give up the buffer since we don't need it any more */
  526. buf->refcount--;
  527. PrivateRefCount[BufferDescriptorGetBuffer(buf) - 1] = 0;
  528. AddBufferToFreelist(buf);
  529. buf->flags |= BM_FREE;
  530. buf->flags &= ~BM_IO_IN_PROGRESS;
  531. }
  532. PinBuffer(buf2);
  533. inProgress = (buf2->flags & BM_IO_IN_PROGRESS);
  534. *foundPtr = TRUE;
  535. if (inProgress)
  536. {
  537. WaitIO(buf2, BufMgrLock);
  538. if (buf2->flags & BM_IO_ERROR)
  539. *foundPtr = FALSE;
  540. }
  541. SpinRelease(BufMgrLock);
  542. return buf2;
  543. }
  544. }
  545. }
  546. /*
  547.  * At this point we should have the sole pin on a non-dirty buffer and
  548.  * we may or may not already have the BM_IO_IN_PROGRESS flag set.
  549.  */
  550. /*
  551.  * Change the name of the buffer in the lookup table:
  552.  *
  553.  * Need to update the lookup table before the read starts. If someone
  554.  * comes along looking for the buffer while we are reading it in, we
  555.  * don't want them to allocate a new buffer.  For the same reason, we
  556.  * didn't want to erase the buf table entry for the buffer we were
  557.  * writing back until now, either.
  558.  */
  559. if (!BufTableDelete(buf))
  560. {
  561. SpinRelease(BufMgrLock);
  562. elog(FATAL, "buffer wasn't in the buffer tablen");
  563. }
  564. /* record the database name and relation name for this buffer */
  565. strcpy(buf->sb_relname, reln->rd_rel->relname.data);
  566. strcpy(buf->sb_dbname, DatabaseName);
  567. INIT_BUFFERTAG(&(buf->tag), reln, blockNum);
  568. if (!BufTableInsert(buf))
  569. {
  570. SpinRelease(BufMgrLock);
  571. elog(FATAL, "Buffer in lookup table twice n");
  572. }
  573. /*
  574.  * Buffer contents are currently invalid.  Have to mark IO IN PROGRESS
  575.  * so no one fiddles with them until the read completes.  If this
  576.  * routine has been called simply to allocate a buffer, no io will be
  577.  * attempted, so the flag isnt set.
  578.  */
  579. if (!inProgress)
  580. {
  581. buf->flags |= BM_IO_IN_PROGRESS;
  582. #ifdef HAS_TEST_AND_SET
  583. Assert(S_LOCK_FREE(&(buf->io_in_progress_lock)));
  584. S_LOCK(&(buf->io_in_progress_lock));
  585. #endif  /* HAS_TEST_AND_SET */
  586. }
  587. #ifdef BMTRACE
  588. _bm_trace((reln->rd_rel->relisshared ? 0 : MyDatabaseId), RelationGetRelid(reln), blockNum, BufferDescriptorGetBuffer(buf), BMT_ALLOCNOTFND);
  589. #endif  /* BMTRACE */
  590. SpinRelease(BufMgrLock);
  591. return buf;
  592. }
  593. /*
  594.  * WriteBuffer
  595.  *
  596.  * Pushes buffer contents to disk if WriteMode is BUFFER_FLUSH_WRITE.
  597.  * Otherwise, marks contents as dirty.
  598.  *
  599.  * Assume that buffer is pinned.  Assume that reln is
  600.  * valid.
  601.  *
  602.  * Side Effects:
  603.  * Pin count is decremented.
  604.  */
  605. #undef WriteBuffer
  606. int
  607. WriteBuffer(Buffer buffer)
  608. {
  609. BufferDesc *bufHdr;
  610. if (WriteMode == BUFFER_FLUSH_WRITE)
  611. return FlushBuffer(buffer, TRUE);
  612. else
  613. {
  614. if (BufferIsLocal(buffer))
  615. return WriteLocalBuffer(buffer, TRUE);
  616. if (BAD_BUFFER_ID(buffer))
  617. return FALSE;
  618. bufHdr = &BufferDescriptors[buffer - 1];
  619. SharedBufferChanged = true;
  620. SpinAcquire(BufMgrLock);
  621. Assert(bufHdr->refcount > 0);
  622. bufHdr->flags |= (BM_DIRTY | BM_JUST_DIRTIED);
  623. UnpinBuffer(bufHdr);
  624. SpinRelease(BufMgrLock);
  625. CommitInfoNeedsSave[buffer - 1] = 0;
  626. }
  627. return TRUE;
  628. }
  629. #ifdef NOT_USED
  630. void
  631. WriteBuffer_Debug(char *file, int line, Buffer buffer)
  632. {
  633. WriteBuffer(buffer);
  634. if (ShowPinTrace && BufferIsLocal(buffer) && is_userbuffer(buffer))
  635. {
  636. BufferDesc *buf;
  637. buf = &BufferDescriptors[buffer - 1];
  638. fprintf(stderr, "UNPIN(WR) %ld relname = %s, blockNum = %d, 
  639. refcount = %ld, file: %s, line: %dn",
  640. buffer, buf->sb_relname, buf->tag.blockNum,
  641. PrivateRefCount[buffer - 1], file, line);
  642. }
  643. }
  644. #endif
  645. /*
  646.  * DirtyBufferCopy() -- For a given dbid/relid/blockno, if the buffer is
  647.  * in the cache and is dirty, mark it clean and copy
  648.  * it to the requested location.  This is a logical
  649.  * write, and has been installed to support the cache
  650.  * management code for write-once storage managers.
  651.  *
  652.  * DirtyBufferCopy() -- Copy a given dirty buffer to the requested
  653.  *  destination.
  654.  *
  655.  * We treat this as a write.  If the requested buffer is in the pool
  656.  * and is dirty, we copy it to the location requested and mark it
  657.  * clean. This routine supports the Sony jukebox storage manager,
  658.  * which agrees to take responsibility for the data once we mark
  659.  * it clean.
  660.  *
  661.  * NOTE: used by sony jukebox code in postgres 4.2   - ay 2/95
  662.  */
  663. #ifdef NOT_USED
  664. void
  665. DirtyBufferCopy(Oid dbid, Oid relid, BlockNumber blkno, char *dest)
  666. {
  667. BufferDesc *buf;
  668. BufferTag btag;
  669. btag.relId.relId = relid;
  670. btag.relId.dbId = dbid;
  671. btag.blockNum = blkno;
  672. SpinAcquire(BufMgrLock);
  673. buf = BufTableLookup(&btag);
  674. if (buf == (BufferDesc *) NULL
  675. || !(buf->flags & BM_DIRTY)
  676. || !(buf->flags & BM_VALID))
  677. {
  678. SpinRelease(BufMgrLock);
  679. return;
  680. }
  681. /*
  682.  * hate to do this holding the lock, but release and reacquire is
  683.  * slower
  684.  */
  685. memmove(dest, (char *) MAKE_PTR(buf->data), BLCKSZ);
  686. buf->flags &= ~BM_DIRTY;
  687. SpinRelease(BufMgrLock);
  688. }
  689. #endif
  690. /*
  691.  * FlushBuffer -- like WriteBuffer, but force the page to disk.
  692.  *
  693.  * 'buffer' is known to be dirty/pinned, so there should not be a
  694.  * problem reading the BufferDesc members without the BufMgrLock
  695.  * (nobody should be able to change tags, flags, etc. out from under
  696.  * us).
  697.  */
  698. static int
  699. FlushBuffer(Buffer buffer, bool release)
  700. {
  701. BufferDesc *bufHdr;
  702. Oid bufdb;
  703. Relation bufrel;
  704. int status;
  705. if (BufferIsLocal(buffer))
  706. return FlushLocalBuffer(buffer, release);
  707. if (BAD_BUFFER_ID(buffer))
  708. return STATUS_ERROR;
  709. bufHdr = &BufferDescriptors[buffer - 1];
  710. bufdb = bufHdr->tag.relId.dbId;
  711. Assert(bufdb == MyDatabaseId || bufdb == (Oid) NULL);
  712. bufrel = RelationIdCacheGetRelation(bufHdr->tag.relId.relId);
  713. Assert(bufrel != (Relation) NULL);
  714. SharedBufferChanged = true;
  715. /* To check if block content changed while flushing. - vadim 01/17/97 */
  716. SpinAcquire(BufMgrLock);
  717. bufHdr->flags &= ~BM_JUST_DIRTIED;
  718. SpinRelease(BufMgrLock);
  719. status = smgrflush(DEFAULT_SMGR, bufrel, bufHdr->tag.blockNum,
  720.    (char *) MAKE_PTR(bufHdr->data));
  721. RelationDecrementReferenceCount(bufrel);
  722. if (status == SM_FAIL)
  723. {
  724. elog(ERROR, "FlushBuffer: cannot flush block %u of the relation %s",
  725.  bufHdr->tag.blockNum, bufHdr->sb_relname);
  726. return STATUS_ERROR;
  727. }
  728. BufferFlushCount++;
  729. SpinAcquire(BufMgrLock);
  730. /*
  731.  * If this buffer was marked by someone as DIRTY while we were
  732.  * flushing it out we must not clear DIRTY flag - vadim 01/17/97
  733.  */
  734. if (bufHdr->flags & BM_JUST_DIRTIED)
  735. {
  736. elog(NOTICE, "FlusfBuffer: content of block %u (%s) changed while flushing",
  737.  bufHdr->tag.blockNum, bufHdr->sb_relname);
  738. }
  739. else
  740. bufHdr->flags &= ~BM_DIRTY;
  741. if (release)
  742. UnpinBuffer(bufHdr);
  743. SpinRelease(BufMgrLock);
  744. CommitInfoNeedsSave[buffer - 1] = 0;
  745. return STATUS_OK;
  746. }
  747. /*
  748.  * WriteNoReleaseBuffer -- like WriteBuffer, but do not unpin the buffer
  749.  *    when the operation is complete.
  750.  *
  751.  * We know that the buffer is for a relation in our private cache,
  752.  * because this routine is called only to write out buffers that
  753.  * were changed by the executing backend.
  754.  */
  755. int
  756. WriteNoReleaseBuffer(Buffer buffer)
  757. {
  758. BufferDesc *bufHdr;
  759. if (WriteMode == BUFFER_FLUSH_WRITE)
  760. return FlushBuffer(buffer, FALSE);
  761. else
  762. {
  763. if (BufferIsLocal(buffer))
  764. return WriteLocalBuffer(buffer, FALSE);
  765. if (BAD_BUFFER_ID(buffer))
  766. return STATUS_ERROR;
  767. bufHdr = &BufferDescriptors[buffer - 1];
  768. SharedBufferChanged = true;
  769. SpinAcquire(BufMgrLock);
  770. bufHdr->flags |= (BM_DIRTY | BM_JUST_DIRTIED);
  771. SpinRelease(BufMgrLock);
  772. CommitInfoNeedsSave[buffer - 1] = 0;
  773. }
  774. return STATUS_OK;
  775. }
  776. #undef ReleaseAndReadBuffer
  777. /*
  778.  * ReleaseAndReadBuffer -- combine ReleaseBuffer() and ReadBuffer()
  779.  * so that only one semop needs to be called.
  780.  *
  781.  */
  782. Buffer
  783. ReleaseAndReadBuffer(Buffer buffer,
  784.  Relation relation,
  785.  BlockNumber blockNum)
  786. {
  787. BufferDesc *bufHdr;
  788. Buffer retbuf;
  789. if (BufferIsLocal(buffer))
  790. {
  791. Assert(LocalRefCount[-buffer - 1] > 0);
  792. LocalRefCount[-buffer - 1]--;
  793. }
  794. else
  795. {
  796. if (BufferIsValid(buffer))
  797. {
  798. bufHdr = &BufferDescriptors[buffer - 1];
  799. Assert(PrivateRefCount[buffer - 1] > 0);
  800. PrivateRefCount[buffer - 1]--;
  801. if (PrivateRefCount[buffer - 1] == 0 &&
  802. LastRefCount[buffer - 1] == 0)
  803. {
  804. /*
  805.  * only release buffer if it is not pinned in previous
  806.  * ExecMain level
  807.  */
  808. SpinAcquire(BufMgrLock);
  809. bufHdr->refcount--;
  810. if (bufHdr->refcount == 0)
  811. {
  812. AddBufferToFreelist(bufHdr);
  813. bufHdr->flags |= BM_FREE;
  814. }
  815. if (CommitInfoNeedsSave[buffer - 1])
  816. {
  817. bufHdr->flags |= (BM_DIRTY | BM_JUST_DIRTIED);
  818. CommitInfoNeedsSave[buffer - 1] = 0;
  819. }
  820. retbuf = ReadBufferWithBufferLock(relation, blockNum, true);
  821. return retbuf;
  822. }
  823. }
  824. }
  825. return ReadBuffer(relation, blockNum);
  826. }
  827. /*
  828.  * BufferSync -- Flush all dirty buffers in the pool.
  829.  *
  830.  * This is called at transaction commit time. It does the wrong thing,
  831.  * right now. We should flush only our own changes to stable storage,
  832.  * and we should obey the lock protocol on the buffer manager metadata
  833.  * as we do it.  Also, we need to be sure that no other transaction is
  834.  * modifying the page as we flush it. This is only a problem for objects
  835.  * that use a non-two-phase locking protocol, like btree indices. For
  836.  * those objects, we would like to set a write lock for the duration of
  837.  * our IO.  Another possibility is to code updates to btree pages
  838.  * carefully, so that writing them out out of order cannot cause
  839.  * any unrecoverable errors.
  840.  *
  841.  * I don't want to think hard about this right now, so I will try
  842.  * to come back to it later.
  843.  */
  844. static void
  845. BufferSync()
  846. {
  847. int i;
  848. Oid bufdb;
  849. Oid bufrel;
  850. Relation reln;
  851. BufferDesc *bufHdr;
  852. int status;
  853. SpinAcquire(BufMgrLock);
  854. for (i = 0, bufHdr = BufferDescriptors; i < NBuffers; i++, bufHdr++)
  855. {
  856. if ((bufHdr->flags & BM_VALID) && (bufHdr->flags & BM_DIRTY))
  857. {
  858. bufdb = bufHdr->tag.relId.dbId;
  859. bufrel = bufHdr->tag.relId.relId;
  860. if (bufdb == MyDatabaseId || bufdb == (Oid) 0)
  861. {
  862. reln = RelationIdCacheGetRelation(bufrel);
  863. /*
  864.  * We have to pin buffer to keep anyone from stealing it
  865.  * from the buffer pool while we are flushing it or
  866.  * waiting in WaitIO. It's bad for GetFreeBuffer in
  867.  * BufferAlloc, but there is no other way to prevent
  868.  * writing into disk block data from some other buffer,
  869.  * getting smgr status of some other block and clearing
  870.  * BM_DIRTY of ...   - VAdim 09/16/96
  871.  */
  872. PinBuffer(bufHdr);
  873. if (bufHdr->flags & BM_IO_IN_PROGRESS)
  874. {
  875. WaitIO(bufHdr, BufMgrLock);
  876. UnpinBuffer(bufHdr);
  877. if (bufHdr->flags & BM_IO_ERROR)
  878. {
  879. elog(ERROR, "BufferSync: write error %u for %s",
  880.  bufHdr->tag.blockNum, bufHdr->sb_relname);
  881. }
  882. if (reln != (Relation) NULL)
  883. RelationDecrementReferenceCount(reln);
  884. continue;
  885. }
  886. /*
  887.  * To check if block content changed while flushing (see
  888.  * below). - vadim 01/17/97
  889.  */
  890. bufHdr->flags &= ~BM_JUST_DIRTIED;
  891. /*
  892.  * If we didn't have the reldesc in our local cache, flush
  893.  * this page out using the 'blind write' storage manager
  894.  * routine.  If we did find it, use the standard
  895.  * interface.
  896.  */
  897. #ifndef OPTIMIZE_SINGLE
  898. SpinRelease(BufMgrLock);
  899. #endif  /* OPTIMIZE_SINGLE */
  900. if (reln == (Relation) NULL)
  901. {
  902. status = smgrblindwrt(DEFAULT_SMGR, bufHdr->sb_dbname,
  903.    bufHdr->sb_relname, bufdb, bufrel,
  904.   bufHdr->tag.blockNum,
  905. (char *) MAKE_PTR(bufHdr->data));
  906. }
  907. else
  908. {
  909. status = smgrwrite(DEFAULT_SMGR, reln,
  910.    bufHdr->tag.blockNum,
  911.    (char *) MAKE_PTR(bufHdr->data));
  912. }
  913. #ifndef OPTIMIZE_SINGLE
  914. SpinAcquire(BufMgrLock);
  915. #endif  /* OPTIMIZE_SINGLE */
  916. UnpinBuffer(bufHdr);
  917. if (status == SM_FAIL)
  918. {
  919. bufHdr->flags |= BM_IO_ERROR;
  920. elog(ERROR, "BufferSync: cannot write %u for %s",
  921.  bufHdr->tag.blockNum, bufHdr->sb_relname);
  922. }
  923. BufferFlushCount++;
  924. /*
  925.  * If this buffer was marked by someone as DIRTY while we
  926.  * were flushing it out we must not clear DIRTY flag -
  927.  * vadim 01/17/97
  928.  */
  929. if (!(bufHdr->flags & BM_JUST_DIRTIED))
  930. bufHdr->flags &= ~BM_DIRTY;
  931. if (reln != (Relation) NULL)
  932. RelationDecrementReferenceCount(reln);
  933. }
  934. }
  935. }
  936. SpinRelease(BufMgrLock);
  937. LocalBufferSync();
  938. }
  939. /*
  940.  * WaitIO -- Block until the IO_IN_PROGRESS flag on 'buf'
  941.  * is cleared.  Because IO_IN_PROGRESS conflicts are
  942.  * expected to be rare, there is only one BufferIO
  943.  * lock in the entire system. All processes block
  944.  * on this semaphore when they try to use a buffer
  945.  * that someone else is faulting in.  Whenever a
  946.  * process finishes an IO and someone is waiting for
  947.  * the buffer, BufferIO is signaled (SignalIO).  All
  948.  * waiting processes then wake up and check to see
  949.  * if their buffer is now ready.  This implementation
  950.  * is simple, but efficient enough if WaitIO is
  951.  * rarely called by multiple processes simultaneously.
  952.  *
  953.  * ProcSleep atomically releases the spinlock and goes to
  954.  * sleep.
  955.  *
  956.  * Note: there is an easy fix if the queue becomes long.
  957.  * save the id of the buffer we are waiting for in
  958.  * the queue structure.  That way signal can figure
  959.  * out which proc to wake up.
  960.  */
  961. #ifdef HAS_TEST_AND_SET
  962. static void
  963. WaitIO(BufferDesc *buf, SPINLOCK spinlock)
  964. {
  965. SpinRelease(spinlock);
  966. S_LOCK(&(buf->io_in_progress_lock));
  967. S_UNLOCK(&(buf->io_in_progress_lock));
  968. SpinAcquire(spinlock);
  969. }
  970. #else /* HAS_TEST_AND_SET */
  971. IpcSemaphoreId WaitIOSemId;
  972. IpcSemaphoreId WaitCLSemId;
  973. static void
  974. WaitIO(BufferDesc *buf, SPINLOCK spinlock)
  975. {
  976. bool inProgress;
  977. for (;;)
  978. {
  979. /* wait until someone releases IO lock */
  980. (*NWaitIOBackendP)++;
  981. SpinRelease(spinlock);
  982. IpcSemaphoreLock(WaitIOSemId, 0, 1);
  983. SpinAcquire(spinlock);
  984. inProgress = (buf->flags & BM_IO_IN_PROGRESS);
  985. if (!inProgress)
  986. break;
  987. }
  988. }
  989. /*
  990.  * SignalIO
  991.  */
  992. static void
  993. SignalIO(BufferDesc *buf)
  994. {
  995. /* somebody better be waiting. */
  996. Assert(buf->refcount > 1);
  997. IpcSemaphoreUnlock(WaitIOSemId, 0, *NWaitIOBackendP);
  998. *NWaitIOBackendP = 0;
  999. }
  1000. #endif  /* HAS_TEST_AND_SET */
  1001. long NDirectFileRead; /* some I/O's are direct file access.
  1002.  * bypass bufmgr */
  1003. long NDirectFileWrite; /* e.g., I/O in psort and hashjoin. */
  1004. void
  1005. PrintBufferUsage(FILE *statfp)
  1006. {
  1007. float hitrate;
  1008. float localhitrate;
  1009. if (ReadBufferCount == 0)
  1010. hitrate = 0.0;
  1011. else
  1012. hitrate = (float) BufferHitCount *100.0 / ReadBufferCount;
  1013. if (ReadLocalBufferCount == 0)
  1014. localhitrate = 0.0;
  1015. else
  1016. localhitrate = (float) LocalBufferHitCount *100.0 / ReadLocalBufferCount;
  1017. fprintf(statfp, "!tShared blocks: %10ld read, %10ld written, buffer hit rate = %.2f%%n",
  1018. ReadBufferCount - BufferHitCount, BufferFlushCount, hitrate);
  1019. fprintf(statfp, "!tLocal  blocks: %10ld read, %10ld written, buffer hit rate = %.2f%%n",
  1020. ReadLocalBufferCount - LocalBufferHitCount, LocalBufferFlushCount, localhitrate);
  1021. fprintf(statfp, "!tDirect blocks: %10ld read, %10ld writtenn",
  1022. NDirectFileRead, NDirectFileWrite);
  1023. }
  1024. void
  1025. ResetBufferUsage()
  1026. {
  1027. BufferHitCount = 0;
  1028. ReadBufferCount = 0;
  1029. BufferFlushCount = 0;
  1030. LocalBufferHitCount = 0;
  1031. ReadLocalBufferCount = 0;
  1032. LocalBufferFlushCount = 0;
  1033. NDirectFileRead = 0;
  1034. NDirectFileWrite = 0;
  1035. }
  1036. /* ----------------------------------------------
  1037.  * ResetBufferPool
  1038.  *
  1039.  * this routine is supposed to be called when a transaction aborts.
  1040.  * it will release all the buffer pins held by the transaciton.
  1041.  *
  1042.  * ----------------------------------------------
  1043.  */
  1044. void
  1045. ResetBufferPool()
  1046. {
  1047. int i;
  1048. for (i = 1; i <= NBuffers; i++)
  1049. {
  1050. CommitInfoNeedsSave[i - 1] = 0;
  1051. if (BufferIsValid(i))
  1052. {
  1053. while (PrivateRefCount[i - 1] > 0)
  1054. ReleaseBuffer(i);
  1055. }
  1056. LastRefCount[i - 1] = 0;
  1057. }
  1058. ResetLocalBufferPool();
  1059. }
  1060. /* -----------------------------------------------
  1061.  * BufferPoolCheckLeak
  1062.  *
  1063.  * check if there is buffer leak
  1064.  *
  1065.  * -----------------------------------------------
  1066.  */
  1067. int
  1068. BufferPoolCheckLeak()
  1069. {
  1070. int i;
  1071. int result = 0;
  1072. for (i = 1; i <= NBuffers; i++)
  1073. {
  1074. if (BufferIsValid(i))
  1075. {
  1076. BufferDesc *buf = &(BufferDescriptors[i - 1]);
  1077. elog(NOTICE,
  1078.  "Buffer Leak: [%03d] (freeNext=%d, freePrev=%d, 
  1079. relname=%s, blockNum=%d, flags=0x%x, refcount=%d %d)",
  1080.  i - 1, buf->freeNext, buf->freePrev,
  1081.  buf->sb_relname, buf->tag.blockNum, buf->flags,
  1082.  buf->refcount, PrivateRefCount[i - 1]);
  1083. result = 1;
  1084. }
  1085. }
  1086. return (result);
  1087. }
  1088. /* ------------------------------------------------
  1089.  * FlushBufferPool
  1090.  *
  1091.  * flush all dirty blocks in buffer pool to disk
  1092.  *
  1093.  * ------------------------------------------------
  1094.  */
  1095. void
  1096. FlushBufferPool(int StableMainMemoryFlag)
  1097. {
  1098. if (!StableMainMemoryFlag)
  1099. {
  1100. BufferSync();
  1101. smgrcommit();
  1102. }
  1103. }
  1104. /*
  1105.  * BufferGetBlockNumber
  1106.  * Returns the block number associated with a buffer.
  1107.  *
  1108.  * Note:
  1109.  * Assumes that the buffer is valid.
  1110.  */
  1111. BlockNumber
  1112. BufferGetBlockNumber(Buffer buffer)
  1113. {
  1114. Assert(BufferIsValid(buffer));
  1115. /* XXX should be a critical section */
  1116. if (BufferIsLocal(buffer))
  1117. return LocalBufferDescriptors[-buffer - 1].tag.blockNum;
  1118. else
  1119. return BufferDescriptors[buffer - 1].tag.blockNum;
  1120. }
  1121. #ifdef NOT_USED
  1122. /*
  1123.  * BufferGetRelation
  1124.  * Returns the relation desciptor associated with a buffer.
  1125.  *
  1126.  * Note:
  1127.  * Assumes buffer is valid.
  1128.  */
  1129. Relation
  1130. BufferGetRelation(Buffer buffer)
  1131. {
  1132. Relation relation;
  1133. Oid relid;
  1134. Assert(BufferIsValid(buffer));
  1135. Assert(!BufferIsLocal(buffer)); /* not supported for local buffers */
  1136. /* XXX should be a critical section */
  1137. relid = BufferDescriptors[buffer - 1].tag.relId.relId;
  1138. relation = RelationIdGetRelation(relid);
  1139. RelationDecrementReferenceCount(relation);
  1140. if (RelationHasReferenceCountZero(relation))
  1141. {
  1142. /*
  1143.  * elog(NOTICE, "BufferGetRelation: 0->1");
  1144.  */
  1145. RelationIncrementReferenceCount(relation);
  1146. }
  1147. return relation;
  1148. }
  1149. #endif
  1150. /*
  1151.  * BufferReplace
  1152.  *
  1153.  * Flush the buffer corresponding to 'bufHdr'
  1154.  *
  1155.  */
  1156. static int
  1157. BufferReplace(BufferDesc *bufHdr, bool bufferLockHeld)
  1158. {
  1159. Relation reln;
  1160. Oid bufdb,
  1161. bufrel;
  1162. int status;
  1163. if (!bufferLockHeld)
  1164. SpinAcquire(BufMgrLock);
  1165. /*
  1166.  * first try to find the reldesc in the cache, if no luck, don't
  1167.  * bother to build the reldesc from scratch, just do a blind write.
  1168.  */
  1169. bufdb = bufHdr->tag.relId.dbId;
  1170. bufrel = bufHdr->tag.relId.relId;
  1171. if (bufdb == MyDatabaseId || bufdb == (Oid) NULL)
  1172. reln = RelationIdCacheGetRelation(bufrel);
  1173. else
  1174. reln = (Relation) NULL;
  1175. /* To check if block content changed while flushing. - vadim 01/17/97 */
  1176. bufHdr->flags &= ~BM_JUST_DIRTIED;
  1177. SpinRelease(BufMgrLock);
  1178. if (reln != (Relation) NULL)
  1179. {
  1180. status = smgrflush(DEFAULT_SMGR, reln, bufHdr->tag.blockNum,
  1181.    (char *) MAKE_PTR(bufHdr->data));
  1182. }
  1183. else
  1184. {
  1185. /* blind write always flushes */
  1186. status = smgrblindwrt(DEFAULT_SMGR, bufHdr->sb_dbname,
  1187.   bufHdr->sb_relname, bufdb, bufrel,
  1188.   bufHdr->tag.blockNum,
  1189.   (char *) MAKE_PTR(bufHdr->data));
  1190. }
  1191. if (reln != (Relation) NULL)
  1192. RelationDecrementReferenceCount(reln);
  1193. if (status == SM_FAIL)
  1194. return FALSE;
  1195. BufferFlushCount++;
  1196. return TRUE;
  1197. }
  1198. /*
  1199.  * RelationGetNumberOfBlocks
  1200.  * Returns the buffer descriptor associated with a page in a relation.
  1201.  *
  1202.  * Note:
  1203.  * XXX may fail for huge relations.
  1204.  * XXX should be elsewhere.
  1205.  * XXX maybe should be hidden
  1206.  */
  1207. BlockNumber
  1208. RelationGetNumberOfBlocks(Relation relation)
  1209. {
  1210. return ((relation->rd_myxactonly) ? relation->rd_nblocks :
  1211. smgrnblocks(DEFAULT_SMGR, relation));
  1212. }
  1213. /* ---------------------------------------------------------------------
  1214.  * ReleaseRelationBuffers
  1215.  *
  1216.  * this function unmarks all the dirty pages of a relation
  1217.  * in the buffer pool so that at the end of transaction
  1218.  * these pages will not be flushed.
  1219.  * XXX currently it sequentially searches the buffer pool, should be
  1220.  * changed to more clever ways of searching.
  1221.  * --------------------------------------------------------------------
  1222.  */
  1223. void
  1224. ReleaseRelationBuffers(Relation rel)
  1225. {
  1226. int i;
  1227. int holding = 0;
  1228. BufferDesc *buf;
  1229. if (rel->rd_myxactonly)
  1230. {
  1231. for (i = 0; i < NLocBuffer; i++)
  1232. {
  1233. buf = &LocalBufferDescriptors[i];
  1234. if ((buf->flags & BM_DIRTY) &&
  1235. (buf->tag.relId.relId == RelationGetRelid(rel)))
  1236. buf->flags &= ~BM_DIRTY;
  1237. }
  1238. return;
  1239. }
  1240. for (i = 1; i <= NBuffers; i++)
  1241. {
  1242. buf = &BufferDescriptors[i - 1];
  1243. if (!holding)
  1244. {
  1245. SpinAcquire(BufMgrLock);
  1246. holding = 1;
  1247. }
  1248. if ((buf->flags & BM_DIRTY) &&
  1249. (buf->tag.relId.dbId == MyDatabaseId) &&
  1250. (buf->tag.relId.relId == RelationGetRelid(rel)))
  1251. {
  1252. buf->flags &= ~BM_DIRTY;
  1253. if (!(buf->flags & BM_FREE))
  1254. {
  1255. SpinRelease(BufMgrLock);
  1256. holding = 0;
  1257. ReleaseBuffer(i);
  1258. }
  1259. }
  1260. }
  1261. if (holding)
  1262. SpinRelease(BufMgrLock);
  1263. }
  1264. /* ---------------------------------------------------------------------
  1265.  * DropBuffers
  1266.  *
  1267.  * This function marks all the buffers in the buffer cache for a
  1268.  * particular database as clean.  This is used when we destroy a
  1269.  * database, to avoid trying to flush data to disk when the directory
  1270.  * tree no longer exists.
  1271.  *
  1272.  * This is an exceedingly non-public interface.
  1273.  * --------------------------------------------------------------------
  1274.  */
  1275. void
  1276. DropBuffers(Oid dbid)
  1277. {
  1278. int i;
  1279. BufferDesc *buf;
  1280. SpinAcquire(BufMgrLock);
  1281. for (i = 1; i <= NBuffers; i++)
  1282. {
  1283. buf = &BufferDescriptors[i - 1];
  1284. if ((buf->tag.relId.dbId == dbid) && (buf->flags & BM_DIRTY))
  1285. buf->flags &= ~BM_DIRTY;
  1286. }
  1287. SpinRelease(BufMgrLock);
  1288. }
  1289. /* -----------------------------------------------------------------
  1290.  * PrintBufferDescs
  1291.  *
  1292.  * this function prints all the buffer descriptors, for debugging
  1293.  * use only.
  1294.  * -----------------------------------------------------------------
  1295.  */
  1296. void
  1297. PrintBufferDescs()
  1298. {
  1299. int i;
  1300. BufferDesc *buf = BufferDescriptors;
  1301. if (IsUnderPostmaster)
  1302. {
  1303. SpinAcquire(BufMgrLock);
  1304. for (i = 0; i < NBuffers; ++i, ++buf)
  1305. {
  1306. elog(DEBUG, "[%02d] (freeNext=%d, freePrev=%d, relname=%s, 
  1307. blockNum=%d, flags=0x%x, refcount=%d %d)",
  1308.  i, buf->freeNext, buf->freePrev,
  1309.  buf->sb_relname, buf->tag.blockNum, buf->flags,
  1310.  buf->refcount, PrivateRefCount[i]);
  1311. }
  1312. SpinRelease(BufMgrLock);
  1313. }
  1314. else
  1315. {
  1316. /* interactive backend */
  1317. for (i = 0; i < NBuffers; ++i, ++buf)
  1318. {
  1319. printf("[%-2d] (%s, %d) flags=0x%x, refcnt=%d %ld)n",
  1320.    i, buf->sb_relname, buf->tag.blockNum,
  1321.    buf->flags, buf->refcount, PrivateRefCount[i]);
  1322. }
  1323. }
  1324. }
  1325. void
  1326. PrintPinnedBufs()
  1327. {
  1328. int i;
  1329. BufferDesc *buf = BufferDescriptors;
  1330. SpinAcquire(BufMgrLock);
  1331. for (i = 0; i < NBuffers; ++i, ++buf)
  1332. {
  1333. if (PrivateRefCount[i] > 0)
  1334. elog(NOTICE, "[%02d] (freeNext=%d, freePrev=%d, relname=%s, 
  1335. blockNum=%d, flags=0x%x, refcount=%d %d)n",
  1336.  i, buf->freeNext, buf->freePrev, buf->sb_relname,
  1337.  buf->tag.blockNum, buf->flags,
  1338.  buf->refcount, PrivateRefCount[i]);
  1339. }
  1340. SpinRelease(BufMgrLock);
  1341. }
  1342. /*
  1343.  * BufferPoolBlowaway
  1344.  *
  1345.  * this routine is solely for the purpose of experiments -- sometimes
  1346.  * you may want to blowaway whatever is left from the past in buffer
  1347.  * pool and start measuring some performance with a clean empty buffer
  1348.  * pool.
  1349.  */
  1350. #ifdef NOT_USED
  1351. void
  1352. BufferPoolBlowaway()
  1353. {
  1354. int i;
  1355. BufferSync();
  1356. for (i = 1; i <= NBuffers; i++)
  1357. {
  1358. if (BufferIsValid(i))
  1359. {
  1360. while (BufferIsValid(i))
  1361. ReleaseBuffer(i);
  1362. }
  1363. BufTableDelete(&BufferDescriptors[i - 1]);
  1364. }
  1365. }
  1366. #endif
  1367. /* ---------------------------------------------------------------------
  1368.  * BlowawayRelationBuffers
  1369.  *
  1370.  * This function blowaway all the pages with blocknumber >= passed
  1371.  * of a relation in the buffer pool. Used by vacuum before truncation...
  1372.  *
  1373.  * Returns: 0 - Ok, -1 - DIRTY, -2 - PINNED
  1374.  *
  1375.  * XXX currently it sequentially searches the buffer pool, should be
  1376.  * changed to more clever ways of searching.
  1377.  * --------------------------------------------------------------------
  1378.  */
  1379. int
  1380. BlowawayRelationBuffers(Relation rel, BlockNumber block)
  1381. {
  1382. int i;
  1383. BufferDesc *buf;
  1384. if (rel->rd_myxactonly)
  1385. {
  1386. for (i = 0; i < NLocBuffer; i++)
  1387. {
  1388. buf = &LocalBufferDescriptors[i];
  1389. if (buf->tag.relId.relId == RelationGetRelid(rel) &&
  1390. buf->tag.blockNum >= block)
  1391. {
  1392. if (buf->flags & BM_DIRTY)
  1393. {
  1394. elog(NOTICE, "BlowawayRelationBuffers(%s (local), %u): block %u is dirty",
  1395. rel->rd_rel->relname.data, block, buf->tag.blockNum);
  1396. return -1;
  1397. }
  1398. if (LocalRefCount[i] > 0)
  1399. {
  1400. elog(NOTICE, "BlowawayRelationBuffers(%s (local), %u): block %u is referenced (%d)",
  1401.  rel->rd_rel->relname.data, block,
  1402.  buf->tag.blockNum, LocalRefCount[i]);
  1403. return -2;
  1404. }
  1405. buf->tag.relId.relId = InvalidOid;
  1406. }
  1407. }
  1408. return 0;
  1409. }
  1410. SpinAcquire(BufMgrLock);
  1411. for (i = 0; i < NBuffers; i++)
  1412. {
  1413. buf = &BufferDescriptors[i];
  1414. if (buf->tag.relId.dbId == MyDatabaseId &&
  1415. buf->tag.relId.relId == RelationGetRelid(rel) &&
  1416. buf->tag.blockNum >= block)
  1417. {
  1418. if (buf->flags & BM_DIRTY)
  1419. {
  1420. elog(NOTICE, "BlowawayRelationBuffers(%s, %u): block %u is dirty (private %d, last %d, global %d)",
  1421.  buf->sb_relname, block, buf->tag.blockNum,
  1422.  PrivateRefCount[i], LastRefCount[i], buf->refcount);
  1423. SpinRelease(BufMgrLock);
  1424. return -1;
  1425. }
  1426. if (!(buf->flags & BM_FREE))
  1427. {
  1428. elog(NOTICE, "BlowawayRelationBuffers(%s, %u): block %u is referenced (private %d, last %d, global %d)",
  1429.  buf->sb_relname, block, buf->tag.blockNum,
  1430.  PrivateRefCount[i], LastRefCount[i], buf->refcount);
  1431. SpinRelease(BufMgrLock);
  1432. return -2;
  1433. }
  1434. BufTableDelete(buf);
  1435. }
  1436. }
  1437. SpinRelease(BufMgrLock);
  1438. return 0;
  1439. }
  1440. #undef ReleaseBuffer
  1441. /*
  1442.  * ReleaseBuffer -- remove the pin on a buffer without
  1443.  * marking it dirty.
  1444.  *
  1445.  */
  1446. int
  1447. ReleaseBuffer(Buffer buffer)
  1448. {
  1449. BufferDesc *bufHdr;
  1450. if (BufferIsLocal(buffer))
  1451. {
  1452. Assert(LocalRefCount[-buffer - 1] > 0);
  1453. LocalRefCount[-buffer - 1]--;
  1454. return STATUS_OK;
  1455. }
  1456. if (BAD_BUFFER_ID(buffer))
  1457. return STATUS_ERROR;
  1458. bufHdr = &BufferDescriptors[buffer - 1];
  1459. Assert(PrivateRefCount[buffer - 1] > 0);
  1460. PrivateRefCount[buffer - 1]--;
  1461. if (PrivateRefCount[buffer - 1] == 0 && LastRefCount[buffer - 1] == 0)
  1462. {
  1463. /*
  1464.  * only release buffer if it is not pinned in previous ExecMain
  1465.  * levels
  1466.  */
  1467. SpinAcquire(BufMgrLock);
  1468. bufHdr->refcount--;
  1469. if (bufHdr->refcount == 0)
  1470. {
  1471. AddBufferToFreelist(bufHdr);
  1472. bufHdr->flags |= BM_FREE;
  1473. }
  1474. if (CommitInfoNeedsSave[buffer - 1])
  1475. {
  1476. bufHdr->flags |= (BM_DIRTY | BM_JUST_DIRTIED);
  1477. CommitInfoNeedsSave[buffer - 1] = 0;
  1478. }
  1479. SpinRelease(BufMgrLock);
  1480. }
  1481. return STATUS_OK;
  1482. }
  1483. #ifdef NOT_USED
  1484. void
  1485. IncrBufferRefCount_Debug(char *file, int line, Buffer buffer)
  1486. {
  1487. IncrBufferRefCount(buffer);
  1488. if (ShowPinTrace && !BufferIsLocal(buffer) && is_userbuffer(buffer))
  1489. {
  1490. BufferDesc *buf = &BufferDescriptors[buffer - 1];
  1491. fprintf(stderr, "PIN(Incr) %ld relname = %s, blockNum = %d, 
  1492. refcount = %ld, file: %s, line: %dn",
  1493. buffer, buf->sb_relname, buf->tag.blockNum,
  1494. PrivateRefCount[buffer - 1], file, line);
  1495. }
  1496. }
  1497. #endif
  1498. #ifdef NOT_USED
  1499. void
  1500. ReleaseBuffer_Debug(char *file, int line, Buffer buffer)
  1501. {
  1502. ReleaseBuffer(buffer);
  1503. if (ShowPinTrace && !BufferIsLocal(buffer) && is_userbuffer(buffer))
  1504. {
  1505. BufferDesc *buf = &BufferDescriptors[buffer - 1];
  1506. fprintf(stderr, "UNPIN(Rel) %ld relname = %s, blockNum = %d, 
  1507. refcount = %ld, file: %s, line: %dn",
  1508. buffer, buf->sb_relname, buf->tag.blockNum,
  1509. PrivateRefCount[buffer - 1], file, line);
  1510. }
  1511. }
  1512. #endif
  1513. #ifdef NOT_USED
  1514. int
  1515. ReleaseAndReadBuffer_Debug(char *file,
  1516.    int line,
  1517.    Buffer buffer,
  1518.    Relation relation,
  1519.    BlockNumber blockNum)
  1520. {
  1521. bool bufferValid;
  1522. Buffer b;
  1523. bufferValid = BufferIsValid(buffer);
  1524. b = ReleaseAndReadBuffer(buffer, relation, blockNum);
  1525. if (ShowPinTrace && bufferValid && BufferIsLocal(buffer)
  1526. && is_userbuffer(buffer))
  1527. {
  1528. BufferDesc *buf = &BufferDescriptors[buffer - 1];
  1529. fprintf(stderr, "UNPIN(Rel&Rd) %ld relname = %s, blockNum = %d, 
  1530. refcount = %ld, file: %s, line: %dn",
  1531. buffer, buf->sb_relname, buf->tag.blockNum,
  1532. PrivateRefCount[buffer - 1], file, line);
  1533. }
  1534. if (ShowPinTrace && BufferIsLocal(buffer) && is_userbuffer(buffer))
  1535. {
  1536. BufferDesc *buf = &BufferDescriptors[b - 1];
  1537. fprintf(stderr, "PIN(Rel&Rd) %ld relname = %s, blockNum = %d, 
  1538. refcount = %ld, file: %s, line: %dn",
  1539. b, buf->sb_relname, buf->tag.blockNum,
  1540. PrivateRefCount[b - 1], file, line);
  1541. }
  1542. return b;
  1543. }
  1544. #endif
  1545. #ifdef BMTRACE
  1546. /*
  1547.  * trace allocations and deallocations in a circular buffer in
  1548.  * shared memory. check the buffer before doing the allocation,
  1549.  * and die if there's anything fishy.
  1550.  */
  1551. _bm_trace(Oid dbId, Oid relId, int blkNo, int bufNo, int allocType)
  1552. {
  1553. long start,
  1554. cur;
  1555. bmtrace    *tb;
  1556. start = *CurTraceBuf;
  1557. if (start > 0)
  1558. cur = start - 1;
  1559. else
  1560. cur = BMT_LIMIT - 1;
  1561. for (;;)
  1562. {
  1563. tb = &TraceBuf[cur];
  1564. if (tb->bmt_op != BMT_NOTUSED)
  1565. {
  1566. if (tb->bmt_buf == bufNo)
  1567. {
  1568. if ((tb->bmt_op == BMT_DEALLOC)
  1569. || (tb->bmt_dbid == dbId && tb->bmt_relid == relId
  1570. && tb->bmt_blkno == blkNo))
  1571. goto okay;
  1572. /* die holding the buffer lock */
  1573. _bm_die(dbId, relId, blkNo, bufNo, allocType, start, cur);
  1574. }
  1575. }
  1576. if (cur == start)
  1577. goto okay;
  1578. if (cur == 0)
  1579. cur = BMT_LIMIT - 1;
  1580. else
  1581. cur--;
  1582. }
  1583. okay:
  1584. tb = &TraceBuf[start];
  1585. tb->bmt_pid = MyProcPid;
  1586. tb->bmt_buf = bufNo;
  1587. tb->bmt_dbid = dbId;
  1588. tb->bmt_relid = relId;
  1589. tb->bmt_blkno = blkNo;
  1590. tb->bmt_op = allocType;
  1591. *CurTraceBuf = (start + 1) % BMT_LIMIT;
  1592. }
  1593. _bm_die(Oid dbId, Oid relId, int blkNo, int bufNo,
  1594. int allocType, long start, long cur)
  1595. {
  1596. FILE    *fp;
  1597. bmtrace    *tb;
  1598. int i;
  1599. tb = &TraceBuf[cur];
  1600. if ((fp = AllocateFile("/tmp/death_notice", "w")) == NULL)
  1601. elog(FATAL, "buffer alloc trace error and can't open log file");
  1602. fprintf(fp, "buffer alloc trace detected the following error:nn");
  1603. fprintf(fp, "    buffer %d being %s inconsistently with a previous %snn",
  1604.  bufNo, (allocType == BMT_DEALLOC ? "deallocated" : "allocated"),
  1605. (tb->bmt_op == BMT_DEALLOC ? "deallocation" : "allocation"));
  1606. fprintf(fp, "the trace buffer contains:n");
  1607. i = start;
  1608. for (;;)
  1609. {
  1610. tb = &TraceBuf[i];
  1611. if (tb->bmt_op != BMT_NOTUSED)
  1612. {
  1613. fprintf(fp, "     [%3d]%spid %d buf %2d for <%d,%u,%d> ",
  1614. i, (i == cur ? " ---> " : "t"),
  1615. tb->bmt_pid, tb->bmt_buf,
  1616. tb->bmt_dbid, tb->bmt_relid, tb->bmt_blkno);
  1617. switch (tb->bmt_op)
  1618. {
  1619. case BMT_ALLOCFND:
  1620. fprintf(fp, "allocate (found)n");
  1621. break;
  1622. case BMT_ALLOCNOTFND:
  1623. fprintf(fp, "allocate (not found)n");
  1624. break;
  1625. case BMT_DEALLOC:
  1626. fprintf(fp, "deallocaten");
  1627. break;
  1628. default:
  1629. fprintf(fp, "unknown op type %dn", tb->bmt_op);
  1630. break;
  1631. }
  1632. }
  1633. i = (i + 1) % BMT_LIMIT;
  1634. if (i == start)
  1635. break;
  1636. }
  1637. fprintf(fp, "noperation causing error:n");
  1638. fprintf(fp, "tpid %d buf %d for <%d,%u,%d> ",
  1639. getpid(), bufNo, dbId, relId, blkNo);
  1640. switch (allocType)
  1641. {
  1642. case BMT_ALLOCFND:
  1643. fprintf(fp, "allocate (found)n");
  1644. break;
  1645. case BMT_ALLOCNOTFND:
  1646. fprintf(fp, "allocate (not found)n");
  1647. break;
  1648. case BMT_DEALLOC:
  1649. fprintf(fp, "deallocaten");
  1650. break;
  1651. default:
  1652. fprintf(fp, "unknown op type %dn", allocType);
  1653. break;
  1654. }
  1655. FreeFile(fp);
  1656. kill(getpid(), SIGILL);
  1657. }
  1658. #endif  /* BMTRACE */
  1659. void
  1660. BufferRefCountReset(int *refcountsave)
  1661. {
  1662. int i;
  1663. for (i = 0; i < NBuffers; i++)
  1664. {
  1665. refcountsave[i] = PrivateRefCount[i];
  1666. LastRefCount[i] += PrivateRefCount[i];
  1667. PrivateRefCount[i] = 0;
  1668. }
  1669. }
  1670. void
  1671. BufferRefCountRestore(int *refcountsave)
  1672. {
  1673. int i;
  1674. for (i = 0; i < NBuffers; i++)
  1675. {
  1676. PrivateRefCount[i] = refcountsave[i];
  1677. LastRefCount[i] -= refcountsave[i];
  1678. refcountsave[i] = 0;
  1679. }
  1680. }
  1681. int
  1682. SetBufferWriteMode(int mode)
  1683. {
  1684. int old;
  1685. old = WriteMode;
  1686. WriteMode = mode;
  1687. return old;
  1688. }
  1689. void
  1690. SetBufferCommitInfoNeedsSave(Buffer buffer)
  1691. {
  1692. if (!BufferIsLocal(buffer))
  1693. CommitInfoNeedsSave[buffer - 1]++;
  1694. }
  1695. void
  1696. UnlockBuffers()
  1697. {
  1698. BufferDesc *buf;
  1699. int i;
  1700. for (i = 0; i < NBuffers; i++)
  1701. {
  1702. if (BufferLocks[i] == 0)
  1703. continue;
  1704. Assert(BufferIsValid(i + 1));
  1705. buf = &(BufferDescriptors[i]);
  1706. #ifdef HAS_TEST_AND_SET
  1707. S_LOCK(&(buf->cntx_lock));
  1708. #else
  1709. IpcSemaphoreLock(WaitCLSemId, 0, IpcExclusiveLock);
  1710. #endif
  1711. if (BufferLocks[i] & BL_R_LOCK)
  1712. {
  1713. Assert(buf->r_locks > 0);
  1714. (buf->r_locks)--;
  1715. }
  1716. if (BufferLocks[i] & BL_RI_LOCK)
  1717. {
  1718. /* 
  1719.  * Someone else could remove our RI lock when acquiring
  1720.  * W lock. This is possible if we came here from elog(ERROR)
  1721.  * from IpcSemaphore{Lock|Unlock}(WaitCLSemId). And so we
  1722.  * don't do Assert(buf->ri_lock) here.
  1723.  */
  1724. buf->ri_lock = false;
  1725. }
  1726. if (BufferLocks[i] & BL_W_LOCK)
  1727. {
  1728. Assert(buf->w_lock);
  1729. buf->w_lock = false;
  1730. }
  1731. #ifdef HAS_TEST_AND_SET
  1732. S_UNLOCK(&(buf->cntx_lock));
  1733. #else
  1734. IpcSemaphoreUnlock(WaitCLSemId, 0, IpcExclusiveLock);
  1735. #endif
  1736. BufferLocks[i] = 0;
  1737. }
  1738. }
  1739. void
  1740. LockBuffer(Buffer buffer, int mode)
  1741. {
  1742. BufferDesc *buf;
  1743. Assert(BufferIsValid(buffer));
  1744. if (BufferIsLocal(buffer))
  1745. return;
  1746. buf = &(BufferDescriptors[buffer - 1]);
  1747. #ifdef HAS_TEST_AND_SET
  1748. S_LOCK(&(buf->cntx_lock));
  1749. #else
  1750. IpcSemaphoreLock(WaitCLSemId, 0, IpcExclusiveLock);
  1751. #endif
  1752. if (mode == BUFFER_LOCK_UNLOCK)
  1753. {
  1754. if (BufferLocks[buffer - 1] & BL_R_LOCK)
  1755. {
  1756. Assert(buf->r_locks > 0);
  1757. Assert(!(buf->w_lock));
  1758. Assert(!(BufferLocks[buffer - 1] & (BL_W_LOCK | BL_RI_LOCK)))
  1759. (buf->r_locks)--;
  1760. BufferLocks[buffer - 1] &= ~BL_R_LOCK;
  1761. }
  1762. else if (BufferLocks[buffer - 1] & BL_W_LOCK)
  1763. {
  1764. Assert(buf->w_lock);
  1765. Assert(buf->r_locks == 0);
  1766. Assert(!(BufferLocks[buffer - 1] & (BL_R_LOCK | BL_RI_LOCK)))
  1767. buf->w_lock = false;
  1768. BufferLocks[buffer - 1] &= ~BL_W_LOCK;
  1769. }
  1770. else
  1771. elog(ERROR, "UNLockBuffer: buffer %u is not locked", buffer);
  1772. }
  1773. else if (mode == BUFFER_LOCK_SHARE)
  1774. {
  1775. unsigned i = 0;
  1776. Assert(!(BufferLocks[buffer - 1] & (BL_R_LOCK | BL_W_LOCK | BL_RI_LOCK)));
  1777. while (buf->ri_lock || buf->w_lock)
  1778. {
  1779. #ifdef HAS_TEST_AND_SET
  1780. S_UNLOCK(&(buf->cntx_lock));
  1781. s_lock_sleep(i++);
  1782. S_LOCK(&(buf->cntx_lock));
  1783. #else
  1784. IpcSemaphoreUnlock(WaitCLSemId, 0, IpcExclusiveLock);
  1785. s_lock_sleep(i++);
  1786. IpcSemaphoreLock(WaitCLSemId, 0, IpcExclusiveLock);
  1787. #endif
  1788. }
  1789. (buf->r_locks)++;
  1790. BufferLocks[buffer - 1] |= BL_R_LOCK;
  1791. }
  1792. else if (mode == BUFFER_LOCK_EXCLUSIVE)
  1793. {
  1794. unsigned i = 0;
  1795. Assert(!(BufferLocks[buffer - 1] & (BL_R_LOCK | BL_W_LOCK | BL_RI_LOCK)));
  1796. while (buf->r_locks > 0 || buf->w_lock)
  1797. {
  1798. if (buf->r_locks > 3 || (BufferLocks[buffer - 1] & BL_RI_LOCK))
  1799. {
  1800. /*
  1801.  * Our RI lock might be removed by concurrent W lock
  1802.  * acquiring (see what we do with RI locks below
  1803.  * when our own W acquiring succeeded) and so
  1804.  * we set RI lock again if we already did this.
  1805.  */
  1806. BufferLocks[buffer - 1] |= BL_RI_LOCK;
  1807. buf->ri_lock = true;
  1808. }
  1809. #ifdef HAS_TEST_AND_SET
  1810. S_UNLOCK(&(buf->cntx_lock));
  1811. s_lock_sleep(i++);
  1812. S_LOCK(&(buf->cntx_lock));
  1813. #else
  1814. IpcSemaphoreUnlock(WaitCLSemId, 0, IpcExclusiveLock);
  1815. s_lock_sleep(i++);
  1816. IpcSemaphoreLock(WaitCLSemId, 0, IpcExclusiveLock);
  1817. #endif
  1818. }
  1819. buf->w_lock = true;
  1820. BufferLocks[buffer - 1] |= BL_W_LOCK;
  1821. if (BufferLocks[buffer - 1] & BL_RI_LOCK)
  1822. {
  1823. /*
  1824.  * It's possible to remove RI locks acquired by another
  1825.  * W lockers here, but they'll take care about it.
  1826.  */
  1827. buf->ri_lock = false;
  1828. BufferLocks[buffer - 1] &= ~BL_RI_LOCK;
  1829. }
  1830. }
  1831. else
  1832. elog(ERROR, "LockBuffer: unknown lock mode %d", mode);
  1833. #ifdef HAS_TEST_AND_SET
  1834. S_UNLOCK(&(buf->cntx_lock));
  1835. #else
  1836. IpcSemaphoreUnlock(WaitCLSemId, 0, IpcExclusiveLock);
  1837. #endif
  1838. }