YoushouldhavereceivedacopyoftheGNUGeneralPublicLicense alongwiththisprogram;ifnot,writetotheFreeSoftware
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
/* types of condition variables */ #define COND_FOR_REQUESTED 0/* queue of thread waiting for read operation */ #define COND_FOR_SAVED 1/* queue of thread waiting for flush */ #define COND_FOR_WRLOCK 2/* queue of write lock */ #define COND_SIZE 3/* number of COND_* queues */
typedef mysql_cond_t KEYCACHE_CONDVAR;
/* descriptor of the page in the page cache block buffer */ struct st_pagecache_page
{
PAGECACHE_FILE file; /* file to which the page belongs to */
pgcache_page_no_t pageno; /* number of the page in the file */
};
/* element in the chain of a hash table bucket */ struct st_pagecache_hash_link
{ struct st_pagecache_hash_link
*next, **prev; /* to connect links in the same bucket */ struct st_pagecache_block_link
*block; /* reference to the block for the page: */
PAGECACHE_FILE file; /* from such a file */
pgcache_page_no_t pageno; /* this page */
uint requests; /* number of requests for the page */
};
/* simple states of a block */ #define PCBLOCK_ERROR 1/* an error occurred when performing disk i/o */ #define PCBLOCK_READ 2/* there is an active page in the block buffer */
/* Atreadisreadingthedatatothepage. Ifthepagecontainedoldchangeddata,itwillbewrittenoutwith thisstatesetontheblock. Thepageisnotyetreadytobeusedforreading.
*/ #define PCBLOCK_IN_SWITCH 4 /* Blockdoesnotacceptnewrequestsforoldpagethatwouldcause thepagetobepinnedorwrittento. (Readsthatcopiestheblockcanstillcontinue). Thisstatehappenswhenanotherthreadiswaitingforreaderstofinish toreaddatatotheblock(aftertheblock,ifitwaschanged,hasbeen flushedouttodisk).
*/ #define PCBLOCK_REASSIGNED 8 #define PCBLOCK_IN_FLUSH 16/* block is in flush operation */ #define PCBLOCK_CHANGED 32/* block buffer contains a dirty page */ #define PCBLOCK_DIRECT_W 64/* possible direct write to the block */ #define PCBLOCK_DEL_WRITE 128/* should be written on delete */ #define PCBLOCK_BIG_READ 256/* the first block of the big read in progress ornotfirstblockwhichotherthreadwait
to be read in big read operation */
/* page status, returned by find_block */ #define PAGE_READ 0 #define PAGE_TO_BE_READ 1 #define PAGE_WAIT_TO_BE_READ 2
/* block temperature determines in which (sub-)chain the block currently is */ enum PCBLOCK_TEMPERATURE { PCBLOCK_COLD /*free*/ , PCBLOCK_WARM , PCBLOCK_HOT };
/* debug info */ #ifdef DBUG_TRACE staticconstchar *page_cache_page_type_str[]=
{ /* used only for control page type changing during debugging */ "EMPTY", "PLAIN", "LSN", "READ_UNKNOWN"
};
/* page cache block */ struct st_pagecache_block_link
{ struct st_pagecache_block_link
*next_used, **prev_used; /* to connect links in the LRU chain (ring) */ struct st_pagecache_block_link
*next_changed, **prev_changed; /* for lists of file dirty/clean blocks */ struct st_pagecache_hash_link
*hash_link; /* backward ptr to referring hash_link */ #ifndef DBUG_OFF
PAGECACHE_PIN_INFO *pin_list;
PAGECACHE_LOCK_INFO *lock_list; #endif
KEYCACHE_CONDVAR *condvar; /* condition variable for 'no readers' event */
uchar *buffer; /* buffer for the block page */
pthread_t write_locker;
ulonglong last_hit_time; /* timestamp of the last hit */
WQUEUE
wqueue[COND_SIZE]; /* queues on waiting requests for new/old pages */
uint32 requests; /* number of requests for the block */
uint32 pins; /* pin counter */
uint32 wlocks; /* write locks counter */
uint32 rlocks; /* read locks counter */
uint32 rlocks_queue; /* rd. locks waiting wr. lock of this thread */
uint16 status; /* state of the block */
int16 error; /* error code for block in case of error */ enum PCBLOCK_TEMPERATURE temperature; /* block temperature: cold, warm, hot*/ enum pagecache_page_type type; /* type of the block */
uint hits_left; /* number of hits left until promotion */ /** @brief LSN when first became dirty; LSN_MAX means "not yet set" */
LSN rec_lsn;
};
/** @brief information describing a run of flush_pagecache_blocks_int() */ struct st_file_in_flush
{
File file; /** @briefthreadswaitingforthethreadcurrentlyflushingthisfiletobe done
*/
WQUEUE flush_queue; /** @briefifthethreadcurrentlyflushingthefilehasanon-empty first_in_switchlist.
*/
my_bool first_in_switch;
};
while (wqueue->last_thread->next != thread)
{
pagecache_pthread_cond_wait(&thread->suspend, &pagecache->cache_lock);
}
pagecache->resize_in_flush= 1; if (flush_all_key_blocks(pagecache))
{ /* TODO: if this happens, we should write a warning in the log file ! */
pagecache->resize_in_flush= 0;
blocks= 0;
pagecache->can_be_used= 0; goto finish;
}
pagecache->resize_in_flush= 0;
pagecache->can_be_used= 0; while (pagecache->cnt_for_resize_op)
{
DBUG_PRINT("wait", ("suspend thread %s %ld", thread->name, thread->id));
pagecache_pthread_cond_wait(&thread->suspend, &pagecache->cache_lock);
}
end_pagecache(pagecache, 0); /* Don't free mutex */ /* The following will work even if use_mem is 0 */
blocks= init_pagecache(pagecache, pagecache->block_size, use_mem,
division_limit, age_threshold, changed_blocks_hash_size,
pagecache->readwrite_flags);
finish:
wqueue_unlink_from_queue(wqueue, thread); /* Signal for the next resize request to proceed if any */ if (wqueue->last_thread)
{
DBUG_PRINT("signal",
("thread %s %ld", wqueue->last_thread->next->name,
wqueue->last_thread->next->id));
pagecache_pthread_cond_signal(&wqueue->last_thread->next->suspend);
}
pagecache_pthread_mutex_unlock(&pagecache->cache_lock);
DBUG_RETURN(blocks);
} #endif/* 0 */
if (pagecache->disk_blocks > 0)
{ #ifndef DBUG_OFF
check_pagecache_is_cleaned_up(pagecache); #endif
if (pagecache->block_mem)
{
my_large_free(pagecache->block_mem, pagecache->mem_size);
pagecache->block_mem= NULL;
my_free(pagecache->block_root);
pagecache->block_root= NULL;
}
pagecache->disk_blocks= -1; /* Reset blocks_changed to be safe if flush_all_key_blocks is called */
pagecache->blocks_changed= 0;
}
PCBLOCK_INFO(block);
KEYCACHE_DBUG_ASSERT(! (block->hash_link && block->hash_link->requests)); if (!hot && pagecache->waiting_for_block.last_thread)
{ /* Signal that in the LRU warm sub-chain an available block has appeared */ struct st_my_thread_var *last_thread=
pagecache->waiting_for_block.last_thread; struct st_my_thread_var *first_thread= last_thread->next; struct st_my_thread_var *next_thread= first_thread;
PAGECACHE_HASH_LINK *hash_link=
(PAGECACHE_HASH_LINK *) first_thread->keycache_link; struct st_my_thread_var *thread;
block= pagecache->used_ins; /* Check if we should link a hot block to the warm block */ if (block && pagecache->time - block->last_hit_time >
pagecache->age_threshold)
{
unlink_block(pagecache, block);
link_block(pagecache, block, 0, 0); if (block->temperature != PCBLOCK_WARM)
{
pagecache->warm_blocks++;
block->temperature= PCBLOCK_WARM;
}
KEYCACHE_DBUG_PRINT("unreg_request", ("#warm_blocks: %zu",
pagecache->warm_blocks));
}
}
DBUG_VOID_RETURN;
}
if ((*hash_link->prev= hash_link->next))
hash_link->next->prev= hash_link->prev;
hash_link->block= NULL; if (pagecache->waiting_for_hash_link.last_thread)
{ /* Signal that a free hash link has appeared */ struct st_my_thread_var *last_thread=
pagecache->waiting_for_hash_link.last_thread; struct st_my_thread_var *first_thread= last_thread->next; struct st_my_thread_var *next_thread= first_thread;
PAGECACHE_PAGE *first_page= (PAGECACHE_PAGE *) (first_thread->keycache_link); struct st_my_thread_var *thread;
if (wrmode && pagecache->resize_in_flush)
{ /* This is a write request during the flush phase of a resize operation */
if (page_status != PAGE_READ)
{ /* We don't need the page in the cache: we are going to write on disk */
DBUG_ASSERT(hash_link->requests > 0);
hash_link->requests--;
unlink_hash(pagecache, hash_link); return0;
} if (!(block->status & PCBLOCK_IN_FLUSH))
{
DBUG_ASSERT(hash_link->requests > 0);
hash_link->requests--; /* Removeblocktoinvalidatethepageintheblockbuffer aswearegoingtowritedirectlyondisk. Althoughwehaveanexclusivelockfortheupdatedkeypart thecontrolcanbeyieldedbythecurrentthreadaswemight haveunfinishedreadersofotherkeypartsintheblock buffer.Stillweareguaranteednottohaveanyreaders ofthekeypartwearewritingintountiltheblockis removedfromthecacheaswesetthePCBLOCK_REASSIGNED flag(seethecodebelowthathandlesreadingrequests).
*/
free_block(pagecache, block, 0); return0;
} /* Wait until the page is flushed on disk */
DBUG_ASSERT(hash_link->requests > 0);
hash_link->requests--;
wait_for_flush(pagecache, block);
/* Invalidate page in the block if it has not been done yet */
DBUG_ASSERT(block->status); /* Should always be true */ if (block->status)
free_block(pagecache, block, 0); return0;
}
if (page_status == PAGE_READ &&
(block->status & (PCBLOCK_IN_SWITCH | PCBLOCK_REASSIGNED)))
{ /* This is a request for a page to be removed from cache */
KEYCACHE_DBUG_PRINT("find_block",
("request for old page in block: %u " "wrmode: %d block->status: %d",
PCBLOCK_NUMBER(pagecache, block), wrmode,
block->status)); /* Onlyreadingrequestscanproceeduntiltheolddirtypageisflushed, allothersaretobesuspended,thenresubmitted
*/ if (!wrmode && block_is_copied && !(block->status & PCBLOCK_REASSIGNED))
{ if (reg_req)
reg_requests(pagecache, block, 1);
} else
{ /* WhenwecomehereeitherPCBLOCK_REASSIGNEDorPCBLOCK_IN_SWITCHare active.Inbothcaseswqueue_release_queue()iscalledwhenthe statechanges.
*/
DBUG_ASSERT(block->hash_link == hash_link);
remove_reader(block);
KEYCACHE_DBUG_PRINT("find_block",
("request waiting for old page to be saved"));
{ struct st_my_thread_var *thread= my_thread_var; /* Put the request into the queue of those waiting for the old page */
wqueue_add_to_queue(&block->wqueue[COND_FOR_SAVED], thread); /* Wait until the request can be resubmitted */ do
{
DBUG_PRINT("wait",
("suspend thread %s %ld", thread->name,
(ulong) thread->id));
pagecache_pthread_cond_wait(&thread->suspend,
&pagecache->cache_lock);
} while(thread->next);
}
KEYCACHE_DBUG_PRINT("find_block",
("request for old page resubmitted"));
DBUG_PRINT("info", ("restarting...")); /* Resubmit the request */ goto restart;
}
} else
{ /* This is a request for a new page or for a page not to be removed */ if (! block)
{
DBUG_PRINT("info", ("request for a new page")); /* No block is assigned for the page yet */ if (pagecache->blocks_unused)
{
DBUG_PRINT("info", ("there is never used blocks")); if (pagecache->free_block_list)
{ /* There is a block in the free list. */
block= pagecache->free_block_list;
pagecache->free_block_list= block->next_used;
block->next_used= NULL;
} else
{ /* There are some never used blocks, take first of them */
block= &pagecache->block_root[pagecache->blocks_used];
block->buffer= ADD_TO_PTR(pagecache->block_mem,
(pagecache->blocks_used*
pagecache->block_size),
uchar*);
pagecache->blocks_used++;
}
pagecache->blocks_unused--;
DBUG_ASSERT(block->wlocks == 0);
DBUG_ASSERT(block->rlocks == 0);
DBUG_ASSERT(block->rlocks_queue == 0);
DBUG_ASSERT(block->pins == 0);
block->status= 0; #ifdef DBUG_ASSERT_EXISTS
block->type= PAGECACHE_EMPTY_PAGE; #endif
DBUG_ASSERT(reg_req);
block->requests= 1;
block->temperature= PCBLOCK_COLD;
block->hits_left= init_hits_left;
block->last_hit_time= 0;
block->rec_lsn= LSN_MAX;
link_to_file_list(pagecache, block, file, 0);
DBUG_PRINT("hash",
("block (no block assigned): %p hash_link: %p -> %p",
block, block->hash_link, hash_link));
block->hash_link= hash_link;
DBUG_PRINT("hash",
("hash_link (no block assignment): %p hash_link: %p -> %p",
hash_link, hash_link->block, block));
hash_link->block= block;
page_status= PAGE_TO_BE_READ;
DBUG_PRINT("info", ("page to be read set for page %p (%u)",
block, PCBLOCK_NUMBER(pagecache, block)));
KEYCACHE_PRINT("find_block",
("got free or never used block %u",
PCBLOCK_NUMBER(pagecache, block)));
} else
{
DBUG_PRINT("info", ("there is NOT never used blocks")); /* There are no never used blocks, use a block from the LRU chain */
if (! pagecache->used_last)
{ struct st_my_thread_var *thread;
DBUG_PRINT("info", ("there is NOT UNUSED blocks")); /* WaituntilanewblockisaddedtotheLRUchain; severalthreadsmightwaithereforthesamepage, allofthemmustgetthesameblock.
Theblockisgiventousbythenextthreadexecuting link_block().
*/ if (fast)
{
DBUG_ASSERT(hash_link->requests == 0);
unlink_hash(pagecache, hash_link);
DBUG_PRINT("info", ("fast and no blocks in LRU"));
KEYCACHE_DBUG_PRINT("find_block",
("fast and no blocks in LRU"));
DBUG_RETURN(0);
}
thread= my_thread_var;
thread->keycache_link= (void *) hash_link;
wqueue_link_into_queue(&pagecache->waiting_for_block, thread); do
{
DBUG_PRINT("wait",
("suspend thread %s %ld", thread->name,
(ulong) thread->id));
pagecache_pthread_cond_wait(&thread->suspend,
&pagecache->cache_lock);
} while (thread->next);
thread->keycache_link= NULL;
block= hash_link->block; /* Ensure that the block is registered */
DBUG_ASSERT(block->requests >= 1);
} else
{
DBUG_PRINT("info", ("take a block from LRU")); /* TakethefirstblockfromtheLRUchain unlinkingitfromthechain
*/
block= pagecache->used_last->next_used; if (fast &&
((block->status & (PCBLOCK_IN_FLUSH | PCBLOCK_CHANGED)) ||
(block->hash_link && block->hash_link != hash_link &&
block->hash_link->requests)))
{
DBUG_ASSERT(hash_link->requests == 0);
unlink_hash(pagecache, hash_link);
DBUG_PRINT("info", ("fast and LRU block is in switch or has " "readers"));
KEYCACHE_DBUG_PRINT("find_block",
("fast and LRU block is in switch or has " "readers"));
DBUG_RETURN (0);
} if (reg_req)
reg_requests(pagecache, block, 1);
DBUG_PRINT("hash", ("hash_link (LRU): %p, hash_link: %p -> %p",
hash_link, hash_link->block, block));
hash_link->block= block;
DBUG_ASSERT(block->requests == 1);
}
if (block->hash_link != hash_link &&
! (block->status & PCBLOCK_IN_SWITCH) )
{ /* If another thread is flushing the block, wait for it. */ if (block->status & PCBLOCK_IN_FLUSH)
wait_for_flush(pagecache, block);
/* this is a primary request for a new page */
DBUG_ASSERT(block->wlocks == 0);
DBUG_ASSERT(block->rlocks == 0);
DBUG_ASSERT(block->rlocks_queue == 0);
DBUG_ASSERT(block->pins == 0);
block->status|= PCBLOCK_IN_SWITCH;
KEYCACHE_DBUG_PRINT("find_block",
("got block %u for new page",
PCBLOCK_NUMBER(pagecache, block)));
if (block->status & PCBLOCK_CHANGED)
{ /* The block contains a dirty page - push it out of the cache */
KEYCACHE_DBUG_PRINT("find_block", ("block is dirty"));
/* Remove the hash link for this page from the hash table */
unlink_hash(pagecache, block->hash_link);
/* All pending requests for this page must be resubmitted */ if (block->wqueue[COND_FOR_SAVED].last_thread)
wqueue_release_queue(&block->wqueue[COND_FOR_SAVED]);
}
link_to_file_list(pagecache, block, file,
(my_bool)(block->hash_link ? 1 : 0));
switch (lock) { case PAGECACHE_LOCK_WRITE: /* free -> write */ /* Writelock and pin the buffer */ if (get_wrlock(pagecache, block))
{ /* Couldn't lock because block changed status => need retry */ goto retry;
}
/* The cache is locked so nothing afraid of */
add_pin(block);
info_add_lock(block, 1); break; case PAGECACHE_LOCK_WRITE_TO_READ: /* write -> read */ case PAGECACHE_LOCK_WRITE_UNLOCK: /* write -> free */ /* Removes write lock and puts read lock */
release_wrlock(block, lock == PAGECACHE_LOCK_WRITE_TO_READ); /* fall through */ case PAGECACHE_LOCK_READ_UNLOCK: /* read -> free */ if (lock == PAGECACHE_LOCK_READ_UNLOCK)
release_rdlock(block); /* fall through */ case PAGECACHE_LOCK_LEFT_READLOCKED: /* read -> read */ if (pin == PAGECACHE_UNPIN)
{
remove_pin(block, FALSE);
} if (lock == PAGECACHE_LOCK_WRITE_TO_READ)
{
info_change_lock(block, 0);
} elseif (lock == PAGECACHE_LOCK_WRITE_UNLOCK ||
lock == PAGECACHE_LOCK_READ_UNLOCK)
{
info_remove_lock(block);
} break; case PAGECACHE_LOCK_READ: /* free -> read */ if (get_rdlock(pagecache, block))
{ /* Couldn't lock because block changed status => need retry */ goto retry;
}
if (pin == PAGECACHE_PIN)
{ /* The cache is locked so nothing afraid off */
add_pin(block);
}
info_add_lock(block, 0); break; case PAGECACHE_LOCK_LEFT_UNLOCKED: /* free -> free */ if (pin == PAGECACHE_UNPIN)
{
remove_pin(block, any);
} /* fall through */ case PAGECACHE_LOCK_LEFT_WRITELOCKED: /* write -> write */ break; /* do nothing */ default:
DBUG_ASSERT(0); /* Never should happened */
}
/* find first page of the big block (page_to_read) */
page_to_read= ((block->hash_link->pageno -
block->hash_link->file.head_blocks) /
big_block_size_in_pages);
page_to_read= (page_to_read * big_block_size_in_pages +
block->hash_link->file.head_blocks); if (page_to_read != our_page)
{
block_to_read= find_block(pagecache, &block->hash_link->file,
page_to_read, 1, FALSE, TRUE/* copy under protection (?)*/, TRUE/*register*/, FALSE, &page_st);
DBUG_ASSERT(block_to_read == block_to_read->hash_link->block);
if (block_to_read->status & PCBLOCK_ERROR)
{ /* We get first block with an error so all operation failed */
DBUG_PRINT("error", ("Got error when reading first page"));
block->status|= PCBLOCK_ERROR;
block->error= block_to_read->error;
remove_reader(block_to_read);
unreg_request(pagecache, block_to_read, 1);
DBUG_VOID_RETURN;
} if (block_to_read->status & PCBLOCK_BIG_READ)
{ /* Otherthreadisreadingthebigblocksowewillwaitwhenitwill havereadourblockforus
*/ struct st_my_thread_var *thread; /* Eitherthepagewasnotyetreadandthereisanotherthread doingtheread(page_st==PAGE_WAIT_TO_BE_READ)orthepage wasjustreadandthereareotherthreadswaitingforthepage buttheyhavenotyetunmarkedthePCLBOCK_BIG_READflag (page_st==PAGE_READ)
*/
DBUG_ASSERT(page_st == PAGE_READ || page_st == PAGE_WAIT_TO_BE_READ);
block->status|= PCBLOCK_BIG_READ; // will be read by other thread /* Blockreadfailedbecausesomebodyelseisreadingthefirstblock (andallotherblockspartofthisone). Waituntilblockisavailable.
*/
thread= my_thread_var; /* Put the request into a queue and wait until it can be processed */
wqueue_add_to_queue(&block_to_read->wqueue[COND_FOR_REQUESTED], thread); do
{
DBUG_PRINT("wait",
("suspend thread %s %ld", thread->name,
(ulong) thread->id));
pagecache_pthread_cond_wait(&thread->suspend,
&pagecache->cache_lock);
} while (thread->next); // page should be read by other thread
DBUG_ASSERT(block->status & PCBLOCK_READ ||
block->status & PCBLOCK_ERROR); /* Itispossiblethatotherthreadalreadyremovedtheflag(in caseoftwothreadswaiting)butitwillnotharmtotryto removeiteveninthatcase.
*/
block->status&= ~PCBLOCK_BIG_READ; // all is read => lets finish nice
DBUG_ASSERT(block_to_read != block);
remove_reader(block_to_read);
unreg_request(pagecache, block_to_read, 1);
DBUG_VOID_RETURN;
} else
{ // only primary request here, PAGE_WAIT_TO_BE_READ is impossible
DBUG_ASSERT(page_st != PAGE_WAIT_TO_BE_READ);
}
} else
{
block_to_read= block;
page_st= PAGE_TO_BE_READ;
}
DBUG_ASSERT(!(block_to_read->status & PCBLOCK_BIG_READ)); // Mark the first page of a big block
block_to_read->status|= PCBLOCK_BIG_READ;
// Don't keep cache locked during the possible slow read from s3
pagecache_pthread_mutex_unlock(&pagecache->cache_lock);
// perform read of big block
args.page= NULL;
args.pageno= page_to_read;
args.data= block->hash_link->file.callback_data;
/* Copy the first page to the cache */ if (page_st != PAGE_READ)
{
DBUG_ASSERT(page_st != PAGE_WAIT_TO_BE_READ);
memcpy(block_to_read->buffer, data.str, pagecache->block_size);
block_to_read->status|= PCBLOCK_READ;
} else
{
DBUG_ASSERT(block_to_read->status & PCBLOCK_READ);
} /* Signal that all pending requests for this page now can be processed */ if (block_to_read->wqueue[COND_FOR_REQUESTED].last_thread)
wqueue_release_queue(&block_to_read->wqueue[COND_FOR_REQUESTED]);
/* Copy the rest of the pages */ for (offset= pagecache->block_size, page= page_to_read + 1;
offset < data.length;
offset+= pagecache->block_size, page++)
{
DBUG_ASSERT(offset + pagecache->block_size <= data.length); if (page == our_page)
{
DBUG_ASSERT(!(block->status & PCBLOCK_READ));
memcpy(block->buffer, data.str + offset, pagecache->block_size);
block->status|= PCBLOCK_READ;
} else
{
PAGECACHE_BLOCK_LINK *bl;
bl= find_block(pagecache, &block->hash_link->file, page, 1, FALSE, TRUE/* copy under protection (?)*/, TRUE/*register*/, TRUE /*fast*/, &page_st); if (!bl)
{ /* Wecannotgetthispageeasy. Maybewewillbeluckywithotherpages, alsoamongotherpagescanbepagewhichwaitedbyotherthread
*/ continue;
}
DBUG_ASSERT(bl == bl->hash_link->block); if ((bl->status & PCBLOCK_ERROR) == 0 &&
(page_st == PAGE_TO_BE_READ || // page should be read
(page_st == PAGE_WAIT_TO_BE_READ &&
(bl->status & PCBLOCK_BIG_READ)))) // or page waited by other thread
{
memcpy(bl->buffer, data.str + offset, pagecache->block_size);
bl->status|= PCBLOCK_READ;
}
remove_reader(bl);
unreg_request(pagecache, bl, 1); /* Signal that all pending requests for this page now can be processed */ if (bl->wqueue[COND_FOR_REQUESTED].last_thread)
wqueue_release_queue(&bl->wqueue[COND_FOR_REQUESTED]);
}
} if (page < our_page)
{ /* we break earlier, but still have to fill page what was requested */
DBUG_ASSERT(!(block->status & PCBLOCK_READ));
memcpy(block->buffer,
data.str + ((our_page - page_to_read) * pagecache->block_size),
pagecache->block_size);
block->status|= PCBLOCK_READ;
}
pagecache->big_block_free(&data);
end:
block_to_read->status&= ~PCBLOCK_BIG_READ; if (block_to_read != block)
{ /* Unlock the 'first block' in the big read */
remove_reader(block_to_read);
unreg_request(pagecache, block_to_read, 1);
} /* Signal that all pending requests for this page now can be processed */ if (block->wqueue[COND_FOR_REQUESTED].last_thread)
wqueue_release_queue(&block->wqueue[COND_FOR_REQUESTED]);
DBUG_VOID_RETURN;
error: /* Readfailed.Markallreaderswaitingfortheablockcoveredbythe bigblockthatthereadfailed
*/ for (offset= 0, page= page_to_read + 1;
offset < big_block_size_in_pages;
offset++)
{ if (page != our_page)
{
PAGECACHE_BLOCK_LINK *bl;
bl= find_block(pagecache, &block->hash_link->file, page, 1, FALSE, TRUE/* copy under protection (?)*/, TRUE/*register*/, TRUE /*fast*/, &page_st); if (!bl)
{ /* Wecannotgetthispageeasy. Maybewewillbeluckywithotherpages, alsoamongotherpagescanbepagewhichwaitedbyotherthread
*/ continue;
}
DBUG_ASSERT(bl == bl->hash_link->block); if ((bl->status & PCBLOCK_ERROR) == 0 &&
(page_st == PAGE_TO_BE_READ || // page should be read
(page_st == PAGE_WAIT_TO_BE_READ &&
(bl->status & PCBLOCK_BIG_READ)))) // or page waited by other thread
{
bl->status|= PCBLOCK_ERROR;
bl->error= (int16) my_errno;
}
remove_reader(bl);
unreg_request(pagecache, bl, 1); /* Signal that all pending requests for this page now can be processed */ if (bl->wqueue[COND_FOR_REQUESTED].last_thread)
wqueue_release_queue(&bl->wqueue[COND_FOR_REQUESTED]);
}
} goto end;
} #endif/* WITH_S3_STORAGE_ENGINE */
pagecache->global_cache_read++; /* Pageisnotinbufferyet,istobereadfromdisk Hereotherthreadsmaystepinandregisterassecondaryreaders. Theywillregisterinblock->wqueue[COND_FOR_REQUESTED].
*/
pagecache_pthread_mutex_unlock(&pagecache->cache_lock);
args.page= block->buffer;
args.pageno= block->hash_link->pageno;
args.data= block->hash_link->file.callback_data;
error= (*block->hash_link->file.pre_read_hook)(&args); if (!error)
{
error= pagecache_fread(pagecache, &block->hash_link->file,
args.page,
block->hash_link->pageno,
pagecache->readwrite_flags);
}
error= (*block->hash_link->file.post_read_hook)(error != 0, &args);
pagecache_pthread_mutex_lock(&pagecache->cache_lock); if (error)
{
DBUG_ASSERT(maria_in_recovery || !maria_assert_if_crashed_table);
block->status|= PCBLOCK_ERROR;
block->error= (int16) my_errno;
my_debug_put_break_here();
} else
{
block->status|= PCBLOCK_READ;
}
DBUG_PRINT("read_block",
("primary request: new page in cache")); /* Signal that all pending requests for this page now can be processed */ if (block->wqueue[COND_FOR_REQUESTED].last_thread)
wqueue_release_queue(&block->wqueue[COND_FOR_REQUESTED]);
} else
{ /* Thiscodeisexecutedonlybythreads thatsubmittedsecondaryrequests
*/
struct st_my_thread_var *thread= my_thread_var; /* Put the request into a queue and wait until it can be processed */
wqueue_add_to_queue(&block->wqueue[COND_FOR_REQUESTED], thread); do
{
DBUG_PRINT("wait",
("suspend thread %s %ld", thread->name,
(ulong) thread->id));
pagecache_pthread_cond_wait(&thread->suspend,
&pagecache->cache_lock);
} while (thread->next);
DBUG_PRINT("read_block",
("secondary request: new page in cache"));
}
DBUG_VOID_RETURN;
}
DBUG_ASSERT(block->type != PAGECACHE_READ_UNKNOWN_PAGE);
lsn_store(block->buffer, lsn); /* we stored LSN in page so we dirtied it */ if (!(block->status & PCBLOCK_CHANGED))
link_to_changed_list(pagecache, block);
}
DBUG_VOID_RETURN;
}
/* description of how to change lock before and after read/write */ struct rw_lock_change
{
my_bool need_lock_change; /* need changing of lock at the end */ enum pagecache_page_lock new_lock; /* lock at the beginning */ enum pagecache_page_lock unlock_lock; /* lock at the end */
};
/* description of how to change pin before and after read/write */ struct rw_pin_change
{ enum pagecache_page_pin new_pin; /* pin status at the beginning */ enum pagecache_page_pin unlock_pin; /* pin status at the end */
};
if (error)
{
block->status|= PCBLOCK_ERROR;
block->error= (int16) my_errno;
my_debug_put_break_here(); goto out;
}
} else
{
PAGECACHE_IO_HOOK_ARGS args;
PAGECACHE_FILE *filedesc= &block->hash_link->file;
args.page= block->buffer;
args.pageno= block->hash_link->pageno;
args.data= filedesc->callback_data; /* We are not going to write the page but have to call callbacks */
DBUG_PRINT("info", ("flush_callback: %p data: %p",
filedesc->flush_log_callback,
filedesc->callback_data)); if ((*filedesc->flush_log_callback)(&args))
{
DBUG_PRINT("error", ("flush or write callback problem"));
error= 1; goto out;
}
}
pagecache->blocks_changed--;
pagecache->global_blocks_changed--; /* free_block()willchangethestatusandrec_lsnoftheblocksono needtochangethemhere.
*/
} /* Cache is locked, so we can relese page before freeing it */ if (make_lock_and_pin(pagecache, block,
PAGECACHE_LOCK_WRITE_UNLOCK,
PAGECACHE_UNPIN, FALSE))
DBUG_ASSERT(0);
DBUG_ASSERT(block->hash_link->requests > 0);
page_link->requests--; /* See NOTE for pagecache_unlock() about registering requests. */
free_block(pagecache, block, 0);
dec_counter_for_resize_op(pagecache); return0;
out: /* Cache is locked, so we can relese page before freeing it */ if (make_lock_and_pin(pagecache, block,
PAGECACHE_LOCK_WRITE_UNLOCK,
PAGECACHE_UNPIN, FALSE))
DBUG_ASSERT(0);
page_link->requests--;
unreg_request(pagecache, block, 1);
dec_counter_for_resize_op(pagecache); return error;
}
/* This lock is deleted in pagecache_delete_internal() called below */
inc_counter_for_resize_op(pagecache); /* make_lock_and_pin()can'tfailhere,becausewearekeepingpinonthe blockanditcan'tbeevicted(whichiscauseoflockfailandretry)
*/ if (make_lock_and_pin(pagecache, block, lock, pin, FALSE))
DBUG_ASSERT(0);
if (pagecache->can_be_used)
{ /* Key cache is used */ int page_st;
my_bool need_page_ready_signal= FALSE;
pagecache_pthread_mutex_lock(&pagecache->cache_lock); if (!pagecache->can_be_used)
{
pagecache_pthread_mutex_unlock(&pagecache->cache_lock); goto no_key_cache;
}
inc_counter_for_resize_op(pagecache);
pagecache->global_cache_w_requests++; /* Hereweregisterarequestifthepagewasnotalreadypinned. SeeNOTEforpagecache_unlockaboutregisteringrequests.
*/
reg_request= ((pin == PAGECACHE_PIN_LEFT_UNPINNED) ||
(pin == PAGECACHE_PIN));
block= find_block(pagecache, file, pageno, level, TRUE, FALSE,
reg_request, FALSE, &page_st); if (!block)
{
DBUG_ASSERT(write_mode != PAGECACHE_WRITE_DONE); /* It happens only for requests submitted during resize operation */
dec_counter_for_resize_op(pagecache);
pagecache_pthread_mutex_unlock(&pagecache->cache_lock); /* Write to the disk key cache is in resize at the moment*/ goto no_key_cache;
}
DBUG_PRINT("info", ("page status: %d", page_st)); if (!(block->status & PCBLOCK_ERROR) &&
((page_st == PAGE_TO_BE_READ &&
(offset || size < pagecache->block_size)) ||
(page_st == PAGE_WAIT_TO_BE_READ)))
{ /* The requested page is to be read into the block buffer */
read_block(pagecache, block,
(my_bool)(page_st == PAGE_TO_BE_READ));
DBUG_PRINT("info", ("read is done"));
} elseif (page_st == PAGE_TO_BE_READ)
{
need_page_ready_signal= TRUE;
}
DBUG_ASSERT(block->type == PAGECACHE_EMPTY_PAGE ||
block->type == PAGECACHE_READ_UNKNOWN_PAGE ||
block->type == type || /* this is for when going to non-trans to trans */
(block->type == PAGECACHE_PLAIN_PAGE &&
type == PAGECACHE_LSN_PAGE));
block->type= type; /* we write to the page so it has no sense to keep the flag */
block->status&= ~PCBLOCK_DIRECT_W;
DBUG_PRINT("info", ("Drop PCBLOCK_DIRECT_W for block: %p", block));
if (write_mode == PAGECACHE_WRITE_DONE)
{ if (block->status & PCBLOCK_ERROR)
{
my_debug_put_break_here();
DBUG_PRINT("warning", ("Writing on page with error"));
} else
{ /* Copy data from buff */
memcpy(block->buffer + offset, buff, size);
block->status= PCBLOCK_READ;
KEYCACHE_DBUG_PRINT("key_cache_insert",
("Page injection")); /* Signal that all pending requests for this now can be processed. */ if (block->wqueue[COND_FOR_REQUESTED].last_thread)
wqueue_release_queue(&block->wqueue[COND_FOR_REQUESTED]);
}
} else
{ if (! (block->status & PCBLOCK_CHANGED))
link_to_changed_list(pagecache, block);
memcpy(block->buffer + offset, buff, size);
block->status|= PCBLOCK_READ; /* Page is correct again if we made a full write in it */ if (size == pagecache->block_size)
block->status&= ~PCBLOCK_ERROR;
}
if (need_page_ready_signal &&
block->wqueue[COND_FOR_REQUESTED].last_thread)
wqueue_release_queue(&block->wqueue[COND_FOR_REQUESTED]);
if (first_REDO_LSN_for_page)
{ /* single write action of the last write action */
DBUG_ASSERT(lock == PAGECACHE_LOCK_WRITE_UNLOCK ||
lock == PAGECACHE_LOCK_LEFT_UNLOCKED);
DBUG_ASSERT(pin == PAGECACHE_UNPIN ||
pin == PAGECACHE_PIN_LEFT_UNPINNED);
pagecache_set_block_rec_lsn(block, first_REDO_LSN_for_page);
}
if (need_lock_change)
{ /* Wedon'tsetrec_lsnoftheblock;thisisokasforthe Maria-block-record'spages,wealwayskeeppagespinnedhere.
*/ if (make_lock_and_pin(pagecache, block,
write_lock_change_table[lock].unlock_lock,
write_pin_change_table[pin].unlock_pin, FALSE))
DBUG_ASSERT(0);
}
/* Unregister the request */
DBUG_ASSERT(block->hash_link->requests > 0);
block->hash_link->requests--; /* See NOTE for pagecache_unlock about registering requests. */ if (pin == PAGECACHE_PIN_LEFT_UNPINNED || pin == PAGECACHE_UNPIN)
{
unreg_request(pagecache, block, 1);
DBUG_ASSERT(page_link == &fake_link);
} else
*page_link= block;
if (block->status & PCBLOCK_ERROR)
{
error= 1;
my_debug_put_break_here();
}
no_key_cache: /* Wecan'tbypassthenormalpagecacheoperationsbecauseneed wholepageforcallingcallbacks&soon. Thisbranchshouldnotbeusedfornow(butitisfixedasit shouldbejusttoavoidconfusing)
*/
DBUG_ASSERT(0); /* Key cache is not used */ if (write_mode == PAGECACHE_WRITE_DELAY)
{ /* We can't use mutex here as the key cache may not be initialized */
pagecache->global_cache_w_requests++;
pagecache->global_cache_write++; if (offset != 0 || size != pagecache->block_size)
{
uchar *page_buffer= (uchar *) alloca(pagecache->block_size);
PAGECACHE_IO_HOOK_ARGS args;
args.page= page_buffer;
args.pageno= pageno;
args.data= file->callback_data;
/* All pending requests for this page must be resubmitted. */ if (block->wqueue[COND_FOR_SAVED].last_thread)
wqueue_release_queue(&block->wqueue[COND_FOR_SAVED]); return1;
}
unlink_hash(pagecache, block->hash_link);
}
/* Block->requestsis!=0ifunreg_requests()/link_block()gavetheblock toawaitingthread
*/ if (!block->requests)
{
DBUG_ASSERT(block->next_used != 0);
/* Remove the free block from the LRU ring. */
unlink_block(pagecache, block); /* Insert the free block in the free list. */
block->next_used= pagecache->free_block_list;
pagecache->free_block_list= block; /* Keep track of the number of currently unused blocks. */
pagecache->blocks_unused++;
} else
{ /* keep flag set by link_block() */
block->status= status & PCBLOCK_REASSIGNED;
}
/* All pending requests for this page must be resubmitted. */ if (block->wqueue[COND_FOR_SAVED].last_thread)
wqueue_release_queue(&block->wqueue[COND_FOR_SAVED]);
struct st_file_in_flush us_flusher, *other_flusher;
us_flusher.file= file->file;
us_flusher.flush_queue.last_thread= NULL;
us_flusher.first_in_switch= FALSE; while ((other_flusher= (struct st_file_in_flush *)
my_hash_search(&pagecache->files_in_flush, (uchar *)&file->file, sizeof(file->file))))
{ /* Fileisinflushalready:wait,unlessFLUSH_KEEP_LAZY."Flusher" means"whocanmarkPCBLOCK_IN_FLUSH",i.e.callerof flush_pagecache_blocks_int().
*/ struct st_my_thread_var *thread; if (type == FLUSH_KEEP_LAZY)
{
DBUG_PRINT("info",("FLUSH_KEEP_LAZY skips"));
DBUG_RETURN(0);
}
thread= my_thread_var;
wqueue_add_to_queue(&other_flusher->flush_queue, thread); do
{
DBUG_PRINT("wait",
("(1) suspend thread %s %ld",
thread->name, (ulong) thread->id));
pagecache_pthread_cond_wait(&thread->suspend,
&pagecache->cache_lock);
} while (thread->next);
} /* we are the only flusher of this file now */ while (my_hash_insert(&pagecache->files_in_flush, (uchar *)&us_flusher))
{ /* Outofmemory,waitforflusherstoemptythehashandretry;should rarelyhappen.Otherthreadsareflushingthefile;whendone,they aregoingtoremovethemselvesfromthehash,andthusmemorywill appearagain.However,thismemorymaybestolenbyyetanotherthread (forapurposeunrelatedtopagecache),beforeweretry my_hash_insert().Sotheloopmayrunforlong.Onlyifthethreadwas killeddoweaborttheloop,returning1(error)whichcancausethe tabletobemarkedascorrupted(cfmaria_chk_size(),maria_close()) andthusrequireatablecheck.
*/
DBUG_ASSERT(0);
pagecache_pthread_mutex_unlock(&pagecache->cache_lock); if (my_thread_var->abort)
DBUG_RETURN(1); /* End if aborted by user */
sleep(10);
pagecache_pthread_mutex_lock(&pagecache->cache_lock);
}
if (type != FLUSH_IGNORE_CHANGED)
{ /* Counthowmanykeyblockswehavetocachetobeable toflushalldirtypageswithminimumseekmoves.
*/ for (block= pagecache->changed_blocks[FILE_HASH(*file, pagecache)] ;
block;
block= block->next_changed)
{ if (block->hash_link->file.file == file->file)
{
count++;
KEYCACHE_DBUG_ASSERT(count<= pagecache->blocks_used);
}
}
count++; /* Allocate one extra for easy end-of-buffer test */ /* Allocate a new buffer only if its bigger than the one we have */ if (count > FLUSH_CACHE &&
!(cache=
(PAGECACHE_BLOCK_LINK**)
my_malloc(PSI_INSTRUMENT_ME, sizeof(PAGECACHE_BLOCK_LINK*)*count, MYF(0))))
{
cache= cache_buff;
count= FLUSH_CACHE;
}
}
/* Retrieve the blocks and write them to a buffer to be flushed */
restart:
end= (pos= cache)+count; for (block= pagecache->changed_blocks[FILE_HASH(*file, pagecache)] ;
block;
block= next)
{ #ifdefined(PAGECACHE_DEBUG)
cnt++;
KEYCACHE_DBUG_ASSERT(cnt <= pagecache->blocks_used); #endif
next= block->next_changed; if (block->hash_link->file.file != file->file) continue; if (filter != NULL)
{ int filter_res= (*filter)(block->type, block->hash_link->pageno,
block->rec_lsn, filter_arg);
DBUG_PRINT("info",("filter returned %d", filter_res)); if (filter_res == FLUSH_FILTER_SKIP_TRY_NEXT) continue; if (filter_res == FLUSH_FILTER_SKIP_ALL) break;
DBUG_ASSERT(filter_res == FLUSH_FILTER_OK);
}
{
DBUG_ASSERT(!(block->status & PCBLOCK_IN_FLUSH)); /* Wecareonlyfortheblocksforwhichflushingwasnot initiatedbyotherthreadsasaresultofpageswapping
*/ if (! (block->status & PCBLOCK_IN_SWITCH))
{ /* MarktheblockwithBLOCK_IN_FLUSHinordernottolet otherthreadstouseitfornewpagesandinterferewith oursequenceofflushingdirtyfilepages
*/
block->status|= PCBLOCK_IN_FLUSH;
reg_requests(pagecache, block, 1); if (type != FLUSH_IGNORE_CHANGED)
{
*pos++= block; /* It's not a temporary file */ if (pos == end)
{ /* Thishappensonlyifthereisnotenough memoryforthebigblock
*/ if ((rc|= flush_cached_blocks(pagecache, file, cache,
end, type, &error)) &
(PCFLUSH_ERROR | PCFLUSH_PINNED))
last_errno=error;
DBUG_PRINT("info", ("restarting...")); /* Restartthescanassomeotherthreadmighthavechanged thechangedblockschain:theblocksthatwereinswitch statebeforetheflushstartedhavetobeexcluded
*/ goto restart;
}
} else
{ /* It's a temporary file */
pagecache->blocks_changed--;
pagecache->global_blocks_changed--;
free_block(pagecache, block, 0);
}
} elseif (type != FLUSH_KEEP_LAZY)
{ /* Linktheblockintoalistofblocks'inswitch',andthenwewill waitforthislisttobeempty,whichmeanstheyhavebeenflushed
*/
unlink_changed(block);
link_changed(block, &first_in_switch);
us_flusher.first_in_switch= TRUE;
}
}
} if (pos != cache)
{ if ((rc|= flush_cached_blocks(pagecache, file, cache, pos, type,
&error)) &
(PCFLUSH_ERROR | PCFLUSH_PINNED))
last_errno= error;
} /* Wait until list of blocks in switch is empty */ while (first_in_switch)
{ #ifdefined(PAGECACHE_DEBUG)
cnt= 0; #endif
block= first_in_switch;
{ struct st_my_thread_var *thread= my_thread_var;
wqueue_add_to_queue(&block->wqueue[COND_FOR_SAVED], thread); do
{
DBUG_PRINT("wait",
("(2) suspend thread %s %ld",
thread->name, (ulong) thread->id));
pagecache_pthread_cond_wait(&thread->suspend,
&pagecache->cache_lock);
} while (thread->next);
} #ifdefined(PAGECACHE_DEBUG)
cnt++;
KEYCACHE_DBUG_ASSERT(cnt <= pagecache->blocks_used); #endif
}
us_flusher.first_in_switch= FALSE; /* The following happens very seldom */ if (! (type == FLUSH_KEEP || type == FLUSH_KEEP_LAZY ||
type == FLUSH_FORCE_WRITE))
{ /* thiscodewouldfreeallblockswhilefiltermaybehandledonlya few,thatisnotpossible.
*/
DBUG_ASSERT(filter == NULL); #ifdefined(PAGECACHE_DEBUG)
cnt=0; #endif for (block= pagecache->file_blocks[FILE_HASH(*file, pagecache)] ;
block;
block= next)
{ #ifdefined(PAGECACHE_DEBUG)
cnt++;
KEYCACHE_DBUG_ASSERT(cnt <= pagecache->blocks_used); #endif
next= block->next_changed; if (block->hash_link->file.file == file->file &&
!block->pins &&
(! (block->status & PCBLOCK_CHANGED)
|| type == FLUSH_IGNORE_CHANGED))
{
reg_requests(pagecache, block, 1);
free_block(pagecache, block, 1);
}
}
} /* wake up others waiting to flush this file */
my_hash_delete(&pagecache->files_in_flush, (uchar *)&us_flusher); if (us_flusher.flush_queue.last_thread)
wqueue_release_queue(&us_flusher.flush_queue);
}
DBUG_EXECUTE("check_pagecache",
test_key_cache(pagecache, "end of flush_pagecache_blocks", 0);); if (cache != cache_buff)
my_free(cache); if (rc != 0)
{ if (last_errno)
my_errno= last_errno; /* Return first error */
DBUG_PRINT("error", ("Got error: %d", my_errno));
}
DBUG_RETURN(rc);
}
i=0;
thread=last=waiting_for_hash_link.last_thread;
fprintf(pagecache_dump_file, "queue of threads waiting for hash link\n"); if (thread) do
{
thread= thread->next;
page= (PAGECACHE_PAGE *) thread->keycache_link;
fprintf(pagecache_dump_file, "thread: %s %ld, (file,pageno)=(%u,%lu)\n",
thread->name, (ulong) thread->id,
(uint) page->file.file,(ulong) page->pageno); if (++i == MAX_QUEUE_LEN) break;
} while (thread != last);
i=0;
thread=last=waiting_for_block.last_thread;
fprintf(pagecache_dump_file, "queue of threads waiting for block\n"); if (thread) do
{
thread=thread->next;
hash_link= (PAGECACHE_HASH_LINK *) thread->keycache_link;
fprintf(pagecache_dump_file, "thread: %s %u hash_link:%u (file,pageno)=(%u,%lu)\n",
thread->name, (ulong) thread->id,
(uint) PAGECACHE_HASH_LINK_NUMBER(pagecache, hash_link),
(uint) hash_link->file.file,(ulong) hash_link->pageno); if (++i == MAX_QUEUE_LEN) break;
} while (thread != last);
for (i=0 ; i < pagecache->blocks_used ; i++)
{ int j;
block= &pagecache->block_root[i];
hash_link= block->hash_link;
fprintf(pagecache_dump_file, "block:%u hash_link:%d status:%x #requests=%u waiting_for_readers:%d\n",
i, (int) (hash_link ?
PAGECACHE_HASH_LINK_NUMBER(pagecache, hash_link) :
-1),
block->status, block->requests, block->condvar ? 1 : 0); for (j=0 ; j < COND_SIZE; j++)
{
PAGECACHE_WQUEUE *wqueue=&block->wqueue[j];
thread= last= wqueue->last_thread;
fprintf(pagecache_dump_file, "queue #%d\n", j); if (thread)
{ do
{
thread=thread->next;
fprintf(pagecache_dump_file, "thread: %s %ld\n", thread->name, (ulong) thread->id); if (++i == MAX_QUEUE_LEN) break;
} while (thread != last);
}
}
}
fprintf(pagecache_dump_file, "LRU chain:");
block= pagecache= used_last; if (block)
{ do
{
block= block->next_used;
fprintf(pagecache_dump_file, "block:%u, ", PCBLOCK_NUMBER(pagecache, block));
} while (block != pagecache->used_last);
}
fprintf(pagecache_dump_file, "\n");
fclose(pagecache_dump_file);
}
#endif/* defined(PAGECACHE_TIMEOUT) */
#ifdefined(PAGECACHE_TIMEOUT) && !defined(_WIN32)
staticint pagecache_pthread_cond_wait(mysql_cond_t *cond,
mysql_mutex_t *mutex)
{ int rc; struct timeval now; /* time when we started waiting */ struct timespec timeout; /* timeout value for the wait function */ struct timezone tz; #ifdefined(PAGECACHE_DEBUG) int cnt=0; #endif
/* Get current time */
gettimeofday(&now, &tz); /* Prepare timeout value */
timeout.tv_sec= now.tv_sec + PAGECACHE_TIMEOUT; /* timevalusesmicroseconds. timespecusesnanoseconds. 1nanosecond=1000microseconds
*/
timeout.tv_nsec= now.tv_usec * 1000;
KEYCACHE_THREAD_TRACE_END("started waiting"); #ifdefined(PAGECACHE_DEBUG)
cnt++; if (cnt % 100 == 0)
fprintf(pagecache_debug_log, "waiting...\n");
fflush(pagecache_debug_log); #endif
rc= mysql_cond_timedwait(cond, mutex, &timeout);
KEYCACHE_THREAD_TRACE_BEGIN("finished waiting"); if (rc == ETIMEDOUT || rc == ETIME)
{ #ifdefined(PAGECACHE_DEBUG)
fprintf(pagecache_debug_log,"aborted by pagecache timeout\n");
fclose(pagecache_debug_log);
abort(); #endif
pagecache_dump();
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.